diff options
863 files changed, 33501 insertions, 16395 deletions
diff --git a/.gitignore b/.gitignore index 46a47febcb..0986bb6e4c 100644 --- a/.gitignore +++ b/.gitignore @@ -110,6 +110,7 @@ JAVADOC-GENERATED !/bootstrap/bin/*.boot /bootstrap/lib/asn1 +/bootstrap/lib/common_test /bootstrap/lib/hipe /bootstrap/lib/ic /bootstrap/lib/orber @@ -119,6 +120,7 @@ JAVADOC-GENERATED /bootstrap/lib/syntax_tools /bootstrap/lib/test_server /bootstrap/lib/wx +/bootstrap/lib/xmerl /Makefile /configure diff --git a/INSTALL.md b/INSTALL.md index a7bc0a53e4..34dd9fed8e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -693,34 +693,61 @@ Universal binaries and 64bit binaries are mutually exclusive options. Building a fast Erlang VM on Mac OS Lion ---------------------------------------- -Starting with XCode 4.2, Apple no longer includes a "real" `gcc` +Starting with Xcode 4.2, Apple no longer includes a "real" `gcc` compiler (not based on the LLVM). Building with `llvm-gcc` or `clang` will work, but the performance of the Erlang run-time system will not be the best possible. Note that if you have `gcc-4.2` installed and included in `PATH` -(from a previous version of XCode), `configure` will automatically +(from a previous version of Xcode), `configure` will automatically make sure that `gcc-4.2` will be used to compile `beam_emu.c` (the source file most in need of `gcc`). If you don't have `gcc-4.2.` and want to build a run-time system with the best possible performance, do like this: -Install XCode from the AppStore if it is not already installed. +Install Xcode from the AppStore if it is not already installed. + +If you have Xcode 4.3, or later, you will also need to download +"Command Line Tools" via the Downloads preference pane in Xcode. + +Some tools may still be lacking or out-of-date, we recommend using +[Homebrew](https://github.com/mxcl/homebrew/wiki/installation) or +Macports to update those tools. Install MacPorts (<http://www.macports.org/>). Then: $ sudo port selfupdate $ sudo port install gcc45 +universal -If you want to build the `wx` application, get wxMac-2.8.12 +### Building with wxErlang ### + +If you want to build the `wx` application, you will need to get wxMac-2.8.12 (`wxMac-2.8.12.tar.gz` from -<http://sourceforge.net/projects/wxwindows/files/2.8.12/>) and build: +<http://sourceforge.net/projects/wxwindows/files/2.8.12/>) and install it. + +Export the path for MacOSX10.6.sdk: + + $ export SDK=/Developer/SDKs/MacOSX10.6.sdk + +In Xcode 4.3 the path has changed so use the following instead, + + $ export SDK=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk - $ arch_flags="-arch i386" ./configure CFLAGS="$arch_flags" CXXFLAGS="$arch_flags" CPPFLAGS="$arch_flags" LDFLAGS="$arch_flags" OBJCFLAGS="$arch_flags" OBJCXXFLAGS="$arch_flags" --prefix=/usr/local --with-macosx-sdk=/Developer/SDKs/MacOSX10.6.sdk --with-macosx-version-min=10.6 --enable-unicode --with-opengl --disable-shared +Then configure and build wxMac: + + $ arch_flags="-arch i386" ./configure CFLAGS="$arch_flags" CXXFLAGS="$arch_flags" CPPFLAGS="$arch_flags" LDFLAGS="$arch_flags" OBJCFLAGS="$arch_flags" OBJCXXFLAGS="$arch_flags" --prefix=/usr/local --with-macosx-sdk="$SDK" --with-macosx-version-min=10.6 --enable-unicode --with-opengl --disable-shared + $ make + $ sudo make install + +To link wx properly you will also need to build and install `wxStyledTextCtrl`: + + $ cd contrib/src/stc $ make $ sudo make install +### Finish up ### + Build Erlang with the MacPorts GCC as the main compiler (using `clang` for the Objective-C Cocoa code in the `wx` application): diff --git a/Makefile.in b/Makefile.in index 77dbd58181..544233f097 100644 --- a/Makefile.in +++ b/Makefile.in @@ -175,12 +175,14 @@ BOOTSTRAP_ONLY = @BOOTSTRAP_ONLY@ CROSS_COMPILING = @CROSS_COMPILING@ ifeq ($(CROSS_COMPILING),yes) INSTALL_CROSS = -cross +TARGET_HOST=$(shell $(ERL_TOP)/erts/autoconf/config.guess) else ifneq ($(DESTDIR),) INSTALL_CROSS = -cross else INSTALL_CROSS = endif +TARGET_HOST= endif # A BSD compatible install program @@ -228,10 +230,10 @@ BOOTSTRAP_ROOT = $(ERL_TOP) LOCAL_PATH = $(ERL_TOP)/erts/bin/$(TARGET):$(ERL_TOP)/erts/bin ifeq ($(TARGET),win32) BOOT_PREFIX=$(WIN32_WRAPPER_PATH):$(BOOTSTRAP_ROOT)/bootstrap/bin: -TEST_PATH_PREFIX=$(WIN32_WRAPPER_PATH):$(ERL_TOP)/bin: +TEST_PATH_PREFIX=$(WIN32_WRAPPER_PATH):$(ERL_TOP)/bin/win32: else BOOT_PREFIX=$(BOOTSTRAP_ROOT)/bootstrap/bin: -TEST_PATH_PREFIX=$(ERL_TOP)/bin: +TEST_PATH_PREFIX=$(ERL_TOP)/bin/$(TARGET_HOST): endif # ---------------------------------------------------------------------- @@ -333,7 +335,7 @@ all: cross_check_erl depend emulator libs start_scripts endif cross_check_erl: - @PATH=$(BOOT_PREFIX)$${PATH} $(ERL_TOP)/make/cross_check_erl \ + @PATH=$(BOOT_PREFIX)"$${PATH}" $(ERL_TOP)/make/cross_check_erl \ -target $(TARGET) -otp $(OTP) -erl_top $(ERL_TOP) \ -force $(ERL_XCOMP_FORCE_DIFFERENT_OTP) @@ -380,16 +382,16 @@ noboot_install: release: $(INST_DEP) ifeq ($(OTP_SMALL_BUILD),true) cd $(ERL_TOP)/lib && \ - ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)$${PATH} \ - $(MAKE) TESTROOT=$(RELEASE_ROOT) release + ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)"$${PATH}" \ + $(MAKE) TESTROOT="$(RELEASE_ROOT)" release else cd $(ERL_TOP)/lib && \ - ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)$${PATH} \ - $(MAKE) BUILD_ALL=1 TESTROOT=$(RELEASE_ROOT) release + ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)"$${PATH}" \ + $(MAKE) BUILD_ALL=1 TESTROOT="$(RELEASE_ROOT)" release endif cd $(ERL_TOP)/erts && \ - ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)$${PATH} \ - $(MAKE) BUILD_ALL=1 TESTROOT=$(RELEASE_ROOT) release + ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)"$${PATH}" \ + $(MAKE) BUILD_ALL=1 TESTROOT="$(RELEASE_ROOT)" release # --------------------------------------------------------------- # Target only used when building commercial ERTS patches @@ -397,22 +399,22 @@ endif release_docs docs: mod2app ifeq ($(OTP_SMALL_BUILD),true) cd $(ERL_TOP)/lib && \ - PATH=$(ERL_TOP)/bin:$${PATH} ERL_TOP=$(ERL_TOP) \ - $(MAKE) TESTROOT=$(RELEASE_ROOT) $@ + PATH=$(ERL_TOP)/bin:"$${PATH}" ERL_TOP=$(ERL_TOP) \ + $(MAKE) TESTROOT="$(RELEASE_ROOT)" $@ else cd $(ERL_TOP)/lib && \ - PATH=$(ERL_TOP)/bin:$${PATH} ERL_TOP=$(ERL_TOP) \ - $(MAKE) BUILD_ALL=1 TESTROOT=$(RELEASE_ROOT) $@ + PATH=$(ERL_TOP)/bin:"$${PATH}" ERL_TOP=$(ERL_TOP) \ + $(MAKE) BUILD_ALL=1 TESTROOT="$(RELEASE_ROOT)" $@ endif cd $(ERL_TOP)/erts && \ - PATH=$(ERL_TOP)/bin:$${PATH} ERL_TOP=$(ERL_TOP) \ - $(MAKE) BUILD_ALL=1 TESTROOT=$(RELEASE_ROOT) $@ + PATH=$(ERL_TOP)/bin:"$${PATH}" ERL_TOP=$(ERL_TOP) \ + $(MAKE) BUILD_ALL=1 TESTROOT="$(RELEASE_ROOT)" $@ cd $(ERL_TOP)/system/doc && \ - PATH=$(ERL_TOP)/bin:$${PATH} \ - ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT=$(RELEASE_ROOT) $@ + PATH=$(ERL_TOP)/bin:"$${PATH}" \ + ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT="$(RELEASE_ROOT)" $@ mod2app: - PATH=$(ERL_TOP)/bin:$${PATH} escript $(ERL_TOP)/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml + PATH=$(ERL_TOP)/bin:"$${PATH}" escript $(ERL_TOP)/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml # ---------------------------------------------------------------------- ERLANG_EARS=$(BOOTSTRAP_ROOT)/bootstrap/erts @@ -429,46 +431,46 @@ emulator: libs: ifeq ($(OTP_SMALL_BUILD),true) cd lib && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt else cd lib && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt BUILD_ALL=true endif kernel: cd lib/kernel && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt BUILD_ALL=true stdlib: cd lib/stdlib && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt BUILD_ALL=true compiler: cd lib/compiler && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt BUILD_ALL=true hipe: cd lib/hipe && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt BUILD_ALL=true typer: cd lib/typer && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt BUILD_ALL=true syntax_tools: cd lib/syntax_tools && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt BUILD_ALL=true preloaded: cd erts/preloaded/src && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt BUILD_ALL=true dep depend: @@ -529,7 +531,7 @@ bootstrap_setup_target: secondary_bootstrap_build: cd lib && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt SECONDARY_BOOTSTRAP=true secondary_bootstrap_copy: @@ -622,7 +624,7 @@ secondary_bootstrap_copy: tertiary_bootstrap_build: cd lib && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) opt TERTIARY_BOOTSTRAP=true tertiary_bootstrap_copy: @@ -651,6 +653,8 @@ tertiary_bootstrap_copy: if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/include ; fi if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server ; fi if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server/include ; fi + if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test ; fi + if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test/include ; fi for x in lib/ic/ebin/*.beam; do \ BN=`basename $$x`; \ TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/ic/ebin/$$BN; \ @@ -728,6 +732,7 @@ tertiary_bootstrap_copy: true; \ done +# copy test includes to be able to compile tests with bootstrap compiler for x in lib/test_server/include/*.hrl; do \ BN=`basename $$x`; \ TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/test_server/include/$$BN; \ @@ -738,6 +743,17 @@ tertiary_bootstrap_copy: cp $$x $$TF; \ true; \ done + + for x in lib/common_test/include/*.hrl; do \ + BN=`basename $$x`; \ + TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/common_test/include/$$BN; \ + test -f $$TF && \ + test '!' -z "`find $$x -newer $$TF -print`" && \ + cp $$x $$TF; \ + test '!' -f $$TF && \ + cp $$x $$TF; \ + true; \ + done # cp lib/syntax_tools/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools/ebin .PHONY: check_recreate_primary_bootstrap recreate_primary_bootstrap @@ -860,13 +876,13 @@ KERNEL_PRELOAD_BEAMS=$(KERNEL_PRELOAD:%=$(BOOTSTRAP_TOP)/lib/kernel/ebin/%.beam) start_scripts: @cd erts/start_scripts \ - && ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} $(MAKE) script + && ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" $(MAKE) script # Creates "erl" and "erlc" scripts in bin/erl which uses the libraries in lib local_setup: @rm -f bin/erl bin/erlc bin/cerl @cd erts && \ - ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)$${PATH} \ + ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \ $(MAKE) local_setup @@ -888,8 +904,8 @@ tests release_tests: $(TEST_DIRS) $(TEST_DIRS): if test -f $@/Makefile; then \ - (cd $@; $(MAKE) TESTROOT=$(TESTSUITE_ROOT) \ - PATH=$(TEST_PATH_PREFIX)$(BOOT_PREFIX)$${PATH} release_tests) || exit $$?; \ + (cd $@; $(MAKE) TESTROOT="$(TESTSUITE_ROOT)" \ + PATH=$(TEST_PATH_PREFIX)$(BOOT_PREFIX)"$${PATH}" release_tests) || exit $$?; \ fi # @@ -902,29 +918,29 @@ INST_DEP += install.dirs install.emulator install.libs install.Install install.b install: $(INST_DEP) install-docs: - ERL_TOP=$(ERL_TOP) INSTALLROOT=$(ERLANG_LIBDIR) PATH=$(BOOT_PREFIX)$${PATH} \ - $(MAKE) RELEASE_ROOT=$(ERLANG_LIBDIR) release_docs + ERL_TOP=$(ERL_TOP) INSTALLROOT="$(ERLANG_LIBDIR)" PATH=$(BOOT_PREFIX)"$${PATH}" \ + $(MAKE) RELEASE_ROOT="$(ERLANG_LIBDIR)" release_docs install.emulator: cd erts && \ - ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)$${PATH} \ - $(MAKE) TESTROOT=$(ERLANG_LIBDIR) release + ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)"$${PATH}" \ + $(MAKE) TESTROOT="$(ERLANG_LIBDIR)" release install.libs: ifeq ($(OTP_SMALL_BUILD),true) cd lib && \ - ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)$${PATH} \ - $(MAKE) TESTROOT=$(ERLANG_LIBDIR) release + ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)"$${PATH}" \ + $(MAKE) TESTROOT="$(ERLANG_LIBDIR)" release else cd lib && \ - ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)$${PATH} \ - $(MAKE) TESTROOT=$(ERLANG_LIBDIR) BUILD_ALL=true release + ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)"$${PATH}" \ + $(MAKE) TESTROOT="$(ERLANG_LIBDIR)" BUILD_ALL=true release endif install.Install: - (cd $(ERLANG_LIBDIR) \ - && ./Install $(INSTALL_CROSS) -minimal $(ERLANG_INST_LIBDIR)) + (cd "$(ERLANG_LIBDIR)" \ + && ./Install $(INSTALL_CROSS) -minimal "$(ERLANG_INST_LIBDIR)") # # Install erlang base public files @@ -943,9 +959,9 @@ install.bin: # Directories needed before we can install # install.dirs: - test -d $(BINDIR) || ${MKSUBDIRS} $(BINDIR) - ${MKSUBDIRS} $(ERLANG_LIBDIR) - ${MKSUBDIRS} $(ERLANG_LIBDIR)/usr/lib + test -d "$(BINDIR)" || ${MKSUBDIRS} "$(BINDIR)" + ${MKSUBDIRS} "$(ERLANG_LIBDIR)" + ${MKSUBDIRS} "$(ERLANG_LIBDIR)/usr/lib" .PHONY: strict_install @@ -73,4 +73,4 @@ Copyright and License [1]: http://www.erlang.org [2]: http://wiki.github.com/erlang/otp/submitting-patches - [3]: http://www.erlang.org/faq.html + [3]: http://www.erlang.org/static/doc/mailinglist.html diff --git a/aclocal.m4 b/aclocal.m4 index 339a15a2bb..a76594d86f 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us dnl Cross compilation variables AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)]) +AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)]) @@ -606,6 +607,103 @@ ifelse([$5], , , [$5 fi ]) +dnl ---------------------------------------------------------------------- +dnl +dnl AC_DOUBLE_MIDDLE_ENDIAN +dnl +dnl Checks whether doubles are represented in "middle-endian" format. +dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly, +dnl as well as DOUBLE_MIDDLE_ENDIAN. +dnl +dnl + +AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN], +[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian, +[# It does not; compile a test program. +AC_RUN_IFELSE( +[AC_LANG_SOURCE([[#include <stdlib.h> + +int +main(void) +{ + int i = 0; + int zero = 0; + int bigendian; + int zero_index = 0; + + union + { + long int l; + char c[sizeof (long int)]; + } u; + + /* we'll use the one with 32-bit words */ + union + { + double d; + unsigned int c[2]; + } vint; + + union + { + double d; + unsigned long c[2]; + } vlong; + + union + { + double d; + unsigned short c[2]; + } vshort; + + + /* Are we little or big endian? From Harbison&Steele. */ + u.l = 1; + bigendian = (u.c[sizeof (long int) - 1] == 1); + + zero_index = bigendian ? 1 : 0; + + vint.d = 1.0; + vlong.d = 1.0; + vshort.d = 1.0; + + if (sizeof(unsigned int) == 4) + { + if (vint.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned long) == 4) + { + if (vlong.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned short) == 4) + { + if (vshort.c[zero_index] != 0) + zero = 1; + } + + exit (zero); +} +]])], + [ac_cv_c_double_middle_endian=no], + [ac_cv_c_double_middle_endian=yes], + [ac_cv_c_double_middle=unknown])]) +case $ac_cv_c_double_middle_endian in + yes) + m4_default([$1], + [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1, + [Define to 1 if your processor stores the words in a double in + middle-endian format (like some ARMs).])]) ;; + no) + $2 ;; + *) + m4_default([$3], + [AC_MSG_WARN([unknown double endianness +presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;; +esac +])# AC_C_DOUBLE_MIDDLE_ENDIAN + dnl ---------------------------------------------------------------------- dnl @@ -1337,6 +1435,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian]) fi +case X$erl_xcomp_double_middle_endian in + X) ;; + Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;; + *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);; +esac + +AC_C_DOUBLE_MIDDLE_ENDIAN + AC_ARG_ENABLE(native-ethr-impls, AS_HELP_STRING([--disable-native-ethr-impls], [disable native ethread implementations]), diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot Binary files differindex bdaf605488..3d2153e66d 100644 --- a/bootstrap/bin/start.boot +++ b/bootstrap/bin/start.boot diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot Binary files differindex bdaf605488..3d2153e66d 100644 --- a/bootstrap/bin/start_clean.boot +++ b/bootstrap/bin/start_clean.boot diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam Binary files differindex 4da4c26d92..7b1ee38ef8 100644 --- a/bootstrap/lib/compiler/ebin/beam_asm.beam +++ b/bootstrap/lib/compiler/ebin/beam_asm.beam diff --git a/bootstrap/lib/compiler/ebin/beam_type.beam b/bootstrap/lib/compiler/ebin/beam_type.beam Binary files differindex 116da39f89..3cb5f06859 100644 --- a/bootstrap/lib/compiler/ebin/beam_type.beam +++ b/bootstrap/lib/compiler/ebin/beam_type.beam diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam Binary files differindex 9c6ad019f9..523a6f03fb 100644 --- a/bootstrap/lib/compiler/ebin/beam_utils.beam +++ b/bootstrap/lib/compiler/ebin/beam_utils.beam diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam Binary files differindex 52643de79a..41523ed185 100644 --- a/bootstrap/lib/compiler/ebin/beam_validator.beam +++ b/bootstrap/lib/compiler/ebin/beam_validator.beam diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam Binary files differindex e2bd3c4ce0..c685035e23 100644 --- a/bootstrap/lib/compiler/ebin/compile.beam +++ b/bootstrap/lib/compiler/ebin/compile.beam diff --git a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam Binary files differindex d716583b97..1a176b2e3f 100644 --- a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam +++ b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam diff --git a/bootstrap/lib/kernel/ebin/code.beam b/bootstrap/lib/kernel/ebin/code.beam Binary files differindex 31ccb7367d..b7e4a46066 100644 --- a/bootstrap/lib/kernel/ebin/code.beam +++ b/bootstrap/lib/kernel/ebin/code.beam diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam Binary files differindex adc53aa5f3..dc10d111d2 100644 --- a/bootstrap/lib/kernel/ebin/code_server.beam +++ b/bootstrap/lib/kernel/ebin/code_server.beam diff --git a/bootstrap/lib/kernel/ebin/file.beam b/bootstrap/lib/kernel/ebin/file.beam Binary files differindex 6c3ed89cbb..aa7dcf388d 100644 --- a/bootstrap/lib/kernel/ebin/file.beam +++ b/bootstrap/lib/kernel/ebin/file.beam diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam Binary files differindex 9412148a16..028b5215a2 100644 --- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam +++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam Binary files differindex e47766329b..65f517ed39 100644 --- a/bootstrap/lib/kernel/ebin/inet.beam +++ b/bootstrap/lib/kernel/ebin/inet.beam diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam Binary files differindex 4355f300e9..bfb80cf827 100644 --- a/bootstrap/lib/kernel/ebin/rpc.beam +++ b/bootstrap/lib/kernel/ebin/rpc.beam diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam Binary files differindex 55f5adc24d..2e2b3ef4ed 100644 --- a/bootstrap/lib/stdlib/ebin/epp.beam +++ b/bootstrap/lib/stdlib/ebin/epp.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam Binary files differindex 798924c19b..875ccf94c2 100644 --- a/bootstrap/lib/stdlib/ebin/erl_lint.beam +++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam diff --git a/bootstrap/lib/stdlib/ebin/escript.beam b/bootstrap/lib/stdlib/ebin/escript.beam Binary files differindex 17881e2552..e832f973d7 100644 --- a/bootstrap/lib/stdlib/ebin/escript.beam +++ b/bootstrap/lib/stdlib/ebin/escript.beam diff --git a/bootstrap/lib/stdlib/ebin/filelib.beam b/bootstrap/lib/stdlib/ebin/filelib.beam Binary files differindex 5d8a6f7b07..20f48fe18e 100644 --- a/bootstrap/lib/stdlib/ebin/filelib.beam +++ b/bootstrap/lib/stdlib/ebin/filelib.beam diff --git a/bootstrap/lib/stdlib/ebin/gen_server.beam b/bootstrap/lib/stdlib/ebin/gen_server.beam Binary files differindex 3fa7d89db4..5d68dda4cf 100644 --- a/bootstrap/lib/stdlib/ebin/gen_server.beam +++ b/bootstrap/lib/stdlib/ebin/gen_server.beam diff --git a/bootstrap/lib/stdlib/ebin/lib.beam b/bootstrap/lib/stdlib/ebin/lib.beam Binary files differindex 4b67bbb774..3865cfb227 100644 --- a/bootstrap/lib/stdlib/ebin/lib.beam +++ b/bootstrap/lib/stdlib/ebin/lib.beam diff --git a/erts/Makefile.in b/erts/Makefile.in index 1979c50781..5df6d71ef3 100644 --- a/erts/Makefile.in +++ b/erts/Makefile.in @@ -31,21 +31,10 @@ ifeq ($(NO_START_SCRIPTS),) ERTSDIRS += start_scripts endif -# Until hybrid is nofrag, don't build it. -#BUILD_HYBRID_EMU=@ERTS_BUILD_HYBRID_EMU@ -BUILD_HYBRID_EMU=no - EXTRA_FLAVORS=smp -ifeq ($(BUILD_HYBRID_EMU),yes) -EXTRA_FLAVORS += hybrid -endif .PHONY: all -ifneq ($(BUILD_HYBRID_EMU),yes) all: smp opt -else -all: hybrid smp opt -endif .PHONY: docs docs: @@ -68,13 +57,6 @@ debug opt clean: $(EXTRA_FLAVORS): ( cd emulator && $(MAKE) opt FLAVOR=$@ ) -ifneq ($(BUILD_HYBRID_EMU),yes) -.PHONY: hybrid -hybrid: - @echo '*** Omitted build of hybrid heap emulator' - @echo '*** since target is $(TARGET)' -endif - # Make erl script and erlc in $(ERL_TOP)/bin which runs the compiled version # Note that erlc is not a script and requires extra handling on cygwin. # also note that this file is not created by autoconf, that's why @EXEEXT@ @@ -106,6 +88,7 @@ local_setup: chmod 755 $(ERL_TOP)/bin/erl.exe $(ERL_TOP)/bin/erlc.exe \ $(ERL_TOP)/bin/werl.exe; \ make_local_ini.sh $(ERL_TOP); \ + cp $(ERL_TOP)/bin/erl.ini $(ERL_TOP)/bin/$(TARGET)/erl.ini; \ else \ sed -e "s;%FINAL_ROOTDIR%;$(ERL_TOP);" \ -e "s;erts-.*/bin;bin/$(TARGET);" \ diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 339a15a2bb..a76594d86f 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us dnl Cross compilation variables AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)]) +AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)]) @@ -606,6 +607,103 @@ ifelse([$5], , , [$5 fi ]) +dnl ---------------------------------------------------------------------- +dnl +dnl AC_DOUBLE_MIDDLE_ENDIAN +dnl +dnl Checks whether doubles are represented in "middle-endian" format. +dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly, +dnl as well as DOUBLE_MIDDLE_ENDIAN. +dnl +dnl + +AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN], +[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian, +[# It does not; compile a test program. +AC_RUN_IFELSE( +[AC_LANG_SOURCE([[#include <stdlib.h> + +int +main(void) +{ + int i = 0; + int zero = 0; + int bigendian; + int zero_index = 0; + + union + { + long int l; + char c[sizeof (long int)]; + } u; + + /* we'll use the one with 32-bit words */ + union + { + double d; + unsigned int c[2]; + } vint; + + union + { + double d; + unsigned long c[2]; + } vlong; + + union + { + double d; + unsigned short c[2]; + } vshort; + + + /* Are we little or big endian? From Harbison&Steele. */ + u.l = 1; + bigendian = (u.c[sizeof (long int) - 1] == 1); + + zero_index = bigendian ? 1 : 0; + + vint.d = 1.0; + vlong.d = 1.0; + vshort.d = 1.0; + + if (sizeof(unsigned int) == 4) + { + if (vint.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned long) == 4) + { + if (vlong.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned short) == 4) + { + if (vshort.c[zero_index] != 0) + zero = 1; + } + + exit (zero); +} +]])], + [ac_cv_c_double_middle_endian=no], + [ac_cv_c_double_middle_endian=yes], + [ac_cv_c_double_middle=unknown])]) +case $ac_cv_c_double_middle_endian in + yes) + m4_default([$1], + [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1, + [Define to 1 if your processor stores the words in a double in + middle-endian format (like some ARMs).])]) ;; + no) + $2 ;; + *) + m4_default([$3], + [AC_MSG_WARN([unknown double endianness +presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;; +esac +])# AC_C_DOUBLE_MIDDLE_ENDIAN + dnl ---------------------------------------------------------------------- dnl @@ -1337,6 +1435,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian]) fi +case X$erl_xcomp_double_middle_endian in + X) ;; + Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;; + *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);; +esac + +AC_C_DOUBLE_MIDDLE_ENDIAN + AC_ARG_ENABLE(native-ethr-impls, AS_HELP_STRING([--disable-native-ethr-impls], [disable native ethread implementations]), diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general index 88697b788d..ac792b44b5 100644 --- a/erts/autoconf/vxworks/sed.general +++ b/erts/autoconf/vxworks/sed.general @@ -70,7 +70,6 @@ s|@HCLIBS@|| s|@ENABLE_ALLOC_TYPE_VARS@|| s|@TERMCAP_LIB@|| s|@ERTS_BUILD_SMP_EMU@|no| -s|@ERTS_BUILD_HYBRID_EMU@|no| s|@HAVE_VALGRIND@|no| s|@EXEEXT@|| s|@WITH_SCTP@|| diff --git a/erts/configure.in b/erts/configure.in index cb1b00b8b1..8d70a1b74a 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -157,14 +157,6 @@ AS_HELP_STRING([--without-termcap], [], [with_termcap=yes]) - -AC_ARG_ENABLE(hybrid-heap, -AS_HELP_STRING([--enable-hybrid-heap], [enable hybrid heap]), -[ case "$enableval" in - no) enable_hybrid_heap=no ;; - *) enable_hybrid_heap=yes ;; - esac ], enable_hybrid_heap=unknown) - AC_ARG_ENABLE(lock-checking, AS_HELP_STRING([--enable-lock-checking], [enable lock checking]), [ case "$enableval" in @@ -626,6 +618,8 @@ case $chk_arch_ in armv5b) ARCH=arm;; armv5teb) ARCH=arm;; armv5tel) ARCH=arm;; + armv5tejl) ARCH=arm;; + armv7l) ARCH=arm;; tile) ARCH=tile;; *) ARCH=noarch;; esac @@ -1736,6 +1730,7 @@ case X$erl_xcomp_bigendian in esac AC_C_BIGENDIAN +AC_C_DOUBLE_MIDDLE_ENDIAN dnl fdatasync syscall (Unix only) AC_CHECK_FUNCS([fdatasync]) @@ -1887,9 +1882,10 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlop pread pwrite writev memmove strerror strerror_r strncasecmp \ gethrtime localtime_r gmtime_r inet_pton mmap mremap memcpy mallopt \ sbrk _sbrk __sbrk brk _brk __brk \ - flockfile fstat strlcpy strlcat setsid posix2time setlocale nl_langinfo poll]) + flockfile fstat strlcpy strlcat setsid posix2time time2posix \ + setlocale nl_langinfo poll]) -AC_CHECK_DECLS([posix2time],,,[#include <time.h>]) +AC_CHECK_DECLS([posix2time, time2posix],,,[#include <time.h>]) disable_vfork=false if test "x$EMU_THR_LIB_NAME" != "x"; then @@ -1949,6 +1945,64 @@ AC_CHECK_FUNCS([openpty]) AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h) AC_CHECK_FUNCS([getifaddrs]) +dnl Checks for variables in6addr_any and in6addr_loopback, +dnl +dnl They normally declared by netinet/in.h, according to POSIX, +dnl but not on Windows 7 (Windows SDK 7.1). I would have liked +dnl to just write AC_CHECK_DECL([in6addr_any], ...) but if doing so, +dnl the configure check fails erroneously on Linux with the error +dnl "cannot convert to a pointer type", on a line looking like +dnl "char *p = (char *) in6addr_any;", so work around that +dnl with some more code. +AC_CACHE_CHECK( + [whether in6addr_any is declared], + [erts_cv_have_in6addr_any], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ + #include <sys/types.h> + #include <sys/socket.h> + #include <netinet/in.h> + ]], + [[printf("%d", in6addr_any.s6_addr[16]);]] + )], + [erts_cv_have_in6addr_any=yes], + [erts_cv_have_in6addr_any=no] + )] +) + +case "$erts_cv_have_in6addr_any" in + yes) + AC_DEFINE([HAVE_IN6ADDR_ANY], [1], + [Define to 1 if you have the variable in6addr_any declared.]) +esac + +AC_CACHE_CHECK( + [whether in6addr_loopback is declared], + [erts_cv_have_in6addr_loopback], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ + #include <sys/types.h> + #include <sys/socket.h> + #include <netinet/in.h> + ]], + [[printf("%d", in6addr_loopback.s6_addr[16]);]] + )], + [erts_cv_have_in6addr_loopback=yes], + [erts_cv_have_in6addr_loopback=no] + )] +) + +case "$erts_cv_have_in6addr_loopback" in + yes) + AC_DEFINE([HAVE_IN6ADDR_LOOPBACK], [1], + [Define to 1 if you have the variable in6addr_loopback declared.]) +esac + +AC_CHECK_DECLS([IN6ADDR_ANY_INIT, IN6ADDR_LOOPBACK_INIT], [], [], + [#include <netinet/in.h>]) + dnl ---------------------------------------------------------------------- dnl Checks for features/quirks in the system that affects Erlang. dnl ---------------------------------------------------------------------- @@ -3134,33 +3188,6 @@ fi AC_SUBST(TSP_APP) # -# Check if we should build hybrid emulator -# - -AC_MSG_CHECKING([whether a hybrid heap emulator should be built]) -case $enable_hybrid_heap-$host_os in - yes-*) - AC_MSG_RESULT([yes; enabled by user]) - ERTS_BUILD_HYBRID_EMU=yes;; - no-*) - AC_MSG_RESULT([no; disabled by user]) - ERTS_BUILD_HYBRID_EMU=no;; - *-win32|*-vxworks) # vxworks have their own "configure scripts"... - AC_MSG_RESULT([no; default on this platform]) - ERTS_BUILD_HYBRID_EMU=no;; - *) - AC_MSG_RESULT([yes; default on this platform]) - ERTS_BUILD_HYBRID_EMU=yes;; -esac - - -if test $ERTS_BUILD_HYBRID_EMU = yes; then - AC_DEFINE(ERTS_HAVE_HYBRID_EMU, 1, [Define if the hybrid emulator is built]) -fi - -AC_SUBST(ERTS_BUILD_HYBRID_EMU) - -# # Check if we should enable HiPE. # diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index cfa5527474..13cf003aad 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -174,15 +174,15 @@ $(SPECDIR)/specs_erts_alloc.xml: 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 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man1 - $(INSTALL_DATA) $(MAN1_FILES) $(RELEASE_PATH)/man/man1 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1" release_spec: diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index cfbc38f176..8c028e1d14 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -442,11 +442,7 @@ system with SMP support is available. <c>-smp auto</c> starts the Erlang runtime system with SMP support enabled if it is available and more than one logical processor are detected. - <c>-smp disable</c> starts a runtime system without SMP support. - By default <c>-smp auto</c> will be used unless a conflicting - parameter has been passed, then <c>-smp disable</c> will be - used. Currently only the <c>-hybrid</c> parameter conflicts - with <c>-smp auto</c>.</p> + <c>-smp disable</c> starts a runtime system without SMP support.</p> <p><em>NOTE</em>: The runtime system with SMP support will not be available on all supported platforms. See also the <seealso marker="#+S">+S</seealso> flag.</p> @@ -537,7 +533,7 @@ <p>Force the <c>compressed</c> option on all ETS tables. Only intended for test and evaluation.</p> </item> - <tag><c><![CDATA[+fnl]]></c></tag> + <tag><marker id="file_name_encoding"></marker><c><![CDATA[+fnl]]></c></tag> <item> <p>The VM works with file names as if they are encoded using the ISO-latin-1 encoding, disallowing Unicode characters with codepoints beyond 255. This is default on operating systems that have transparent file naming, i.e. all Unixes except MacOSX.</p> </item> @@ -766,6 +762,16 @@ <seealso marker="erlang#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>. </p> </item> + <tag><marker id="+sbwt"><c>+sbwt none|very_short|short|medium|long|very_long</c></marker></tag> + <item> + <p>Set scheduler busy wait threshold. Default is <c>medium</c>. + The threshold determines how long schedulers should busy + wait when running out of work before going to sleep. + </p> + <p><em>NOTE:</em> This flag may be removed or changed at any time + without prior notice. + </p> + </item> <tag><marker id="+scl"><c>+scl true|false</c></marker></tag> <item> <p>Enable or disable scheduler compaction of load. By default @@ -901,6 +907,17 @@ <p>For more information, see <seealso marker="erlang#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>.</p> </item> + <tag><marker id="+sws"><c>+sws default|legacy|proposal</c></marker></tag> + <item> + <p>Set scheduler wakeup strategy. Default is <c>legacy</c> (has been + used since OTP-R13B). The <c>proposal</c> strategy is the currently + proposed strategy for OTP-R16. Note that the <c>proposal</c> strategy + might change during OTP-R15. + </p> + <p><em>NOTE:</em> This flag may be removed or changed at any time + without prior notice. + </p> + </item> <tag><marker id="+swt"><c>+swt very_low|low|medium|high|very_high</c></marker></tag> <item> <p>Set scheduler wakeup threshold. Default is <c>medium</c>. diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 4fd74b783e..187c263b60 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -4,7 +4,7 @@ <cref> <header> <copyright> - <year>2001</year><year>2011</year> + <year>2001</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -1067,7 +1067,7 @@ typedef struct ErlIOVec { </desc> </func> <func> - <name><ret>ErlDrvBinary*</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name> + <name><ret>ErlDrvBinary *</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name> <fsummary>Allocate a driver binary</fsummary> <desc> <marker id="driver_alloc_binary"></marker> @@ -1087,7 +1087,7 @@ typedef struct ErlIOVec { </desc> </func> <func> - <name><ret>ErlDrvBinary*</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name> + <name><ret>ErlDrvBinary *</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name> <fsummary>Resize a driver binary</fsummary> <desc> <marker id="driver_realloc_binary"></marker> @@ -1277,7 +1277,7 @@ typedef struct ErlIOVec { </desc> </func> <func> - <name><ret>SysIOVec*</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name> + <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name> <fsummary>Get the driver queue as a vector</fsummary> <desc> <marker id="driver_peekq"></marker> @@ -1481,7 +1481,7 @@ typedef struct ErlIOVec { </desc> </func> <func> - <name><ret>char*</ret><nametext>erl_errno_id(int error)</nametext></name> + <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name> <fsummary>Get erlang error atom name from error number</fsummary> <desc> <marker id="erl_errno_id"></marker> diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 5fc6508aad..f484e9eaf7 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -4,7 +4,7 @@ <cref> <header> <copyright> - <year>2001</year><year>2011</year> + <year>2001</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -472,7 +472,7 @@ typedef enum { </section> <funcs> - <func><name><ret>void*</ret><nametext>enif_alloc(size_t size)</nametext></name> + <func><name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name> <fsummary>Allocate dynamic memory.</fsummary> <desc><p>Allocate memory of <c>size</c> bytes. Return NULL if allocation failed.</p></desc> </func> @@ -489,7 +489,7 @@ typedef enum { <p>Return true on success or false if allocation failed.</p> </desc> </func> - <func><name><ret>ErlNifEnv*</ret><nametext>enif_alloc_env()</nametext></name> + <func><name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name> <fsummary>Create a new environment</fsummary> <desc><p>Allocate a new process independent environment. The environment can be used to hold terms that is not bound to any process. Such terms can @@ -499,7 +499,7 @@ typedef enum { <p>Return pointer to the new environment.</p> </desc> </func> - <func><name><ret>void*</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name> + <func><name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name> <fsummary>Allocate a memory managed resource object</fsummary> <desc><p>Allocate a memory managed resource object of type <c>type</c> and size <c>size</c> bytes.</p></desc> </func> @@ -522,7 +522,7 @@ typedef enum { <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_broadcast">erl_drv_cond_broadcast</seealso>. </p></desc> </func> - <func><name><ret>ErlNifCond*</ret><nametext>enif_cond_create(char *name)</nametext></name> + <func><name><ret>ErlNifCond *</ret><nametext>enif_cond_create(char *name)</nametext></name> <fsummary></fsummary> <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_create">erl_drv_cond_create</seealso>. </p></desc> @@ -840,7 +840,7 @@ typedef enum { <fsummary>Create an integer term from a long int</fsummary> <desc><p>Create an integer term from a <c>long int</c>.</p></desc> </func> - <func><name><ret>unsigned char*</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name> + <func><name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name> <fsummary>Allocate and create a new binary term</fsummary> <desc><p>Allocate a binary of size <c>size</c> bytes and create an owning term. The binary data is mutable until the calling NIF returns. This is a @@ -951,7 +951,7 @@ typedef enum { <fsummary>Create an integer term from an unsigned long int</fsummary> <desc><p>Create an integer term from an <c>unsigned long int</c>.</p></desc> </func> - <func><name><ret>ErlNifMutex*</ret><nametext>enif_mutex_create(char *name)</nametext></name> + <func><name><ret>ErlNifMutex *</ret><nametext>enif_mutex_create(char *name)</nametext></name> <fsummary></fsummary> <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_create">erl_drv_mutex_create</seealso>. </p></desc> @@ -976,7 +976,7 @@ typedef enum { <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_unlock">erl_drv_mutex_unlock</seealso>. </p></desc> </func> - <func><name><ret>ErlNifResourceType*</ret><nametext>enif_open_resource_type(ErlNifEnv* env, + <func><name><ret>ErlNifResourceType *</ret><nametext>enif_open_resource_type(ErlNifEnv* env, const char* module_str, const char* name, ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext></name> <fsummary>Create or takeover a resource type</fsummary> @@ -1005,7 +1005,7 @@ typedef enum { and <seealso marker="#upgrade">upgrade</seealso>.</p> </desc> </func> - <func><name><ret>void*</ret><nametext>enif_priv_data(ErlNifEnv* env)</nametext></name> + <func><name><ret>void *</ret><nametext>enif_priv_data(ErlNifEnv* env)</nametext></name> <fsummary>Get the private data of a NIF library</fsummary> <desc><p>Return the pointer to the private data that was set by <c>load</c>, <c>reload</c> or <c>upgrade</c>.</p> @@ -1033,7 +1033,7 @@ typedef enum { References made by <seealso marker="#enif_make_resource">enif_make_resource</seealso> can only be removed by the garbage collector.</p></desc> </func> - <func><name><ret>ErlNifRWLock*</ret><nametext>enif_rwlock_create(char *name)</nametext></name> + <func><name><ret>ErlNifRWLock *</ret><nametext>enif_rwlock_create(char *name)</nametext></name> <fsummary></fsummary> <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_create">erl_drv_rwlock_create</seealso>. </p></desc> @@ -1073,7 +1073,7 @@ typedef enum { <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock</seealso>. </p></desc> </func> - <func><name><ret>ErlNifPid*</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name> + <func><name><ret>ErlNifPid *</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name> <fsummary>Get the pid of the calling process.</fsummary> <desc><p>Initialize the pid variable <c>*pid</c> to represent the calling process. Return <c>pid</c>.</p></desc> @@ -1129,7 +1129,7 @@ typedef enum { <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_join">erl_drv_thread_join </seealso>. </p></desc> </func> - <func><name><ret>ErlNifThreadOpts*</ret><nametext>enif_thread_opts_create(char *name)</nametext></name> + <func><name><ret>ErlNifThreadOpts *</ret><nametext>enif_thread_opts_create(char *name)</nametext></name> <fsummary></fsummary> <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_create">erl_drv_thread_opts_create</seealso>. </p></desc> @@ -1154,7 +1154,7 @@ typedef enum { <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy</seealso>. </p></desc> </func> - <func><name><ret>void*</ret><nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name> + <func><name><ret>void *</ret><nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name> <fsummary></fsummary> <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_get">erl_drv_tsd_get</seealso>. </p></desc> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 0963904b83..248b755969 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2863,6 +2863,10 @@ os_prompt% </pre> <p>It can only be used to check the local time of day if the time-zone info of the underlying operating system is properly configured.</p> + <p>If you do not need the return value to be unique and + monotonically increasing, use + <seealso marker="kernel:os#timestamp/0">os:timestamp/0</seealso> + instead to avoid some overhead.</p> </desc> </func> <func> @@ -3035,8 +3039,11 @@ os_prompt% </pre> port process. Both <c>Name</c> and <c>Val</c> must be strings. The one exception is <c>Val</c> being the atom <c>false</c> (in analogy with <c>os:getenv/1</c>), which - removes the environment variable. Not available on - VxWorks.</p> + removes the environment variable.</p> + <p>If Unicode filename encoding is in effect (see the <seealso + marker="erts:erl#file_name_encoding">erl manual + page</seealso>), the strings (both <c>Name</c> and + <c>Value</c>) may contain characters with codepoints > 255.</p> </item> <tag><c>{args, [ string() ]}</c></tag> <item> @@ -3573,6 +3580,10 @@ os_prompt% </pre> <p><c>Bytes</c> is the total number of bytes written to the port.</p> </item> + <tag><c>{os_pid, Integer | undefined}</c></tag> + <item> + <p><c>Integer</c> is the process identifier (or equivalent) of an OS process created with <c>open_port({spawn | spawn_executable, Command}, Options)</c>. If the port is not the result of spawning an OS process, the value is <c>undefined</c>.</p> + </item> </taglist> <p>Failure: <c>badarg</c> if <c>Port</c> is not a local port.</p> </desc> @@ -4005,14 +4016,6 @@ os_prompt% </pre> <p><c>Size</c> is the size in bytes of the process. This includes call stack, heap and internal structures.</p> </item> - <tag><c>{message_binary, BinInfo}</c></tag> - <item> - <p><c>BinInfo</c> is a list containing miscellaneous information - about binaries currently being referred to by the message - area. This <c>InfoTuple</c> is only valid on an emulator - using the hybrid heap type. This <c>InfoTuple</c> may be - changed or removed without prior notice.</p> - </item> <tag><c>{message_queue_len, MessageQueueLen}</c></tag> <item> <p><c>MessageQueueLen</c> is the number of messages @@ -5799,10 +5802,6 @@ ok can spawn a process that does not use the default settings.</p> </item> - <tag><c>global_heaps_size</c></tag> - <item> - <p>Returns the current size of the shared (global) heap.</p> - </item> <tag><c>heap_sizes</c></tag> <item> <p>Returns a list of integers representing valid heap sizes @@ -5812,7 +5811,7 @@ ok <tag><c>heap_type</c></tag> <item> <p>Returns the heap type used by the current emulator. - Currently the following heap types exist:</p> + Currently only the following heap type exists:</p> <taglist> <tag><c>private</c></tag> <item> @@ -5821,17 +5820,6 @@ ok allowed. Messages passed between processes are copied between heaps.</p> </item> - <tag><c>shared</c></tag> - <item> - <p>One heap for use by all processes. Messages passed - between processes are passed by reference.</p> - </item> - <tag><c>hybrid</c></tag> - <item> - <p>A hybrid of the <c>private</c> and <c>shared</c> heap - types. A shared heap as well as private heaps are - used.</p> - </item> </taglist> </item> <tag><c>info</c></tag> diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml index ebf76a2afe..09ed4bd372 100644 --- a/erts/doc/src/erlc.xml +++ b/erts/doc/src/erlc.xml @@ -67,7 +67,7 @@ <item> <p>Instructs the compiler to search for include files in the specified directory. When encountering an - <c><![CDATA[-include]]></c> or <c><![CDATA[-include_dir]]></c> directive, the + <c><![CDATA[-include]]></c> or <c><![CDATA[-include_lib]]></c> directive, the compiler searches for header files in the following directories:</p> <list type="ordered"> @@ -129,12 +129,6 @@ This option will be ignored by compilers that have a a single output format.</p> </item> - <tag>-hybrid</tag> - <item> - <p>Compile using the hybrid-heap emulator. This is mainly useful - for compiling native code, which needs to be compiled with the same - run-time system that it should be run on.</p> - </item> <tag>-smp</tag> <item> <p>Compile using the SMP emulator. This is mainly useful diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml index c1ecbc7b77..b2f34ed247 100644 --- a/erts/doc/src/erlsrv.xml +++ b/erts/doc/src/erlsrv.xml @@ -357,11 +357,12 @@ The environment of an Erlang machine started the console subsystem and programs running as window applications. An application which runs in the console subsystem (normal for port programs) uses the win32 function - <c><![CDATA[SetConsoleCtrlHandler]]></c> to a control handler that returns - TRUE in answer to the <c><![CDATA[CTRL_LOGOFF_EVENT]]></c>. Other - applications just forward <c><![CDATA[WM_ENDSESSION]]></c> and - <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure. Here - is a brief example in C of how to set the console control + <c><![CDATA[SetConsoleCtrlHandler]]></c> to register a control handler + that returns TRUE in answer to the <c><![CDATA[CTRL_LOGOFF_EVENT]]></c> + and <c><![CDATA[CTRL_SHUTDOWN_EVENT]]></c> events. Other applications + just forward <c><![CDATA[WM_ENDSESSION]]></c> and + <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure. + Here is a brief example in C of how to set the console control handler:</p> <code type="none"><![CDATA[ #include <windows.h> @@ -372,6 +373,8 @@ The environment of an Erlang machine started BOOL WINAPI service_aware_handler(DWORD ctrl){ if(ctrl == CTRL_LOGOFF_EVENT) return TRUE; + if(ctrl == CTRL_SHUTDOWN_EVENT) + return TRUE; return FALSE; } diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 028a2bbf3d..6b6a3374d1 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,74 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 5.9.1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If threads support for the runtime system had been + disabled at compile time (<c>--disable-threads</c> had + been passed to <c>configure</c>), and the <c>+A</c> + command line argument of <c>erl</c> was passed when + starting the runtime system, <seealso + marker="erl_driver#driver_system_info">driver_system_info()</seealso> + erroneously claimed that the runtime system had async + threads even though it had not.</p> + <p> + Due to this bug the file driver did not split tasks into + smaller chunks, but instead completed the whole task at + once, i.e., the scheduler got occupied with I/O for a + longer time than intended.</p> + <p> + Own Id: OTP-10059</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + A proposal for a new scheduler wakeup strategy has been + implemented. For more information see the documentation + of the <seealso marker="erl#+sws">+sws</seealso> command + line argument of <c>erl</c>.</p> + <p> + Own Id: OTP-10033 Aux Id: Seq12025 </p> + </item> + <item> + <p> + A switch for configuration of busy wait length for + scheduler threads has been added. For more information + see the documentation of the <seealso + marker="erl#+sbwt">+sbwt</seealso> command line argument + of <c>erl</c>.</p> + <p> + Own Id: OTP-10044 Aux Id: Seq11976 </p> + </item> + <item> + <p> + The extra memory barriers introduced by bug-fix OTP-9281 + were unnecessarily used also on tables without the + <c>write_concurrency</c> option enabled. This could + unnecessarily degrade performance of ETS tables without + <c>write_concurrency</c> on some hardware (e.g. PowerPC) + while not effecting performance at all on other hardware + (e.g. x86/x86_64).</p> + <p> + OTP-9281 (R14B03): ETS tables using the + <c>write_concurrency</c> option could potentially get + into an internally inconsistent state.</p> + <p> + Own Id: OTP-10048 Aux Id: OTP-9281 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 5.9.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 2efbe2d57e..985ef72517 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -136,13 +136,6 @@ ENABLE_ALLOC_TYPE_VARS += smp nofrag M4FLAGS += -DERTS_SMP=1 else -ifeq ($(FLAVOR),hybrid) -FLAVOR_MARKER=.hybrid -FLAVOR_FLAGS=-DHYBRID -ENABLE_ALLOC_TYPE_VARS += hybrid -else - - # If flavor isn't one of the above, it *is* plain flavor... override FLAVOR=plain FLAVOR_MARKER= @@ -151,7 +144,6 @@ ENABLE_ALLOC_TYPE_VARS += nofrag M4FLAGS += endif -endif TF_MARKER=$(TYPEMARKER)$(FLAVOR_MARKER) @@ -444,18 +436,18 @@ release_spec: @echo $(VOID_EMULATOR)' - omitted target release_spec (install)' else release_spec: all - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/man - $(INSTALL_DIR) $(RELSYSDIR)/doc - $(INSTALL_DIR) $(RELSYSDIR)/bin - $(INSTALL_DIR) $(RELEASE_PATH)/usr/include - $(INSTALL_DATA) $(RELEASE_INCLUDES) $(RELEASE_PATH)/usr/include - $(INSTALL_DATA) $(RELEASE_INCLUDES) $(RELSYSDIR)/include - $(INSTALL_PROGRAM) $(BINDIR)/$(EMULATOR_EXECUTABLE) $(RELSYSDIR)/bin + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/man" + $(INSTALL_DIR) "$(RELSYSDIR)/doc" + $(INSTALL_DIR) "$(RELSYSDIR)/bin" + $(INSTALL_DIR) "$(RELEASE_PATH)/usr/include" + $(INSTALL_DATA) $(RELEASE_INCLUDES) "$(RELEASE_PATH)/usr/include" + $(INSTALL_DATA) $(RELEASE_INCLUDES) "$(RELSYSDIR)/include" + $(INSTALL_PROGRAM) $(BINDIR)/$(EMULATOR_EXECUTABLE) "$(RELSYSDIR)/bin" ifeq ($(ERLANG_OSTYPE), unix) - $(INSTALL_PROGRAM) $(BINDIR)/$(CS_EXECUTABLE) $(RELSYSDIR)/bin + $(INSTALL_PROGRAM) $(BINDIR)/$(CS_EXECUTABLE) "$(RELSYSDIR)/bin" endif endif @@ -748,7 +740,7 @@ RUN_OBJS = \ $(OBJDIR)/register.o $(OBJDIR)/break.o \ $(OBJDIR)/erl_async.o $(OBJDIR)/erl_lock_check.o \ $(OBJDIR)/erl_gc.o $(OBJDIR)/erl_lock_count.o \ - $(OBJDIR)/erl_nmgc.o $(OBJDIR)/erl_posix_str.o \ + $(OBJDIR)/erl_posix_str.o \ $(OBJDIR)/erl_bits.o $(OBJDIR)/erl_math.o \ $(OBJDIR)/erl_fun.o $(OBJDIR)/erl_bif_port.o \ $(OBJDIR)/erl_term.o $(OBJDIR)/erl_node_tables.o \ @@ -932,6 +924,11 @@ $(TTF_DIR)/hipe_arm_bifs.S: hipe/hipe_arm_bifs.m4 hipe/hipe_arm_asm.m4 \ $(OBJDIR)/hipe_arm_bifs.o: $(TTF_DIR)/hipe_arm_bifs.S \ $(TTF_DIR)/hipe_literals.h +# Use -fomit-frame-pointer to work around gcc (v4.5.2) bug causing +# "error: r7 cannot be used in asm here" for DEBUG build. +$(OBJDIR)/hipe_arm.o: hipe/hipe_arm.c + $(CC) $(subst O2,O3, $(CFLAGS)) -fomit-frame-pointer $(INCLUDES) -c $< -o $@ + # end of HiPE section ######################################## diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 02735d4b68..106fad030b 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -244,7 +244,6 @@ atom gather_sched_wall_time_result atom getting_linked atom getting_unlinked atom global -atom global_heaps_size atom Gt='>' atom grun atom group_leader @@ -259,7 +258,6 @@ atom hide atom high atom hipe_architecture atom http httph https http_response http_request http_header http_eoh http_error http_bin httph_bin -atom hybrid atom id atom if_clause atom imports @@ -391,6 +389,7 @@ atom opt atom or atom ordered_set atom orelse +atom os_pid atom os_type atom os_version atom ose_bg_proc diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 78a9d76a20..ada2d152b7 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -419,10 +419,8 @@ check_process_code(Process* rp, Module* modp) Uint mod_size; BeamInstr* end; Eterm* sp; -#ifndef HYBRID /* FIND ME! */ struct erl_off_heap_header* oh; int done_gc = 0; -#endif #define INSIDE(a) (start <= (a) && (a) < end) @@ -481,7 +479,6 @@ check_process_code(Process* rp, Module* modp) * See if there are funs that refer to the old version of the module. */ -#ifndef HYBRID /* FIND ME! */ rescan: for (oh = MSO(rp).first; oh; oh = oh->next) { if (thing_subtag(oh->thing_word) == FUN_SUBTAG) { @@ -507,7 +504,6 @@ check_process_code(Process* rp, Module* modp) } } } -#endif /* * See if there are constants inside the module referenced by the process. diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 18a57931ae..6d3b15cd46 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -26,7 +26,6 @@ #include "erl_vm.h" #include "global.h" #include "erl_process.h" -#include "erl_nmgc.h" #include "error.h" #include "bif.h" #include "big.h" @@ -253,20 +252,6 @@ void** beam_ops; extern int count_instructions; #endif -#if defined(HYBRID) -#define SWAPIN \ - g_htop = global_htop; \ - g_hend = global_hend; \ - HTOP = HEAP_TOP(c_p); \ - E = c_p->stop - -#define SWAPOUT \ - global_htop = g_htop; \ - global_hend = g_hend; \ - HEAP_TOP(c_p) = HTOP; \ - c_p->stop = E - -#else #define SWAPIN \ HTOP = HEAP_TOP(c_p); \ E = c_p->stop @@ -294,8 +279,6 @@ extern int count_instructions; #define LIGHT_SWAPIN HTOP = HEAP_TOP(c_p) -#endif - #ifdef FORCE_HEAP_FRAGS # define HEAP_SPACE_VERIFIED(Words) do { \ c_p->space_verified = (Words); \ @@ -457,36 +440,6 @@ extern int count_instructions; CHECK_TERM(r(0)); \ } while (0) -#ifdef HYBRID -#ifdef INCREMENTAL -#define TestGlobalHeap(Nh, Live, hp) \ - do { \ - unsigned need = (Nh); \ - ASSERT(global_heap <= g_htop && g_htop <= global_hend); \ - SWAPOUT; \ - reg[0] = r(0); \ - FCALLS -= need; \ - (hp) = IncAlloc(c_p,need,reg,(Live)); \ - r(0) = reg[0]; \ - SWAPIN; \ - } while (0) -#else -#define TestGlobalHeap(Nh, Live, hp) \ - do { \ - unsigned need = (Nh); \ - ASSERT(global_heap <= g_htop && g_htop <= global_hend); \ - if (g_hend - g_htop < need) { \ - SWAPOUT; \ - reg[0] = r(0); \ - FCALLS -= erts_global_garbage_collect(c_p, need, reg, (Live)); \ - r(0) = reg[0]; \ - SWAPIN; \ - } \ - (hp) = global_htop; \ - } while (0) -#endif -#endif /* HYBRID */ - #define Init(N) make_blank(yb(N)) #define Init2(Y1, Y2) do { make_blank(Y1); make_blank(Y2); } while (0) @@ -1178,12 +1131,6 @@ void process_main(void) */ register Eterm* HTOP REG_htop = NULL; - -#ifdef HYBRID - Eterm *g_htop; - Eterm *g_hend; -#endif - /* Stack pointer. Grows downwards; points * to last item pushed (normally a saved * continuation pointer). @@ -6549,10 +6496,8 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) hp = funp->env; erts_refc_inc(&fe->refc, 2); funp->thing_word = HEADER_FUN; -#ifndef HYBRID /* FIND ME! */ funp->next = MSO(p).first; MSO(p).first = (struct erl_off_heap_header*) funp; -#endif funp->fe = fe; funp->num_free = num_free; funp->creator = p->id; diff --git a/erts/emulator/beam/benchmark.c b/erts/emulator/beam/benchmark.c index 7fbf44a03c..7ac14b8e8b 100644 --- a/erts/emulator/beam/benchmark.c +++ b/erts/emulator/beam/benchmark.c @@ -33,17 +33,6 @@ unsigned long long messages_copied; unsigned long long messages_ego; unsigned long long minor_gc; unsigned long long major_gc; -#ifdef HYBRID -unsigned long long minor_global_gc; -unsigned long long major_global_gc; -unsigned long long gc_in_copy; -#ifdef INCREMENTAL -unsigned long long minor_gc_cycles; -unsigned long long major_gc_cycles; -unsigned long long minor_gc_stages; -unsigned long long major_gc_stages; -#endif -#endif #endif /* BM_COUNTERS */ #ifdef BM_TIMERS @@ -191,17 +180,6 @@ void init_benchmarking() messages_ego = 0; minor_gc = 0; major_gc = 0; -#ifdef HYBRID - minor_global_gc = 0; - major_global_gc = 0; - gc_in_copy = 0; -#ifdef INCREMENTAL - minor_gc_cycles = 0; - major_gc_cycles = 0; - minor_gc_stages = 0; - major_gc_stages = 0; -#endif -#endif #endif /* BM_COUNTERS */ #ifdef BM_HEAP_SIZES @@ -243,16 +221,6 @@ void save_statistics() erts_fprintf(file,"Number of processes spawned: %lld\n",processes_spawned); erts_fprintf(file,"Number of local minor GCs: %lld\n",minor_gc); erts_fprintf(file,"Number of local major GCs: %lld\n",major_gc); -#ifdef HYBRID - erts_fprintf(file,"Number of global minor GCs: %lld\n",minor_global_gc); - erts_fprintf(file,"Number of global major GCs: %lld\n",major_global_gc); -#ifdef INCREMENTAL - erts_fprintf(file,"Number of minor GC-cycles: %lld\n",minor_gc_cycles); - erts_fprintf(file,"Number of major GC-cycles: %lld\n",major_gc_cycles); - erts_fprintf(file,"Number of minor GC-stages: %lld\n",minor_gc_stages); - erts_fprintf(file,"Number of major GC-stages: %lld\n",major_gc_stages); -#endif -#endif erts_fprintf(file,"Number of messages sent: %lld\n",messages_sent); erts_fprintf(file,"Number of messages copied: %lld\n",messages_copied); erts_fprintf(file,"Number of messages sent to self: %lld\n",messages_ego); diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h index eedb06a1b6..003e821bce 100644 --- a/erts/emulator/beam/benchmark.h +++ b/erts/emulator/beam/benchmark.h @@ -99,17 +99,6 @@ extern unsigned long long messages_copied; extern unsigned long long messages_ego; extern unsigned long long minor_gc; extern unsigned long long major_gc; -#ifdef HYBRID -extern unsigned long long minor_global_gc; -extern unsigned long long major_global_gc; -extern unsigned long long gc_in_copy; -#ifdef INCREMENTAL -extern unsigned long long minor_gc_cycles; -extern unsigned long long major_gc_cycles; -extern unsigned long long minor_gc_stages; -extern unsigned long long major_gc_stages; -#endif -#endif #define BM_COUNT(var) (var)++; diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 39d4582435..fc00b42454 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3517,22 +3517,6 @@ BIF_RETTYPE garbage_collect_0(BIF_ALIST_0) } /**********************************************************************/ -/* Perform garbage collection of the message area */ - -BIF_RETTYPE garbage_collect_message_area_0(BIF_ALIST_0) -{ -#if defined(HYBRID) && !defined(INCREMENTAL) - int reds = 0; - - FLAGS(BIF_P) |= F_NEED_FULLSWEEP; - reds = erts_global_garbage_collect(BIF_P, 0, NULL, 0); - BIF_RET2(am_true, reds); -#else - BIF_RET(am_false); -#endif -} - -/**********************************************************************/ /* Return a list of active ports */ BIF_RETTYPE ports_0(BIF_ALIST_0) diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 8a85e102d1..797bce43ab 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -99,8 +99,6 @@ bif erlang:garbage_collect/0 bif 'erl.system':garbage_collect/0 ebif_garbage_collect_0 bif erlang:garbage_collect/1 bif 'erl.system':garbage_collect/1 ebif_garbage_collect_1 -bif erlang:garbage_collect_message_area/0 -bif 'erl.system':garbage_collect_message_area/0 ebif_garbage_collect_message_area_0 bif erlang:get/0 bif 'erl.lang.proc.pdict':get/0 ebif_get_0 bif erlang:get/1 diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 6f5020dc14..93aa2fb8d0 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -678,7 +678,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) erts_sys_prepare_crash_dump(); - if (erts_sys_getenv("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0) + if (erts_sys_getenv_raw("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0) dumpname = "erl_crash.dump"; else dumpname = &dumpnamebuf[0]; diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index d7345c2f54..36eda04de2 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -26,30 +26,13 @@ #include "global.h" #include "erl_process.h" #include "erl_gc.h" -#include "erl_nmgc.h" #include "big.h" #include "erl_binary.h" #include "erl_bits.h" #include "dtrace-wrapper.h" -#ifdef HYBRID -MA_STACK_DECLARE(src); -MA_STACK_DECLARE(dst); -MA_STACK_DECLARE(offset); -#endif - static void move_one_frag(Eterm** hpp, Eterm* src, Uint src_sz, ErlOffHeap*); -void -init_copy(void) -{ -#ifdef HYBRID - MA_STACK_ALLOC(src); - MA_STACK_ALLOC(dst); - MA_STACK_ALLOC(offset); -#endif -} - /* * Copy object "obj" to process p. */ @@ -432,12 +415,10 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) while (i--) { *htop++ = *objp++; } -#ifndef HYBRID /* FIND ME! */ funp = (ErlFunThing *) tp; funp->next = off_heap->first; off_heap->first = (struct erl_off_heap_header*) funp; erts_refc_inc(&funp->fe->refc, 2); -#endif *argp = make_fun_rel(tp, dst_base); } break; @@ -500,420 +481,6 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) return res; } -#ifdef HYBRID - -#ifdef BM_MESSAGE_SIZES -# define BM_ADD(var,val) (var) += (val); -#else -# define BM_ADD(var,val) -#endif - -#ifdef DEBUG -# define CLEARMEM(PTR,SIZE) memset(PTR,0,SIZE*sizeof(Eterm)) -#else -# define CLEARMEM(PTR,SIZE) -#endif - -#ifdef INCREMENTAL -#define GlobalAlloc(p, need, hp) \ -do { \ - Uint n = (need); \ - BM_ADD(words_copied,n); \ - BM_SWAP_TIMER(copy,system); \ - /* If a new collection cycle is started during copy, the message * \ - * will end up in the old generation and all allocations * \ - * thereafter must go directly into the old generation. */ \ - if (alloc_old) { \ - erts_incremental_gc((p),n,&dest,1); \ - (hp) = erts_inc_alloc(n); \ - } else { \ - (hp) = IncAlloc((p),n,&dest,1); \ - if (ma_gc_flags & GC_CYCLE_START) { \ - alloc_old = 1; \ - global_htop = global_heap; \ - (hp) = erts_inc_alloc(n); \ - } \ - } \ - CLEARMEM((hp),(n)); \ - BM_SWAP_TIMER(system,copy); \ -} while(0) - -#else /* no INCREMELNTAL */ - -#define GlobalAlloc(p, need, hp) \ -do { \ - Uint n = (need); \ - total_need += n; \ - if (total_need >= global_heap_sz) \ - erl_exit(ERTS_ABORT_EXIT, "Copying a message (%d words) larger than the nursery simply won't work...\n", total_need); \ - if (global_hend - n < global_htop) { \ - BM_SWAP_TIMER(copy,system); \ - erts_global_garbage_collect((p),total_need,NULL,0); \ - BM_SWAP_TIMER(system,copy); \ - total_need = 0; \ - ma_src_top = 0; \ - ma_dst_top = 0; \ - ma_offset_top = 0; \ - goto copy_start; \ - } \ - (hp) = global_htop; \ - global_htop += n; \ - BM_ADD(words_copied,n); \ -} while(0) -#endif /* INCREMENTAL */ - -/* Copy a message to the message area. */ -Eterm copy_struct_lazy(Process *from, Eterm orig, Uint offs) -{ - Eterm obj; - Eterm dest; -#ifdef INCREMENTAL - int alloc_old = 0; -#else - int total_need = 0; -#endif - - VERBOSE(DEBUG_MESSAGES, - ("COPY START; %T is sending a message @ 0x%016x\n%T\n", - from->id, orig, orig)); - -#ifndef INCREMENTAL - copy_start: -#endif - MA_STACK_PUSH(src,orig); - MA_STACK_PUSH(dst,&dest); - MA_STACK_PUSH(offset,offs); - - while (ma_src_top > 0) { - obj = MA_STACK_POP(src); - - /* copy_struct_lazy should never be called with something that - * do not need to be copied. Within the loop, nothing that do - * not need copying should be placed in the src-stack. - */ - ASSERT(!NO_COPY(obj)); - - switch (primary_tag(obj)) { - case TAG_PRIMARY_LIST: { - Eterm *hp; - Eterm *objp; - - GlobalAlloc(from,2,hp); - objp = list_val(obj); - - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_list(hp)); - MA_STACK_POP(dst); - - /* TODO: Byt ordningen nedan så att CDR pushas först. */ - - if (NO_COPY(*objp)) { - hp[0] = *objp; -#ifdef INCREMENTAL - if (ptr_within(ptr_val(*objp),inc_fromspc,inc_fromend)) - INC_STORE(gray,hp,2); -#endif - } else { - MA_STACK_PUSH(src,*objp); - MA_STACK_PUSH(dst,hp); - MA_STACK_PUSH(offset,0); - } - - objp++; - - if (NO_COPY(*objp)) { - hp[1] = *objp; -#ifdef INCREMENTAL - if (ptr_within(ptr_val(*objp),inc_fromspc,inc_fromend)) - INC_STORE(gray,hp,2); -#endif - } - else { - MA_STACK_PUSH(src,*objp); - MA_STACK_PUSH(dst,hp); - MA_STACK_PUSH(offset,1); - } - continue; - } - - case TAG_PRIMARY_BOXED: { - Eterm *objp = boxed_val(obj); - - switch (*objp & _TAG_HEADER_MASK) { - case ARITYVAL_SUBTAG: { - Uint ari = arityval(*objp); - Uint i; - Eterm *hp; - GlobalAlloc(from,ari + 1,hp); - /* A GC above might invalidate the value of objp */ - objp = boxed_val(obj); - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_tuple(hp)); - MA_STACK_POP(dst); - *hp = *objp++; - for (i = 1; i <= ari; i++) { - switch (primary_tag(*objp)) { - case TAG_PRIMARY_LIST: - case TAG_PRIMARY_BOXED: - if (NO_COPY(*objp)) { - hp[i] = *objp; -#ifdef INCREMENTAL - if (ptr_within(ptr_val(*objp), - inc_fromspc,inc_fromend)) - INC_STORE(gray,hp,BOXED_NEED(hp,*hp)); -#endif - objp++; - } else { - MA_STACK_PUSH(src,*objp++); - MA_STACK_PUSH(dst,hp); - MA_STACK_PUSH(offset,i); - } - break; - default: - hp[i] = *objp++; - } - } - continue; - } - - case REFC_BINARY_SUBTAG: { - ProcBin *pb; - Uint i = thing_arityval(*objp) + 1; - Eterm *hp; - GlobalAlloc(from,i,hp); - /* A GC above might invalidate the value of objp */ - objp = boxed_val(obj); - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_binary(hp)); - MA_STACK_POP(dst); - pb = (ProcBin*) hp; - while (i--) { - *hp++ = *objp++; - } - erts_refc_inc(&pb->val->refc, 2); - pb->next = erts_global_offheap.first; - erts_global_offheap.first = pb; - OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm)); - continue; - } - - case FUN_SUBTAG: { - ErlFunThing *funp = (ErlFunThing*) objp; - Uint i = thing_arityval(*objp) + 1; - Uint j = i + 1 + funp->num_free; - Uint k = i; - Eterm *hp, *hp_start; - GlobalAlloc(from,j,hp); - /* A GC above might invalidate the value of objp */ - objp = boxed_val(obj); - hp_start = hp; - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_fun(hp)); - MA_STACK_POP(dst); - funp = (ErlFunThing*) hp; - while (i--) { - *hp++ = *objp++; - } -#ifndef HYBRID /* FIND ME! */ - funp->next = erts_global_offheap.first; - erts_global_offheap.first = funp; - erts_refc_inc(&funp->fe->refc, 2); -#endif - for (i = k; i < j; i++) { - switch (primary_tag(*objp)) { - case TAG_PRIMARY_LIST: - case TAG_PRIMARY_BOXED: - if (NO_COPY(*objp)) { -#ifdef INCREMENTAL - if (ptr_within(ptr_val(*objp), - inc_fromspc,inc_fromend)) - INC_STORE(gray,hp,BOXED_NEED(hp,*hp)); -#endif - *hp++ = *objp++; - } else { - MA_STACK_PUSH(src,*objp++); - MA_STACK_PUSH(dst,hp_start); - MA_STACK_PUSH(offset,i); - hp++; - } - break; - default: - *hp++ = *objp++; - } - } - continue; - } - - case EXTERNAL_PID_SUBTAG: - case EXTERNAL_PORT_SUBTAG: - case EXTERNAL_REF_SUBTAG: { - ExternalThing *etp; - Uint i = thing_arityval(*objp) + 1; - Eterm *hp; - GlobalAlloc(from,i,hp); - /* A GC above might invalidate the value of objp */ - objp = boxed_val(obj); - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_external(hp)); - MA_STACK_POP(dst); - etp = (ExternalThing*) hp; - while (i--) { - *hp++ = *objp++; - } - - etp->next = erts_global_offheap.first; - erts_global_offheap.first = etp; - erts_refc_inc(&etp->node->refc, 2); - continue; - } - - case SUB_BINARY_SUBTAG: { - ErlSubBin *sb = (ErlSubBin *) objp; - Eterm *hp; - Eterm res_binary; - Eterm real_bin = sb->orig; - Uint bit_offset = sb->bitoffs; - Uint bit_size = sb -> bitsize; - Uint sub_offset = sb->offs; - size_t size = sb->size; - Uint extra_bytes; - Uint real_size; - Uint sub_binary_heapneed; - if ((bit_size + bit_offset) > 8) { - extra_bytes = 2; - sub_binary_heapneed = ERL_SUB_BIN_SIZE; - } else if ((bit_size + bit_offset) > 0) { - extra_bytes = 1; - sub_binary_heapneed = ERL_SUB_BIN_SIZE; - } else { - extra_bytes = 0; - sub_binary_heapneed = 0; - } - - real_size = size+extra_bytes; - objp = binary_val(real_bin); - if (thing_subtag(*objp) == HEAP_BINARY_SUBTAG) { - ErlHeapBin *from_bin; - ErlHeapBin *to_bin; - Uint i = heap_bin_size(real_size); - GlobalAlloc(from,i+sub_binary_heapneed,hp); - from_bin = (ErlHeapBin *) objp; - to_bin = (ErlHeapBin *) hp; - to_bin->thing_word = header_heap_bin(real_size); - to_bin->size = real_size; - sys_memcpy(to_bin->data, ((byte *)from_bin->data) + - sub_offset, real_size); - res_binary = make_binary(to_bin); - hp += i; - } else { - ProcBin *from_bin; - ProcBin *to_bin; - - ASSERT(thing_subtag(*objp) == REFC_BINARY_SUBTAG); - from_bin = (ProcBin *) objp; - erts_refc_inc(&from_bin->val->refc, 2); - GlobalAlloc(from,PROC_BIN_SIZE+sub_binary_heapneed,hp); - to_bin = (ProcBin *) hp; - to_bin->thing_word = HEADER_PROC_BIN; - to_bin->size = real_size; - to_bin->val = from_bin->val; - to_bin->bytes = from_bin->bytes + sub_offset; - to_bin->next = erts_global_offheap.first; - erts_global_offheap.first = to_bin; - OH_OVERHEAD(&erts_global_offheap, to_bin->size / sizeof(Eterm)); - res_binary=make_binary(to_bin); - hp += PROC_BIN_SIZE; - } - if (extra_bytes != 0) { - ErlSubBin* res; - res = (ErlSubBin *) hp; - res->thing_word = HEADER_SUB_BIN; - res->size = size; - res->bitsize = bit_size; - res->bitoffs = bit_offset; - res->offs = 0; - res->is_writable = 0; - res->orig = res_binary; - res_binary = make_binary(hp); - } - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),res_binary); - MA_STACK_POP(dst); - continue; - } - - case BIN_MATCHSTATE_SUBTAG: - erl_exit(ERTS_ABORT_EXIT, - "copy_struct_lazy: matchstate term not allowed"); - - default: { - Uint size = thing_arityval(*objp) + 1; - Eterm *hp; - GlobalAlloc(from,size,hp); - /* A GC above might invalidate the value of objp */ - objp = boxed_val(obj); - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_boxed(hp)); - MA_STACK_POP(dst); - while (size--) { - *hp++ = *objp++; - } - continue; - } - } - continue; - } - - case TAG_PRIMARY_HEADER: - ASSERT((obj & _TAG_HEADER_MASK) == ARITYVAL_SUBTAG); - { - Eterm *objp = &obj; - Uint ari = arityval(obj); - Uint i; - Eterm *hp; - GlobalAlloc(from,ari + 1,hp); - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_tuple(hp)); - MA_STACK_POP(dst); - *hp = *objp++; - for (i = 1; i <= ari; i++) { - switch (primary_tag(*objp)) { - case TAG_PRIMARY_LIST: - case TAG_PRIMARY_BOXED: - if (NO_COPY(*objp)) { -#ifdef INCREMENTAL - if (ptr_within(ptr_val(*objp),inc_fromspc,inc_fromend)) - INC_STORE(gray,hp,ari + 1); -#endif - hp[i] = *objp++; - } else { - MA_STACK_PUSH(src,*objp++); - MA_STACK_PUSH(dst,hp); - MA_STACK_PUSH(offset,i); - } - break; - default: - hp[i] = *objp++; - } - } - continue; - } - - default: - erl_exit(ERTS_ABORT_EXIT, - "%s, line %d: Internal error in copy_struct_lazy: 0x%08x\n", - __FILE__, __LINE__,obj); - } - } - - VERBOSE(DEBUG_MESSAGES, - ("Copy allocated @ 0x%08lx:\n%T\n", - (unsigned long)ptr_val(dest),dest)); - - ma_gc_flags &= ~GC_CYCLE_START; - - ASSERT(eq(orig, dest)); - ASSERT(ma_src_top == 0); - ASSERT(ma_dst_top == 0); - ASSERT(ma_offset_top == 0); - return dest; -} - -#undef NO_COPY -#endif /* HYBRID */ - /* * Copy a term that is guaranteed to be contained in a single * heap block. The heap block is copied word by word, and any diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 7c75c9fdb7..025258e8de 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -968,16 +968,16 @@ erts_dsig_send_group_leader(ErtsDSigData *dsdp, Eterm leader, Eterm remote) #define VALGRIND_PRINTF_XML VALGRIND_PRINTF #endif -# define PURIFY_MSG(msg) \ - do { \ - char buf__[1]; size_t bufsz__ = sizeof(buf__); \ - if (erts_sys_getenv("VALGRIND_LOG_XML", buf__, &bufsz__) >= 0) { \ - VALGRIND_PRINTF_XML("<erlang_error_log>" \ - "%s, line %d: %s</erlang_error_log>\n", \ - __FILE__, __LINE__, msg); \ - } else { \ - VALGRIND_PRINTF("%s, line %d: %s", __FILE__, __LINE__, msg); \ - } \ +# define PURIFY_MSG(msg) \ + do { \ + char buf__[1]; size_t bufsz__ = sizeof(buf__); \ + if (erts_sys_getenv_raw("VALGRIND_LOG_XML", buf__, &bufsz__) >= 0) { \ + VALGRIND_PRINTF_XML("<erlang_error_log>" \ + "%s, line %d: %s</erlang_error_log>\n", \ + __FILE__, __LINE__, msg); \ + } else { \ + VALGRIND_PRINTF("%s, line %d: %s", __FILE__, __LINE__, msg); \ + } \ } while (0) #else # define PURIFY_MSG(msg) @@ -1582,11 +1582,9 @@ int erts_net_message(Port *prt, } erts_cleanup_offheap(&off_heap); -#ifndef HYBRID /* FIND ME! */ if (ctl != ctl_default) { erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl); } -#endif UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE); ERTS_SMP_CHK_NO_PROC_LOCKS; return 0; @@ -1599,11 +1597,9 @@ int erts_net_message(Port *prt, data_error: PURIFY_MSG("data error"); erts_cleanup_offheap(&off_heap); -#ifndef HYBRID /* FIND ME! */ if (ctl != ctl_default) { erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl); } -#endif UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE); erts_do_exit_port(prt, dep->cid, am_killed); ERTS_SMP_CHK_NO_PROC_LOCKS; diff --git a/erts/emulator/beam/dtrace-wrapper.h b/erts/emulator/beam/dtrace-wrapper.h index 1aeb7f9221..6ec0c91e21 100644 --- a/erts/emulator/beam/dtrace-wrapper.h +++ b/erts/emulator/beam/dtrace-wrapper.h @@ -42,6 +42,8 @@ #define DTRACE_CHARBUF(name, size) \ char name##_BUFFER[size], *name = name##_BUFFER +#define DTRACE_CHARBUF_NAME(name) name##_BUFFER + #if defined(USE_DYNAMIC_TRACE) && defined(USE_VM_PROBES) #include "erlang_dtrace.h" diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 8130d5c576..6fce032f9d 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -2138,9 +2138,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) tmp = alcu_size(ERTS_ALC_A_EHEAP, NULL, 0); } tmp += erts_max_processes*sizeof(Process*); -#ifdef HYBRID - tmp += erts_max_processes*sizeof(Process*); -#endif tmp += erts_bif_timer_memory_size(); tmp += erts_tot_link_lh_size(); diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index bba6b83ac6..4aa8fa82fb 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -291,7 +291,6 @@ type PORT_LOCK STANDARD SYSTEM port_lock type DRIVER_LOCK STANDARD SYSTEM driver_lock type XPORTS_LIST SHORT_LIVED SYSTEM extra_port_list type PROC_LCK_WTR LONG_LIVED SYSTEM proc_lock_waiter -type PROC_LCK_QS LONG_LIVED SYSTEM proc_lock_queues type RUNQ_BLNS LONG_LIVED SYSTEM run_queue_balancing type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data @@ -317,19 +316,6 @@ type ACTIVE_PROCS STANDARD PROCESSES active_procs +endif -+if hybrid - -type ACTIVE_PROCS STANDARD PROCESSES active_procs - -# Used for all memory involved in incremental gc of the message area -# that is, young (x2) and old generation, forwarding pointers and blackmap -type MESSAGE_AREA LONG_LIVED PROCESSES message_area - -# Used in MA_STACK (global.h) and INC_STORAGE (erl_nmgc.h) -type OBJECT_STACK STANDARD PROCESSES object_stack - -+endif - +if smp type SL_PTIMER SHORT_LIVED SYSTEM ptimer_sl type LL_PTIMER STANDARD SYSTEM ptimer_ll diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index e0d525bdde..97ba306a79 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -829,46 +829,83 @@ init_dd_queue(ErtsAllctrDDQueue_t *ddq) ddq->head.used_marker = 1; } -static ERTS_INLINE erts_aint_t -ddq_managed_thread_enqueue(ErtsAllctrDDQueue_t *ddq, void *ptr) +static ERTS_INLINE int +ddq_managed_thread_enqueue(ErtsAllctrDDQueue_t *ddq, void *ptr, int cinit) { - erts_aint_t ilast, itmp; - ErtsAllctrDDBlock_t *this = ptr; + erts_aint_t itmp; + ErtsAllctrDDBlock_t *enq, *this = ptr; erts_atomic_init_nob(&this->atmc_next, ERTS_AINT_NULL); - /* Enqueue at end of list... */ - ilast = erts_atomic_read_nob(&ddq->tail.data.last); - while (1) { - ErtsAllctrDDBlock_t *last = (ErtsAllctrDDBlock_t *) ilast; - itmp = erts_atomic_cmpxchg_mb(&last->atmc_next, - (erts_aint_t) this, - ERTS_AINT_NULL); - if (itmp == ERTS_AINT_NULL) - break; - ilast = itmp; + enq = (ErtsAllctrDDBlock_t *) erts_atomic_read_nob(&ddq->tail.data.last); + itmp = erts_atomic_cmpxchg_relb(&enq->atmc_next, + (erts_aint_t) this, + ERTS_AINT_NULL); + if (itmp == ERTS_AINT_NULL) { + /* We are required to move last pointer */ +#ifdef DEBUG + ASSERT(ERTS_AINT_NULL == erts_atomic_read_nob(&this->atmc_next)); + ASSERT(((erts_aint_t) enq) + == erts_atomic_xchg_relb(&ddq->tail.data.last, + (erts_aint_t) this)); +#else + erts_atomic_set_relb(&ddq->tail.data.last, (erts_aint_t) this); +#endif + return 1; } + else { + /* + * We *need* to insert element somewhere in between the + * last element we read earlier and the actual last element. + */ + int i = cinit; - /* Move last pointer forward... */ - while (1) { - if (erts_atomic_read_rb(&this->atmc_next) != ERTS_AINT_NULL) { - /* Someone else will move it forward */ - return erts_atomic_read_rb(&ddq->tail.data.last); + while (1) { + erts_aint_t itmp2; + erts_atomic_set_nob(&this->atmc_next, itmp); + itmp2 = erts_atomic_cmpxchg_relb(&enq->atmc_next, + (erts_aint_t) this, + itmp); + if (itmp == itmp2) + return 0; /* inserted this */ + if ((i & 1) == 0) + itmp = itmp2; + else { + enq = (ErtsAllctrDDBlock_t *) itmp2; + itmp = erts_atomic_read_acqb(&enq->atmc_next); + ASSERT(itmp != ERTS_AINT_NULL); + } + i++; } - itmp = erts_atomic_cmpxchg_mb(&ddq->tail.data.last, - (erts_aint_t) this, - ilast); - if (ilast == itmp) - return (erts_aint_t) this; - ilast = itmp; } } +static ERTS_INLINE erts_aint_t +check_insert_marker(ErtsAllctrDDQueue_t *ddq, erts_aint_t ilast) +{ + if (!ddq->head.used_marker + && ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) { + erts_aint_t itmp; + ErtsAllctrDDBlock_t *last = (ErtsAllctrDDBlock_t *) ilast; + + erts_atomic_init_nob(&ddq->tail.data.marker.atmc_next, ERTS_AINT_NULL); + itmp = erts_atomic_cmpxchg_relb(&last->atmc_next, + (erts_aint_t) &ddq->tail.data.marker, + ERTS_AINT_NULL); + if (itmp == ERTS_AINT_NULL) { + ilast = (erts_aint_t) &ddq->tail.data.marker; + ddq->head.used_marker = !0; + erts_atomic_set_relb(&ddq->tail.data.last, ilast); + } + } + return ilast; +} + static ERTS_INLINE int -ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr) +ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr, int cinit) { - erts_aint_t ilast; + int last_elem; int um_refc_ix = 0; int managed_thread = erts_thr_progress_is_managed_thread(); if (!managed_thread) { @@ -884,11 +921,11 @@ ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr) } } - ilast = ddq_managed_thread_enqueue(ddq, ptr); + last_elem = ddq_managed_thread_enqueue(ddq, ptr, cinit); if (!managed_thread) erts_atomic_dec_relb(&ddq->tail.data.um_refc[um_refc_ix]); - return ilast == (erts_aint_t) ptr; + return last_elem; } static ERTS_INLINE void * @@ -934,22 +971,18 @@ ddq_check_incoming(ErtsAllctrDDQueue_t *ddq) int um_refc_ix; ddq->head.next.thr_progress_reached = 1; um_refc_ix = ddq->head.next.um_refc_ix; - if (erts_atomic_read_acqb(&ddq->tail.data.um_refc[um_refc_ix]) == 0) { + if (erts_atomic_read_nob(&ddq->tail.data.um_refc[um_refc_ix]) == 0) { /* Move unreferenced end pointer forward... */ + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); + ddq->head.unref_end = ddq->head.next.unref_end; - if (!ddq->head.used_marker - && ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) { - ddq->head.used_marker = 1; - ilast = ddq_managed_thread_enqueue(ddq, &ddq->tail.data.marker); - } + ilast = check_insert_marker(ddq, ilast); - if (ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) - ERTS_THR_MEMORY_BARRIER; - else { + if (ddq->head.unref_end != (ErtsAllctrDDBlock_t *) ilast) { ddq->head.next.unref_end = (ErtsAllctrDDBlock_t *) ilast; - ddq->head.next.thr_progress = erts_thr_progress_later(); + ddq->head.next.thr_progress = erts_thr_progress_later(NULL); erts_atomic32_set_relb(&ddq->tail.data.um_refc_ix, um_refc_ix); ddq->head.next.um_refc_ix = um_refc_ix == 0 ? 1 : 0; @@ -1092,12 +1125,15 @@ handle_delayed_dealloc(Allctr_t *allctr, } static ERTS_INLINE void -enqueue_dealloc_other_instance(ErtsAlcType_t type, Allctr_t *allctr, void *ptr) +enqueue_dealloc_other_instance(ErtsAlcType_t type, + Allctr_t *allctr, + void *ptr, + int cinit) { if (allctr->fix) ((UWord *) ptr)[ERTS_ALCU_DD_FIX_TYPE_OFFS] = (UWord) type; - if (ddq_enqueue(type, &allctr->dd.q, ptr)) + if (ddq_enqueue(type, &allctr->dd.q, ptr, cinit)) erts_alloc_notify_delayed_dealloc(allctr->ix); } @@ -3613,7 +3649,11 @@ erts_alcu_free_thr_pref(ErtsAlcType_t type, void *extra, void *p) get_pref_allctr(extra, &pref_allctr); ptr = get_used_allctr(extra, p, &used_allctr, NULL); if (pref_allctr != used_allctr) - enqueue_dealloc_other_instance(type, used_allctr, ptr); + enqueue_dealloc_other_instance(type, + used_allctr, + ptr, + (used_allctr->dd.ix + - pref_allctr->dd.ix)); else { if (used_allctr->thread_safe) erts_mtx_lock(&used_allctr->mutex); @@ -3988,7 +4028,11 @@ realloc_thr_pref(ErtsAlcType_t type, void *extra, void *p, Uint size, sys_memcpy(res, p, cpy_size); if (!force_move || used_allctr != pref_allctr) - enqueue_dealloc_other_instance(type, used_allctr, ptr); + enqueue_dealloc_other_instance(type, + used_allctr, + ptr, + (used_allctr->dd.ix + - pref_allctr->dd.ix)); else { do_erts_alcu_free(type, used_allctr, ptr); ASSERT(pref_allctr == used_allctr); @@ -4179,6 +4223,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) allctr->dd.use = 1; init_dd_queue(&allctr->dd.q); + allctr->dd.ix = init->ix; } else #endif diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index f321ed21aa..f308039baf 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -253,7 +253,9 @@ erts_get_async_ready_queue(Uint sched_id) static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q) { +#ifdef USE_VM_PROBES int len; +#endif if (is_internal_port(a->port)) { #if ERTS_USE_ASYNC_READY_Q @@ -291,7 +293,9 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, int saved_fin_deq = 0; ErtsThrQFinDeQ_t fin_deq; #endif +#ifdef USE_VM_PROBES int len; +#endif while (1) { ErtsAsync *a = (ErtsAsync *) erts_thr_q_dequeue(q); @@ -390,6 +394,8 @@ static ERTS_INLINE void call_async_ready(ErtsAsync *a) } erts_port_release(p); } + if (a->pdl) + driver_pdl_dec_refc(a->pdl); if (a->hndl) erts_ddll_dereference_driver(a->hndl); } @@ -399,9 +405,6 @@ static ERTS_INLINE void async_reply(ErtsAsync *a, ErtsThrQPrepEnQ_t *prep_enq) #if ERTS_USE_ASYNC_READY_Q ErtsAsyncReadyQ *arq; - if (a->pdl) - driver_pdl_dec_refc(a->pdl); - #if ERTS_ASYNC_PRINT_JOB erts_fprintf(stderr, "=>> %ld\n", a->async_id); #endif @@ -421,8 +424,6 @@ static ERTS_INLINE void async_reply(ErtsAsync *a, ErtsThrQPrepEnQ_t *prep_enq) #else /* ERTS_USE_ASYNC_READY_Q */ call_async_ready(a); - if (a->pdl) - driver_pdl_dec_refc(a->pdl); erts_free(ERTS_ALC_T_ASYNC, (void *) a); #endif /* ERTS_USE_ASYNC_READY_Q */ diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index f889ccdb93..45dc5fb11c 100644..100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -25,7 +25,6 @@ #include "erl_vm.h" #include "global.h" #include "erl_process.h" -#include "erl_nmgc.h" #include "error.h" #include "erl_driver.h" #include "bif.h" @@ -89,12 +88,6 @@ static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE #ifdef ERTS_ENABLE_KERNEL_POLL " [kernel-poll:%s]" #endif -#ifdef HYBRID - " [hybrid heap]" -#endif -#ifdef INCREMENTAL - " [incremental GC]" -#endif #ifdef ET_DEBUG #if ET_DEBUG " [type-assertions]" @@ -576,9 +569,6 @@ static Eterm pi_args[] = { am_min_bin_vheap_size, am_current_location, am_current_stacktrace, -#ifdef HYBRID - am_message_binary -#endif }; #define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(Eterm))) @@ -626,9 +616,6 @@ pi_arg2ix(Eterm arg) case am_min_bin_vheap_size: return 28; case am_current_location: return 29; case am_current_stacktrace: return 30; -#ifdef HYBRID - case am_message_binary: return 31; -#endif default: return -1; } } @@ -1081,12 +1068,8 @@ process_info_aux(Process *BIF_P, if (rp != BIF_P) { Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp); if (is_value(msg)) { - mq[i].copy_struct_size = (is_immed(msg) -#ifdef HYBRID - || NO_COPY(msg) -#endif - ? 0 - : size_object(msg)); + mq[i].copy_struct_size = (is_immed(msg)? 0 : + size_object(msg)); } else if (mq[i].msgp->data.attached) { mq[i].copy_struct_size @@ -1526,16 +1509,6 @@ process_info_aux(Process *BIF_P, break; } -#ifdef HYBRID - case am_message_binary: { - Uint sz = 3; - (void) bld_bin_list(NULL, &sz, erts_global_offheap.mso); - hp = HAlloc(BIF_P, sz); - res = bld_bin_list(&hp, NULL, erts_global_offheap.mso); - break; - } -#endif - case am_sequential_trace_token: res = copy_object(rp->seq_trace_token, BIF_P); hp = HAlloc(BIF_P, 3); @@ -1724,7 +1697,7 @@ static int check_if_xml(void) { char buf[1]; size_t bufsz = sizeof(buf); - return erts_sys_getenv("VALGRIND_LOG_XML", buf, &bufsz) >= 0; + return erts_sys_getenv_raw("VALGRIND_LOG_XML", buf, &bufsz) >= 0; } #else #define check_if_xml() 0 @@ -2354,36 +2327,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) #endif } else if (BIF_ARG_1 == am_heap_sizes) { return erts_heap_sizes(BIF_P); - } else if (BIF_ARG_1 == am_global_heaps_size) { -#ifdef HYBRID - Uint hsz = 0; - Uint sz = 0; - - sz += global_heap_sz; -#ifdef INCREMENTAL - /* The size of the old generation is a bit hard to define here... - * The amount of live data in the last collection perhaps..? */ - sz = 0; -#else - if (global_old_hend && global_old_heap) - sz += global_old_hend - global_old_heap; -#endif - - sz *= sizeof(Eterm); - - (void) erts_bld_uint(NULL, &hsz, sz); - hp = hsz ? HAlloc(BIF_P, hsz) : NULL; - res = erts_bld_uint(&hp, NULL, sz); -#else - res = make_small(0); -#endif - return res; } else if (BIF_ARG_1 == am_heap_type) { -#if defined(HYBRID) - return am_hybrid; -#else return am_private; -#endif } else if (ERTS_IS_ATOM_STR("cpu_topology", BIF_ARG_1)) { res = erts_get_cpu_topology_term(BIF_P, am_used); BIF_TRAP1(erts_format_cpu_topology_trap, BIF_P, res); @@ -2765,7 +2710,8 @@ port_info_1(BIF_ALIST_1) am_id, am_connected, am_input, - am_output + am_output, + am_os_pid }; Eterm items[ASIZE(keys)]; Eterm result = NIL; @@ -2822,6 +2768,7 @@ port_info_1(BIF_ALIST_1) ** name String ** input Number of bytes input from port program ** output Number of bytes output to the port program +** os_pid The child's process ID */ BIF_RETTYPE port_info_2(BIF_ALIST_2) @@ -2922,6 +2869,18 @@ static BIF_RETTYPE port_info(Process* p, Eterm portid, Eterm item) hp = HAlloc(p, hsz); res = erts_bld_uint(&hp, NULL, n); } + else if (item == am_os_pid) { + if (prt->os_pid >= 0) { + Uint hsz = 3; + UWord n = prt->os_pid; + (void) erts_bld_uword(NULL, &hsz, n); + hp = HAlloc(p, hsz); + res = erts_bld_uword(&hp, NULL, n); + } else { + hp = HAlloc(p, 3); + res = am_undefined; + } + } else if (item == am_registered_name) { RegProc *reg; reg = prt->reg; @@ -4110,48 +4069,52 @@ BIF_RETTYPE erts_debug_lock_counters_1(BIF_ALIST_1) Eterm* tp = tuple_val(BIF_ARG_1); switch (arityval(tp[0])) { - case 2: + case 2: { + int opt = 0; + int val = 0; if (ERTS_IS_ATOM_STR("copy_save", tp[1])) { - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - if (tp[2] == am_true) { - - res = erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_COPYSAVE) ? am_true : am_false; - - } else if (tp[2] == am_false) { - - res = erts_lcnt_clear_rt_opt(ERTS_LCNT_OPT_COPYSAVE) ? am_true : am_false; - - } else { - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_ERROR(BIF_P, BADARG); - } - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_RET(res); - + opt = ERTS_LCNT_OPT_COPYSAVE; } else if (ERTS_IS_ATOM_STR("process_locks", tp[1])) { - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - if (tp[2] == am_true) { - - res = erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_PROCLOCK) ? am_true : am_false; - - } else if (tp[2] == am_false) { - - res = erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_PROCLOCK) ? am_true : am_false; + opt = ERTS_LCNT_OPT_PROCLOCK; + } else if (ERTS_IS_ATOM_STR("port_locks", tp[1])) { + opt = ERTS_LCNT_OPT_PORTLOCK; + } else if (ERTS_IS_ATOM_STR("suspend", tp[1])) { + opt = ERTS_LCNT_OPT_SUSPEND; + } else if (ERTS_IS_ATOM_STR("location", tp[1])) { + opt = ERTS_LCNT_OPT_LOCATION; + } else { + BIF_ERROR(BIF_P, BADARG); + } + if (tp[2] == am_true) { + val = 1; + } else if (tp[2] == am_false) { + val = 0; + } else { + BIF_ERROR(BIF_P, BADARG); + } - } else { - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_ERROR(BIF_P, BADARG); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + + if (val) { + res = erts_lcnt_set_rt_opt(opt) ? am_true : am_false; + } else { + res = erts_lcnt_clear_rt_opt(opt) ? am_true : am_false; + } +#ifdef ERTS_SMP + if (res != tp[2]) { + if (opt == ERTS_LCNT_OPT_PORTLOCK) { + erts_lcnt_enable_io_lock_count(val); + } else if (opt == ERTS_LCNT_OPT_PROCLOCK) { + erts_lcnt_enable_proc_lock_count(val); } - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_RET(res); - } - break; + } +#endif + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_RET(res); + break; + } default: break; diff --git a/erts/emulator/beam/erl_bif_os.c b/erts/emulator/beam/erl_bif_os.c index 58d48199fa..d076640a8b 100644 --- a/erts/emulator/beam/erl_bif_os.c +++ b/erts/emulator/beam/erl_bif_os.c @@ -71,15 +71,13 @@ BIF_RETTYPE os_getenv_0(BIF_ALIST_0) Eterm* hp; Eterm ret; Eterm str; - int len; init_getenv_state(&state); ret = NIL; while ((cp = getenv_string(&state)) != NULL) { - len = strlen(cp); - hp = HAlloc(BIF_P, len*2+2); - str = buf_to_intlist(&hp, cp, len, NIL); + str = erts_convert_native_to_filename(BIF_P,(byte *)cp); + hp = HAlloc(BIF_P, 2); ret = CONS(hp, str, ret); } @@ -88,32 +86,30 @@ BIF_RETTYPE os_getenv_0(BIF_ALIST_0) return ret; } - +#define STATIC_BUF_SIZE 1024 BIF_RETTYPE os_getenv_1(BIF_ALIST_1) { Process* p = BIF_P; - Eterm key = BIF_ARG_1; Eterm str; - int len, res; + Sint len; + int res; char *key_str, *val; - char buf[1024]; + char buf[STATIC_BUF_SIZE]; size_t val_size = sizeof(buf); - len = is_string(key); - if (!len) { + key_str = erts_convert_filename_to_native(BIF_ARG_1,buf,STATIC_BUF_SIZE, + ERTS_ALC_T_TMP,1,0,&len); + + if (!key_str) { BIF_ERROR(p, BADARG); } - /* Leave at least one byte in buf for value */ - key_str = len < sizeof(buf)-2 ? &buf[0] : erts_alloc(ERTS_ALC_T_TMP, len+1); - if (intlist_to_buf(key, key_str, len) != len) - erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__); - key_str[len] = '\0'; if (key_str != &buf[0]) val = &buf[0]; else { - val_size -= len + 1; - val = &buf[len + 1]; + /* len includes zero byte */ + val_size -= len; + val = &buf[len]; } res = erts_sys_getenv(key_str, val, &val_size); @@ -121,7 +117,6 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1) no_var: str = am_false; } else { - Eterm* hp; if (res > 0) { val = erts_alloc(ERTS_ALC_T_TMP, val_size); while (1) { @@ -134,9 +129,7 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1) val = erts_realloc(ERTS_ALC_T_TMP, val, val_size); } } - if (val_size) - hp = HAlloc(p, val_size*2); - str = buf_to_intlist(&hp, val, val_size, NIL); + str = erts_convert_native_to_filename(p,(byte *)val); } if (key_str != &buf[0]) erts_free(ERTS_ALC_T_TMP, key_str); @@ -147,46 +140,43 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1) BIF_RETTYPE os_putenv_2(BIF_ALIST_2) { - Process* p = BIF_P; - Eterm key = BIF_ARG_1; - Eterm value = BIF_ARG_2; - char def_buf[1024]; - char *buf = NULL; - int sep_ix, i, key_len, value_len, tot_len; - key_len = is_string(key); - if (!key_len) { - error: - if (buf) - erts_free(ERTS_ALC_T_TMP, (void *) buf); - BIF_ERROR(p, BADARG); + char def_buf_key[STATIC_BUF_SIZE]; + char def_buf_value[STATIC_BUF_SIZE]; + char *key_buf, *value_buf; + + key_buf = erts_convert_filename_to_native(BIF_ARG_1,def_buf_key, + STATIC_BUF_SIZE, + ERTS_ALC_T_TMP,0,0,NULL); + if (!key_buf) { + BIF_ERROR(BIF_P, BADARG); } - if (is_nil(value)) - value_len = 0; - else { - value_len = is_string(value); - if (!value_len) - goto error; + value_buf = erts_convert_filename_to_native(BIF_ARG_2,def_buf_value, + STATIC_BUF_SIZE, + ERTS_ALC_T_TMP,1,0, + NULL); + if (!value_buf) { + if (key_buf != def_buf_key) { + erts_free(ERTS_ALC_T_TMP, key_buf); + } + BIF_ERROR(BIF_P, BADARG); + } + + + if (erts_sys_putenv(key_buf, value_buf)) { + if (key_buf != def_buf_key) { + erts_free(ERTS_ALC_T_TMP, key_buf); + } + if (value_buf != def_buf_value) { + erts_free(ERTS_ALC_T_TMP, value_buf); + } + BIF_ERROR(BIF_P, BADARG); + } + if (key_buf != def_buf_key) { + erts_free(ERTS_ALC_T_TMP, key_buf); } - tot_len = key_len + 1 + value_len + 1; - if (tot_len <= sizeof(def_buf)) - buf = &def_buf[0]; - else - buf = erts_alloc(ERTS_ALC_T_TMP, tot_len); - i = intlist_to_buf(key, buf, key_len); - if (i != key_len) - erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__); - sep_ix = i; - buf[i++] = '='; - if (is_not_nil(value)) - i += intlist_to_buf(value, &buf[i], value_len); - if (i != key_len + 1 + value_len) - erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__); - buf[i] = '\0'; - if (erts_sys_putenv(buf, sep_ix)) { - goto error; + if (value_buf != def_buf_value) { + erts_free(ERTS_ALC_T_TMP, value_buf); } - if (buf != &def_buf[0]) - erts_free(ERTS_ALC_T_TMP, (void *) buf); BIF_RET(am_true); } diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 5525426824..f9009166c0 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -265,7 +265,7 @@ port_call(Process* c_p, Eterm arg1, Eterm arg2, Eterm arg3) Eterm res; Sint result_size; Eterm *hp; - Eterm *hp_end; /* To satisfy hybrid heap architecture */ + Eterm *hp_end; unsigned ret_flags = 0U; int fpe_was_unmasked; @@ -718,7 +718,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump) } else if (option == am_arg0) { char *a0; - if ((a0 = erts_convert_filename_to_native(*tp, ERTS_ALC_T_TMP, 1)) == NULL) { + if ((a0 = erts_convert_filename_to_native(*tp, NULL, 0, ERTS_ALC_T_TMP, 1, 1, NULL)) == NULL) { goto badarg; } if (opts.argv == NULL) { @@ -845,7 +845,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump) goto badarg; } name = tp[1]; - if ((name_buf = erts_convert_filename_to_native(name,ERTS_ALC_T_TMP,0)) == NULL) { + if ((name_buf = erts_convert_filename_to_native(name, NULL, 0, ERTS_ALC_T_TMP,0,1, NULL)) == NULL) { goto badarg; } opts.spawn_type = ERTS_SPAWN_EXECUTABLE; @@ -909,7 +909,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump) } opts.wd = (char *) dir; } else { - if ((opts.wd = erts_convert_filename_to_native(edir,ERTS_ALC_T_TMP,0)) == NULL) { + if ((opts.wd = erts_convert_filename_to_native(edir, NULL, 0, ERTS_ALC_T_TMP,0,1,NULL)) == NULL) { goto badarg; } } @@ -1000,7 +1000,7 @@ static char **convert_args(Eterm l) pp[i++] = erts_default_arg0; while (is_list(l)) { str = CAR(list_val(l)); - if ((b = erts_convert_filename_to_native(str,ERTS_ALC_T_TMP,1)) == NULL) { + if ((b = erts_convert_filename_to_native(str,NULL,0,ERTS_ALC_T_TMP,1,1,NULL)) == NULL) { int j; for (j = 1; j < i; ++j) erts_free(ERTS_ALC_T_TMP, pp[j]); @@ -1035,8 +1035,9 @@ static byte* convert_environment(Process* p, Eterm env) Eterm* hp; Uint heap_size; int n; - Uint size; + Sint size; byte* bytes; + int encoding = erts_get_native_filename_encoding(); if ((n = list_length(env)) < 0) { return NULL; @@ -1079,7 +1080,8 @@ static byte* convert_environment(Process* p, Eterm env) if (is_not_nil(env)) { goto done; } - if (erts_iolist_size(all, &size)) { + + if ((size = erts_native_filename_need(all,encoding)) < 0) { goto done; } @@ -1087,7 +1089,7 @@ static byte* convert_environment(Process* p, Eterm env) * Put the result in a binary (no risk for a memory leak that way). */ (void) erts_new_heap_binary(p, NULL, size, &bytes); - io_list_to_buf(all, (char*)bytes, size); + erts_native_filename_put(all,encoding,bytes); done: erts_free(ERTS_ALC_T_TMP, temp_heap); diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 6f7309f493..b7c82935ff 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -1005,8 +1005,13 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags) if (is_float(arg)) { FloatDef *fdp = (FloatDef*)(float_val(arg) + 1); +#ifdef DOUBLE_MIDDLE_ENDIAN + a = fdp->fw[1]; + b = fdp->fw[0]; +#else a = fdp->fw[0]; b = fdp->fw[1]; +#endif } else if (is_small(arg)) { u.f64 = (double) signed_val(arg); a = u.i32[0]; @@ -1015,8 +1020,13 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags) if (big_to_double(arg, &u.f64) < 0) { return 0; } +#ifdef DOUBLE_MIDDLE_ENDIAN + a = u.i32[1]; + b = u.i32[0]; +#else a = u.i32[0]; b = u.i32[1]; +#endif } else { return 0; } diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index c726be5fb4..2fea4671e1 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -105,7 +105,20 @@ #define NSEG_2 256 /* Size of second segment table */ #define NSEG_INC 128 /* Number of segments to grow after that */ -#define SEGTAB(tb) ((struct segment**)erts_smp_atomic_read_ddrb(&(tb)->segtab)) +#ifdef ERTS_SMP +# define DB_USING_FINE_LOCKING(TB) (((TB))->common.type & DB_FINE_LOCKED) +#else +# define DB_USING_FINE_LOCKING(TB) 0 +#endif + +#ifdef ETHR_ORDERED_READ_DEPEND +#define SEGTAB(tb) ((struct segment**) erts_smp_atomic_read_nob(&(tb)->segtab)) +#else +#define SEGTAB(tb) \ + (DB_USING_FINE_LOCKING(tb) \ + ? ((struct segment**) erts_smp_atomic_read_ddrb(&(tb)->segtab)) \ + : ((struct segment**) erts_smp_atomic_read_nob(&(tb)->segtab))) +#endif #define NACTIVE(tb) ((int)erts_smp_atomic_read_nob(&(tb)->nactive)) #define NITEMS(tb) ((int)erts_smp_atomic_read_nob(&(tb)->common.nitems)) @@ -122,7 +135,9 @@ */ static ERTS_INLINE Uint hash_to_ix(DbTableHash* tb, HashValue hval) { - Uint mask = erts_smp_atomic_read_acqb(&tb->szm); + Uint mask = (DB_USING_FINE_LOCKING(tb) + ? erts_smp_atomic_read_acqb(&tb->szm) + : erts_smp_atomic_read_nob(&tb->szm)); Uint ix = hval & mask; if (ix >= erts_smp_atomic_read_nob(&tb->nactive)) { ix &= mask>>1; @@ -319,7 +334,10 @@ struct ext_segment { static ERTS_INLINE void SET_SEGTAB(DbTableHash* tb, struct segment** segtab) { - erts_smp_atomic_set_wb(&tb->segtab, (erts_aint_t) segtab); + if (DB_USING_FINE_LOCKING(tb)) + erts_smp_atomic_set_wb(&tb->segtab, (erts_aint_t) segtab); + else + erts_smp_atomic_set_nob(&tb->segtab, (erts_aint_t) segtab); #ifdef VALGRIND tb->top_ptr_to_segment_with_active_segtab = EXTSEG(segtab); #endif @@ -2501,6 +2519,28 @@ static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2, return list; } +static ERTS_INLINE int +begin_resizing(DbTableHash* tb) +{ + if (DB_USING_FINE_LOCKING(tb)) + return !erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1); + else { + if (erts_smp_atomic_read_nob(&tb->is_resizing)) + return 0; + erts_smp_atomic_set_nob(&tb->is_resizing, 1); + return 1; + } +} + +static ERTS_INLINE void +done_resizing(DbTableHash* tb) +{ + if (DB_USING_FINE_LOCKING(tb)) + erts_smp_atomic_set_relb(&tb->is_resizing, 0); + else + erts_smp_atomic_set_nob(&tb->is_resizing, 0); +} + /* Grow table with one new bucket. ** Allocate new segment if needed. */ @@ -2513,9 +2553,8 @@ static void grow(DbTableHash* tb, int nactive) int from_ix; int szm; - if (erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1)) { + if (!begin_resizing(tb)) return; /* already in progress */ - } if (NACTIVE(tb) != nactive) { goto abort; /* already done (race) */ } @@ -2547,9 +2586,12 @@ static void grow(DbTableHash* tb, int nactive) } erts_smp_atomic_inc_nob(&tb->nactive); if (from_ix == 0) { - erts_smp_atomic_set_relb(&tb->szm, szm); + if (DB_USING_FINE_LOCKING(tb)) + erts_smp_atomic_set_relb(&tb->szm, szm); + else + erts_smp_atomic_set_nob(&tb->szm, szm); } - erts_smp_atomic_set_relb(&tb->is_resizing, 0); + done_resizing(tb); /* Finally, let's split the bucket. We try to do it in a smart way to keep link order and avoid unnecessary updates of next-pointers */ @@ -2581,7 +2623,7 @@ static void grow(DbTableHash* tb, int nactive) return; abort: - erts_smp_atomic_set_relb(&tb->is_resizing, 0); + done_resizing(tb); } @@ -2590,9 +2632,8 @@ abort: */ static void shrink(DbTableHash* tb, int nactive) { - if (erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1)) { + if (!begin_resizing(tb)) return; /* already in progress */ - } if (NACTIVE(tb) == nactive) { erts_smp_rwmtx_t* lck; int src_ix = nactive - 1; @@ -2639,7 +2680,7 @@ static void shrink(DbTableHash* tb, int nactive) } /*else already done */ - erts_smp_atomic_set_relb(&tb->is_resizing, 0); + done_resizing(tb); } diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index c2f6cfa933..42907e2e84 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1233,7 +1233,7 @@ static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp, Eterm ret; ret = db_prog_match(p, mpsp, args, NULL, NULL, num_args, - ERTS_PAM_CONTIGUOUS_TUPLE | ERTS_PAM_COPY_RESULT, + ERTS_PAM_COPY_RESULT, return_flags); #if defined(HARDDEBUG) if (is_non_value(ret)) { @@ -2995,7 +2995,7 @@ Eterm db_copy_from_comp(DbTableCommon* tb, DbTerm* bp, Eterm** hpp, } ASSERT((*hpp - hp) <= bp->size); #ifdef DEBUG_CLONE - ASSERT(eq_rel(make_tuple(hp),make_tuple(bp->debug_clone),bp->debug_clone)); + ASSERT(eq_rel(make_tuple(hp),NULL,make_tuple(bp->debug_clone),bp->debug_clone)); #endif return make_tuple(hp); } @@ -3018,7 +3018,7 @@ Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p, hp += extra; HRelease(p, endp, hp); #ifdef DEBUG_CLONE - ASSERT(eq_rel(copy, obj->debug_clone[pos], obj->debug_clone)); + ASSERT(eq_rel(copy, NULL, obj->debug_clone[pos], obj->debug_clone)); #endif return copy; } diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index d7d6fcf0a2..2121f72fd2 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -25,7 +25,6 @@ #include "erl_vm.h" #include "global.h" #include "erl_process.h" -#include "erl_nmgc.h" #include "big.h" #include "bif.h" #include "beam_catches.h" @@ -33,34 +32,9 @@ #define WITHIN(ptr, x, y) ((x) <= (ptr) && (ptr) < (y)) -#if defined(HYBRID) -#if defined(INCREMENTAL) -/* Hybrid + Incremental */ -#define IN_HEAP(p, ptr) \ - (WITHIN((ptr), p->heap, p->hend) || \ - (OLD_HEAP(p) && WITHIN((ptr), OLD_HEAP(p), OLD_HEND(p))) || \ - WITHIN((ptr), global_heap, global_hend) || \ - (inc_fromspc && WITHIN((ptr), inc_fromspc, inc_fromend)) || \ - WITHIN((ptr), global_old_heap, global_old_hend)) - -#define IN_MA(ptr) \ - (WITHIN((ptr), global_heap, global_hend) || \ - (inc_fromspc && WITHIN((ptr), inc_fromspc, inc_fromend)) || \ - WITHIN((ptr), global_old_heap, global_old_hend)) -#else -/* Hybrid */ -#define IN_HEAP(p, ptr) \ - (WITHIN((ptr), p->heap, p->hend) || \ - (OLD_HEAP(p) && WITHIN((ptr), OLD_HEAP(p), OLD_HEND(p))) || \ - WITHIN((ptr), global_heap, global_hend) || \ - (global_old_heap && WITHIN((ptr),global_old_heap,global_old_hend))) -#endif -#else -/* Private */ #define IN_HEAP(p, ptr) \ (WITHIN((ptr), p->heap, p->hend) || \ (OLD_HEAP(p) && WITHIN((ptr), OLD_HEAP(p), OLD_HEND(p)))) -#endif #ifdef __GNUC__ @@ -266,13 +240,6 @@ static int verify_eterm(Process *p,Eterm element) } } } -#ifdef INCREMENTAL - else { - if (IN_MA(ptr)) - return 1; - } -#endif - return 0; } @@ -447,51 +414,12 @@ void verify_process(Process *p) VERIFY_ETERM("fvalue",p->fvalue); VERIFY_ETERM("ftrace",p->ftrace); -#ifdef HYBRID - VERIFY_AREA("rrma",p->rrma,p->nrr); -#endif - VERBOSE(DEBUG_MEMORY,("...done\n")); #undef VERIFY_AREA #undef VERIFY_ETERM } -void verify_everything() -{ -#ifdef HYBRID - Uint i; - Uint n = erts_num_active_procs; - -#ifdef INCREMENTAL_FREE_SIZES_NEEDS_TO_BE_TAGGED_AS_HEADERS_WITH_ARITY - INC_Page *page = inc_used_mem; -#endif - - for (i = 0; i < n; i++) { - verify_process(erts_active_procs[i]); - } - - erts_check_memory(NULL,global_heap,global_htop); - -#ifdef INCREMENTAL_FREE_SIZES_NEEDS_TO_BE_TAGGED_AS_HEADERS_WITH_ARITY - while (page) - { - Eterm *end = page + INC_PAGE_SIZE; - Eterm *pos = page->start; - - while( pos < end) { - Eterm val = *pos++; - if(is_header(val)) - pos += thing_arityval(val); - else - verify_eterm(NULL,val); - } - page = page->next; - } -#endif -#endif /* HYBRID */ -} - /* * print_untagged_memory will print the contents of given memory area. */ @@ -582,83 +510,6 @@ void print_tagged_memory(Eterm *pos, Eterm *end) erts_printf("+-%s-+-%s-+\n",dashes,dashes); } -#ifdef HYBRID -void print_ma_info(void) -{ - erts_printf("Message Area (start - top - end): " - "0x%0*lx - 0x%0*lx - 0x%0*lx\n", - PTR_SIZE, (unsigned long)global_heap, - PTR_SIZE, (unsigned long)global_htop, - PTR_SIZE, (unsigned long)global_hend); -#ifndef INCREMENTAL - erts_printf(" High water: 0x%0*lx " - "Old gen: 0x%0*lx - 0x%0*lx - 0x%0*lx\n", - PTR_SIZE, (unsigned long)global_high_water, - PTR_SIZE, (unsigned long)global_old_heap, - PTR_SIZE, (unsigned long)global_old_htop, - PTR_SIZE, (unsigned long)global_old_hend); -#endif -} - -void print_message_area(void) -{ - Eterm *pos = global_heap; - Eterm *end = global_htop; - - erts_printf("From: 0x%0*lx to 0x%0*lx\n", - PTR_SIZE,(unsigned long)pos,PTR_SIZE,(unsigned long)end); - erts_printf("(Old generation: 0x%0*lx to 0x%0*lx\n", - PTR_SIZE, (unsigned long)global_old_heap, - PTR_SIZE, (unsigned long)global_old_hend); - erts_printf("| %-*s | %-*s |\n",PTR_SIZE,"Address",PTR_SIZE,"Contents"); - erts_printf("|-%s-|-%s-|\n",dashes,dashes); - while( pos < end ) { - Eterm val = pos[0]; - erts_printf("| 0x%0*lx | 0x%0*lx | ", - PTR_SIZE,(unsigned long)pos,PTR_SIZE,(unsigned long)val); - ++pos; - if( is_arity_value(val) ) { - erts_printf("Arity(%lu)", arityval(val)); - } else if( is_thing(val) ) { - unsigned int ari = thing_arityval(val); - erts_printf("Thing Arity(%u) Tag(%lu)", ari, thing_subtag(val)); - while( ari ) { - erts_printf("\n| 0x%0*lx | 0x%0*lx | THING", - PTR_SIZE, (unsigned long)pos, - PTR_SIZE, (unsigned long)*pos); - ++pos; - --ari; - } - } else - erts_printf("%.30T", val); - erts_printf("\n"); - } - erts_printf("+-%s-+-%s-+\n",dashes,dashes); -} - -void check_message_area() -{ - Eterm *pos = global_heap; - Eterm *end = global_htop; - - while( pos < end ) { - Eterm val = *pos++; - if(is_header(val)) - pos += thing_arityval(val); - else if(!is_immed(val)) - if ((ptr_val(val) < global_heap || ptr_val(val) >= global_htop) && - (ptr_val(val) < global_old_heap || - ptr_val(val) >= global_old_hend)) - { - erts_printf("check_message_area: Stray pointer found\n"); - print_message_area(); - erts_printf("Crashing to make it look real...\n"); - pos = 0; - } - } -} -#endif /* HYBRID */ - static void print_process_memory(Process *p); static void print_process_memory(Process *p) { @@ -703,19 +554,6 @@ static void print_process_memory(Process *p) erts_printf(" Fvalue: 0x%0*lx\n",PTR_SIZE,p->fvalue); erts_printf(" Ftrace: 0x%0*lx\n",PTR_SIZE,p->ftrace); -#ifdef HYBRID - if (p->nrr > 0) { - int i; - erts_printf(" Remembered Roots:\n"); - for (i = 0; i < p->nrr; i++) - if (p->rrsrc[i] != NULL) - erts_printf("0x%0*lx -> 0x%0*lx\n", - PTR_SIZE, (unsigned long)p->rrsrc[i], - PTR_SIZE, (unsigned long)p->rrma[i]); - erts_printf("\n"); - } -#endif - erts_printf("+- %-*s -+ 0x%0*lx 0x%0*lx %s-%s-+\n", PTR_SIZE, "Stack", PTR_SIZE, (unsigned long)STACK_TOP(p), @@ -757,92 +595,6 @@ void print_memory(Process *p) if (p != NULL) { print_process_memory(p); } -#ifdef HYBRID - else { - Uint i; - Uint n = erts_num_active_procs; - - for (i = 0; i < n; i++) { - Process *p = erts_active_procs[i]; - print_process_memory(p); - } - - erts_printf("==================\n"); - erts_printf("|| Message area ||\n"); - erts_printf("==================\n"); - erts_printf("+-%s-+-%s-%s-%s-%s-+\n", - dashes,dashes,dashes,dashes,dashes); - erts_printf("| %-*s | 0x%0*lx - 0x%0*lx - 0x%0*lx%*s|\n", - PTR_SIZE, "Young", - PTR_SIZE, (unsigned long)global_heap, - PTR_SIZE, (unsigned long)global_htop, - PTR_SIZE, (unsigned long)global_hend, - PTR_SIZE, ""); - erts_printf("+-%s-+-%s-%s-%s-%s-+\n", - dashes,dashes,dashes,dashes,dashes); - - print_untagged_memory(global_heap,global_htop); - - - erts_printf("+-%s-+-%s-%s-%s-%s-+\n", - dashes,dashes,dashes,dashes,dashes); - erts_printf("| %-*s | 0x%0*lx - 0x%0*lx %*s |\n", - PTR_SIZE, "Old", - PTR_SIZE, (unsigned long)global_old_heap, - PTR_SIZE, (unsigned long)global_old_hend, - 2 * PTR_SIZE, ""); - erts_printf("+-%s-+-%s-%s-%s-%s-+\n", - dashes,dashes,dashes,dashes,dashes); - -#ifdef INCREMENTAL - { - INC_Page *page = inc_used_mem; - /* Genom att g� igenom fri-listan f�rst kan vi markera de - omr�den som inte �r allokerade och bara skriva ut de som - lever. - char markarea[INC_PAGESIZE]; - */ - - while (page) { - Eterm *ptr = (Eterm*)page->start; - Eterm *end = (Eterm*)page->start + INC_PAGESIZE; - - erts_printf("| %*s | This: 0x%0*lx Next: 0x%0*lx %*s|\n", - PTR_SIZE, "", - PTR_SIZE, (unsigned long)page, - PTR_SIZE, (unsigned long)page->next, - 2 * PTR_SIZE - 8, ""); - print_untagged_memory(ptr,end); - page = page->next; - } - } - - { - INC_MemBlock *this = inc_free_list; - - erts_printf("-- %-*s --%s-%s-%s-%s-\n",PTR_SIZE+2,"Free list", - dashes,dashes,dashes,dashes); - while (this) { - erts_printf("Block @ 0x%0*lx sz: %8d prev: 0x%0*lx next: 0x%0*lx\n", - PTR_SIZE, (unsigned long)this,this->size, - PTR_SIZE, (unsigned long)this->prev, - PTR_SIZE, (unsigned long)this->next); - this = this->next; - } - erts_printf("--%s---%s-%s-%s-%s--\n", - dashes,dashes,dashes,dashes,dashes); - } - - if (inc_fromspc != NULL) { - erts_printf("-- fromspace - 0x%0*lx 0x%0*lx " - "------------------------------\n", - PTR_SIZE, (unsigned long)inc_fromspc, - PTR_SIZE, (unsigned long)inc_fromend); - print_untagged_memory(inc_fromspc,inc_fromend); - } -#endif /* INCREMENTAL */ - } -#endif /* HYBRID */ } void print_memory_info(Process *p) @@ -869,26 +621,6 @@ void print_memory_info(Process *p) erts_printf("|| Memory info ||\n"); erts_printf("=================\n"); } -#ifdef HYBRID - erts_printf("|- message area --%s-%s-%s-%s-|\n", - dashes,dashes,dashes,dashes); - erts_printf("| Young | 0x%0*lx - 0x%0*lx - 0x%0*lx %*s |\n", - PTR_SIZE, (unsigned long)global_heap, - PTR_SIZE, (unsigned long)global_htop, - PTR_SIZE, (unsigned long)global_hend, - PTR_SIZE, ""); - erts_printf("| Old | 0x%0*lx - 0x%0*lx %*s |\n", - PTR_SIZE, (unsigned long)global_old_heap, - PTR_SIZE, (unsigned long)global_old_hend, - 2 * PTR_SIZE, ""); -#endif -#ifdef INCREMENTAL - if (inc_fromspc != NULL) - erts_printf("| Frmsp | 0x%0*lx - 0x%0*lx %*s |\n", - PTR_SIZE, (unsigned long)inc_fromspc, - PTR_SIZE, (unsigned long)inc_fromend, - 2 * PTR_SIZE, ""); -#endif erts_printf("+-----------------%s-%s-%s-%s-+\n",dashes,dashes,dashes,dashes); } #if !HEAP_ON_C_STACK && defined(DEBUG) diff --git a/erts/emulator/beam/erl_debug.h b/erts/emulator/beam/erl_debug.h index c49354a2b3..a028a95fef 100644 --- a/erts/emulator/beam/erl_debug.h +++ b/erts/emulator/beam/erl_debug.h @@ -42,12 +42,11 @@ #define DEBUG_DEFAULT 0x0000 /* No flags are set per default */ #define DEBUG_SYSTEM 0x0001 /* Misc system info at startup and end */ #define DEBUG_PRIVATE_GC 0x0002 /* GC of private heaps */ -#define DEBUG_HYBRID_GC 0x0004 /* GC of the message area */ -#define DEBUG_ALLOCATION 0x0008 /* HAlloc. To find holes in the heap */ -#define DEBUG_MESSAGES 0x0010 /* Message passing */ -#define DEBUG_THREADS 0x0020 /* Thread-related stuff */ -#define DEBUG_PROCESSES 0x0040 /* Process creation and removal */ -#define DEBUG_MEMORY 0x0080 /* Display results of memory checks */ +#define DEBUG_ALLOCATION 0x0004 /* HAlloc. To find holes in the heap */ +#define DEBUG_MESSAGES 0x0008 /* Message passing */ +#define DEBUG_THREADS 0x0010 /* Thread-related stuff */ +#define DEBUG_PROCESSES 0x0020 /* Process creation and removal */ +#define DEBUG_MEMORY 0x0040 /* Display results of memory checks */ extern Uint32 verbose; @@ -88,7 +87,6 @@ extern void erts_check_stack(Process *p); extern void erts_check_heap(Process *p); extern void erts_check_memory(Process *p, Eterm *start, Eterm *end); extern void verify_process(Process *p); -extern void verify_everything(void); extern void print_tagged_memory(Eterm *start, Eterm *end); extern void print_untagged_memory(Eterm *start, Eterm *end); extern void print_memory(Process *p); @@ -99,10 +97,4 @@ extern void erts_debug_use_tmp_heap(int, Process *); extern void erts_debug_unuse_tmp_heap(int, Process *); #endif -#ifdef HYBRID -extern void print_ma_info(void); -extern void print_message_area(void); -extern void check_message_area(void); -#endif - #endif /* _ERL_DEBUG_H_ */ diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h index 2f165afa06..54cfd6aa83 100644 --- a/erts/emulator/beam/erl_fun.h +++ b/erts/emulator/beam/erl_fun.h @@ -54,9 +54,7 @@ typedef struct erl_fun_entry { typedef struct erl_fun_thing { Eterm thing_word; /* Subtag FUN_SUBTAG. */ ErlFunEntry* fe; /* Pointer to fun entry. */ -#ifndef HYBRID /* FIND ME! */ struct erl_off_heap_header* next; -#endif #ifdef HIPE UWord* native_address; /* Native code for the fun. */ #endif @@ -83,9 +81,7 @@ ErlFunEntry* erts_get_fun_entry2(Eterm mod, int old_uniq, int old_index, byte* uniq, int index, int arity); void erts_erase_fun_entry(ErlFunEntry* fe); -#ifndef HYBRID /* FIND ME! */ void erts_cleanup_funs(ErlFunThing* funp); -#endif void erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end); void erts_dump_fun_entries(int, void *); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index ca4385dd3a..1eb3dba240 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -34,7 +34,6 @@ #include "erl_binary.h" #include "dist.h" #include "erl_mseg.h" -#include "erl_nmgc.h" #include "erl_threads.h" #include "erl_bif_timer.h" #include "erl_instrument.h" @@ -154,28 +153,6 @@ Export *erts_delay_trap = NULL; int erts_use_r9_pids_ports; -#ifdef HYBRID -Eterm *global_heap; -Eterm *global_hend; -Eterm *global_htop; -Eterm *global_saved_htop; -Eterm *global_old_heap; -Eterm *global_old_hend; -ErlOffHeap erts_global_offheap; -Uint global_heap_sz = SH_DEFAULT_SIZE; - -#ifndef INCREMENTAL -Eterm *global_high_water; -Eterm *global_old_htop; -#endif - -Uint16 global_gen_gcs; -Uint16 global_max_gen_gcs; -Uint global_gc_flags; - -Uint global_heap_min_sz = SH_DEFAULT_SIZE; -#endif - int ignore_break; int replace_intr; @@ -281,7 +258,6 @@ erl_init(int ncpu) erl_drv_thr_init(); erts_init_async(); init_io(); - init_copy(); init_load(); erts_init_bif(); erts_init_bif_chksum(); @@ -302,45 +278,6 @@ erl_init(int ncpu) } static void -init_shared_memory(int argc, char **argv) -{ -#ifdef HYBRID - int arg_size = 0; - - global_heap_sz = erts_next_heap_size(global_heap_sz,0); - - /* Make sure arguments will fit on the heap, no one else will check! */ - while (argc--) - arg_size += 2 + strlen(argv[argc]); - if (global_heap_sz < arg_size) - global_heap_sz = erts_next_heap_size(arg_size,1); - -#ifndef INCREMENTAL - global_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, - sizeof(Eterm) * global_heap_sz); - global_hend = global_heap + global_heap_sz; - global_htop = global_heap; - global_high_water = global_heap; - global_old_hend = global_old_htop = global_old_heap = NULL; -#endif - - global_gen_gcs = 0; - global_max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs); - global_gc_flags = erts_default_process_flags; - - erts_global_offheap.mso = NULL; -#ifndef HYBRID /* FIND ME! */ - erts_global_offheap.funs = NULL; -#endif - erts_global_offheap.overhead = 0; -#endif - -#ifdef INCREMENTAL - erts_init_incgc(); -#endif -} - -static void erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** argv) { int i; @@ -511,10 +448,14 @@ void erts_usage(void) erts_fprintf(stderr, "-rg amount set reader groups limit\n"); erts_fprintf(stderr, "-sbt type set scheduler bind type, valid types are:\n"); erts_fprintf(stderr, " u|ns|ts|ps|s|nnts|nnps|tnnps|db\n"); + erts_fprintf(stderr, "-sbwt val set scheduler busy wait threshold, valid values are:\n"); + erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n"); erts_fprintf(stderr, "-scl bool enable/disable compaction of scheduler load,\n"); erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); erts_fprintf(stderr, "-sct cput set cpu topology,\n"); erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); + erts_fprintf(stderr, "-sws val set scheduler wakeup strategy, valid values are:\n"); + erts_fprintf(stderr, " default|legacy|proposal.\n"); erts_fprintf(stderr, "-swt val set scheduler wakeup threshold, valid values are:\n"); erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n"); erts_fprintf(stderr, "-sss size suggested stack size in kilo words for scheduler threads,\n"); @@ -682,7 +623,7 @@ early_init(int *argc, char **argv) /* envbufsz = sizeof(envbuf); - /* erts_sys_getenv() not initialized yet; need erts_sys_getenv__() */ + /* erts_sys_getenv(_raw)() not initialized yet; need erts_sys_getenv__() */ if (erts_sys_getenv__("ERL_THREAD_POOL_SIZE", envbuf, &envbufsz) == 0) erts_async_max_threads = atoi(envbuf); else @@ -790,6 +731,10 @@ early_init(int *argc, char **argv) /* } } +#ifndef USE_THREADS + erts_async_max_threads = 0; +#endif + #ifdef ERTS_SMP no_schedulers = schdlrs; no_schedulers_online = schdlrs_onln; @@ -901,13 +846,13 @@ erl_start(int argc, char **argv) int ncpu = early_init(&argc, argv); envbufsz = sizeof(envbuf); - if (erts_sys_getenv(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0) + if (erts_sys_getenv_raw(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0) user_requested_db_max_tabs = atoi(envbuf); else user_requested_db_max_tabs = 0; envbufsz = sizeof(envbuf); - if (erts_sys_getenv("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) { + if (erts_sys_getenv_raw("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) { Uint16 max_gen_gcs = atoi(envbuf); erts_smp_atomic32_set_nob(&erts_max_gen_gcs, (erts_aint32_t) max_gen_gcs); @@ -991,7 +936,6 @@ erl_start(int argc, char **argv) switch (*ch) { case 's': verbose |= DEBUG_SYSTEM; break; case 'g': verbose |= DEBUG_PRIVATE_GC; break; - case 'h': verbose |= DEBUG_HYBRID_GC; break; case 'M': verbose |= DEBUG_MEMORY; break; case 'a': verbose |= DEBUG_ALLOCATION; break; case 't': verbose |= DEBUG_THREADS; break; @@ -1004,7 +948,6 @@ erl_start(int argc, char **argv) erts_printf("Verbose level: "); if (verbose & DEBUG_SYSTEM) erts_printf("SYSTEM "); if (verbose & DEBUG_PRIVATE_GC) erts_printf("PRIVATE_GC "); - if (verbose & DEBUG_HYBRID_GC) erts_printf("HYBRID_GC "); if (verbose & DEBUG_MEMORY) erts_printf("PARANOID_MEMORY "); if (verbose & DEBUG_ALLOCATION) erts_printf("ALLOCATION "); if (verbose & DEBUG_THREADS) erts_printf("THREADS "); @@ -1032,12 +975,6 @@ erl_start(int argc, char **argv) #ifdef HIPE strcat(tmp, ",HIPE"); #endif -#ifdef INCREMENTAL - strcat(tmp, ",INCREMENTAL_GC"); -#endif -#ifdef HYBRID - strcat(tmp, ",HYBRID"); -#endif erts_fprintf(stderr, "Erlang "); if (tmp[1]) { erts_fprintf(stderr, "(%s) ", tmp+1); @@ -1198,6 +1135,16 @@ erl_start(int argc, char **argv) erts_usage(); } } + else if (has_prefix("bwt", sub_param)) { + arg = get_arg(sub_param+3, argv[i+1], &i); + if (erts_sched_set_busy_wait_threshold(arg) != 0) { + erts_fprintf(stderr, "bad scheduler busy wait threshold: %s\n", + arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("scheduler wakup threshold: %s\n", arg)); + } else if (has_prefix("cl", sub_param)) { arg = get_arg(sub_param+2, argv[i+1], &i); if (sys_strcmp("true", arg) == 0) @@ -1258,13 +1205,23 @@ erl_start(int argc, char **argv) erts_use_sender_punish = 0; else if (sys_strcmp("wt", sub_param) == 0) { arg = get_arg(sub_param+2, argv[i+1], &i); - if (erts_sched_set_wakeup_limit(arg) != 0) { + if (erts_sched_set_wakeup_other_thresold(arg) != 0) { erts_fprintf(stderr, "scheduler wakeup threshold: %s\n", arg); erts_usage(); } VERBOSE(DEBUG_SYSTEM, - ("scheduler wakup threshold: %s\n", arg)); + ("scheduler wakeup threshold: %s\n", arg)); + } + else if (sys_strcmp("ws", sub_param) == 0) { + arg = get_arg(sub_param+2, argv[i+1], &i); + if (erts_sched_set_wakeup_other_type(arg) != 0) { + erts_fprintf(stderr, "scheduler wakeup strategy: %s\n", + arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("scheduler wakeup threshold: %s\n", arg)); } else if (has_prefix("ss", sub_param)) { /* suggested stack size (Kilo Words) for scheduler threads */ @@ -1469,7 +1426,6 @@ erl_start(int argc, char **argv) erl_init(ncpu); - init_shared_memory(boot_argc, boot_argv); load_preloaded(); erts_initialized = 1; @@ -1556,32 +1512,6 @@ system_cleanup(int flush_async) #endif #endif -#ifdef HYBRID - if (ma_src_stack) erts_free(ERTS_ALC_T_OBJECT_STACK, - (void *)ma_src_stack); - if (ma_dst_stack) erts_free(ERTS_ALC_T_OBJECT_STACK, - (void *)ma_dst_stack); - if (ma_offset_stack) erts_free(ERTS_ALC_T_OBJECT_STACK, - (void *)ma_offset_stack); - ma_src_stack = NULL; - ma_dst_stack = NULL; - ma_offset_stack = NULL; - erts_cleanup_offheap(&erts_global_offheap); -#endif - -#if defined(HYBRID) && !defined(INCREMENTAL) - if (global_heap) { - ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, - (void*) global_heap, - sizeof(Eterm) * global_heap_sz); - } - global_heap = NULL; -#endif - -#ifdef INCREMENTAL - erts_cleanup_incgc(); -#endif - erts_exit_flush_async(); } diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index a0f744be9d..b545ec07c0 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -175,9 +175,6 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "sched_stat", NULL }, #endif { "async_init_mtx", NULL }, -#ifdef ERTS_SMP - { "proc_lck_qs_alloc", NULL }, -#endif #ifdef __WIN32__ #ifdef DEBUG { "save_ops_lock", NULL }, diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index a36c53560e..741c0cb08e 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -49,7 +49,7 @@ const char *str_undefined = "undefined"; static ethr_tsd_key lcnt_thr_data_key; static int lcnt_n_thr; -static erts_lcnt_thread_data_t *lcnt_thread_data[1024]; +static erts_lcnt_thread_data_t *lcnt_thread_data[4096]; /* local functions */ @@ -240,7 +240,7 @@ void erts_lcnt_init() { lcnt_lock(); - erts_lcnt_rt_options = ERTS_LCNT_OPT_PROCLOCK; + erts_lcnt_rt_options = ERTS_LCNT_OPT_PROCLOCK | ERTS_LCNT_OPT_LOCATION; eltd = lcnt_thread_data_alloc(); @@ -312,7 +312,7 @@ void erts_lcnt_list_insert(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) } void erts_lcnt_list_delete(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) { - + if (lock->next) lock->next->prev = lock->prev; if (lock->prev) lock->prev->next = lock->next; if (list->head == lock) list->head = lock->next; @@ -334,6 +334,10 @@ void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag ) { } void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id) { int i; + if (!name) { + lock->flag = 0; + return; + } lcnt_lock(); lock->next = NULL; @@ -363,6 +367,8 @@ void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eter void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) { erts_lcnt_lock_t *deleted_lock; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; + lcnt_lock(); if (erts_lcnt_rt_options & ERTS_LCNT_OPT_COPYSAVE) { @@ -378,6 +384,7 @@ void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) { } /* delete original */ erts_lcnt_list_delete(erts_lcnt_data->current_locks, lock); + lock->flag = 0; lcnt_unlock(); } @@ -389,6 +396,7 @@ void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option) { erts_lcnt_thread_data_t *eltd; if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; eltd = lcnt_get_thread_data(); @@ -422,6 +430,7 @@ void erts_lcnt_lock(erts_lcnt_lock_t *lock) { erts_lcnt_thread_data_t *eltd; if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; w_state = ethr_atomic_read(&lock->w_state); ethr_atomic_inc( &lock->w_state); @@ -452,6 +461,7 @@ void erts_lcnt_lock_unaquire(erts_lcnt_lock_t *lock) { /* should check if this thread was "waiting" */ if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; ethr_atomic_dec( &lock->w_state); } @@ -475,6 +485,7 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; #ifdef DEBUG if (!(lock->flag & (ERTS_LCNT_LT_RWMUTEX | ERTS_LCNT_LT_RWSPINLOCK))) { @@ -489,9 +500,13 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line ASSERT(eltd); /* if lock was in conflict, time it */ - - stats = lcnt_get_lock_stats(lock, file, line); + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_LOCATION) { + stats = lcnt_get_lock_stats(lock, file, line); + } else { + stats = &lock->stats[0]; + } + if (eltd->timer_set) { lcnt_time(&timer); @@ -510,6 +525,7 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line void erts_lcnt_unlock_opt(erts_lcnt_lock_t *lock, Uint16 option) { if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_dec(&lock->w_state); if (option & ERTS_LCNT_LO_READ ) ethr_atomic_dec(&lock->r_state); } @@ -520,6 +536,7 @@ void erts_lcnt_unlock(erts_lcnt_lock_t *lock) { erts_aint_t flowstate; #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; #ifdef DEBUG /* flowstate */ flowstate = ethr_atomic_read(&lock->flowstate); @@ -537,6 +554,7 @@ void erts_lcnt_unlock(erts_lcnt_lock_t *lock) { void erts_lcnt_trylock_opt(erts_lcnt_lock_t *lock, int res, Uint16 option) { if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; /* Determine lock_state via res instead of state */ if (res != EBUSY) { if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_inc(&lock->w_state); @@ -555,6 +573,7 @@ void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res) { erts_aint_t flowstate; #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; if (res != EBUSY) { #ifdef DEBUG diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h index 6306580ae4..690551c71f 100644 --- a/erts/emulator/beam/erl_lock_count.h +++ b/erts/emulator/beam/erl_lock_count.h @@ -89,6 +89,7 @@ #define ERTS_LCNT_OPT_LOCATION (((Uint16) 1) << 1) #define ERTS_LCNT_OPT_PROCLOCK (((Uint16) 1) << 2) #define ERTS_LCNT_OPT_COPYSAVE (((Uint16) 1) << 3) +#define ERTS_LCNT_OPT_PORTLOCK (((Uint16) 1) << 4) typedef struct { unsigned long s; @@ -201,5 +202,7 @@ void erts_lcnt_clear_counters(void); char *erts_lcnt_lock_type(Uint16 type); erts_lcnt_data_t *erts_lcnt_get_data(void); +#define ERTS_LCNT_LOCK_TYPE(lockp) ((lockp)->flag & ERTS_LCNT_LT_ALL) + #endif /* ifdef ERTS_ENABLE_LOCK_COUNT */ #endif /* ifndef ERTS_LOCK_COUNT_H__ */ diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index bd86e3ea9e..919567ab27 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -29,7 +29,6 @@ #include "global.h" #include "erl_message.h" #include "erl_process.h" -#include "erl_nmgc.h" #include "erl_binary.h" #include "dtrace-wrapper.h" @@ -303,8 +302,6 @@ notify_new_message(Process *receiver) ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(receiver)); - ACTIVATE(receiver); - switch (receiver->status) { case P_GARBING: switch (receiver->gcstatus) { @@ -905,8 +902,8 @@ erts_send_message(Process* sender, #ifdef USE_VM_PROBES *sender_name = *receiver_name = '\0'; if (DTRACE_ENABLED(message_send)) { - erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id); - erts_snprintf(receiver_name, sizeof(receiver_name), "%T", receiver->id); + erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), "%T", sender->id); + erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), "%T", receiver->id); } #endif if (SEQ_TRACE_TOKEN(sender) != NIL && !(flags & ERTS_SND_FLG_NO_SEQ_TRACE)) { @@ -987,56 +984,6 @@ erts_send_message(Process* sender, #endif ); BM_SWAP_TIMER(send,system); -#ifdef HYBRID - } else { - ErlMessage* mp = message_alloc(); - BM_SWAP_TIMER(send,copy); -#ifdef INCREMENTAL - /* TODO: During GC activate processes if the message relies in - * the fromspace and the sender is active. During major - * collections add the message to the gray stack if it relies - * in the old generation and the sender is active and the - * receiver is inactive. - - if (!IS_CONST(message) && (ma_gc_flags & GC_CYCLE) && - (ptr_val(message) >= inc_fromspc && - ptr_val(message) < inc_fromend) && INC_IS_ACTIVE(sender)) - INC_ACTIVATE(receiver); - else if (!IS_CONST(message) && (ma_gc_flags & GC_CYCLE) && - (ptr_val(message) >= global_old_heap && - ptr_val(message) < global_old_hend) && - INC_IS_ACTIVE(sender) && !INC_IS_ACTIVE(receiver)) - Mark message in blackmap and add it to the gray stack - */ - - if (!IS_CONST(message)) - INC_ACTIVATE(receiver); -#endif - LAZY_COPY(sender,message); - BM_SWAP_TIMER(copy,send); - DTRACE6(message_send, sender_name, receiver_name, - size_object(message)msize, tok_label, tok_lastcnt, tok_serial); - ERL_MESSAGE_TERM(mp) = message; - ERL_MESSAGE_TOKEN(mp) = NIL; -#ifdef USE_VM_PROBES - ERL_MESSAGE_DT_UTAG(mp) = NIL; -#endif - mp->next = NULL; - LINK_MESSAGE(receiver, mp); - ACTIVATE(receiver); - - if (receiver->status == P_WAITING) { - erts_add_to_runq(receiver); - } else if (receiver->status == P_SUSPENDED) { - receiver->rstatus = P_RUNABLE; - } - if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) { - trace_receive(receiver, message); - } - - BM_SWAP_TIMER(send,system); - return; -#else } else if (sender == receiver) { /* Drop message if receiver has a pending exit ... */ #ifdef ERTS_SMP @@ -1145,7 +1092,6 @@ erts_send_message(Process* sender, BM_SWAP_TIMER(send,system); #endif /* #ifndef ERTS_SMP */ return; -#endif /* HYBRID */ } } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index b323bc7f69..4109c20fa7 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -33,6 +33,10 @@ #include "big.h" #include "beam_bp.h" #include "erl_thr_progress.h" +#include "dtrace-wrapper.h" +#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP)) +#define HAVE_USE_DTRACE 1 +#endif #include <limits.h> #include <stddef.h> /* offsetof */ @@ -1841,3 +1845,1079 @@ static unsigned calc_checksum(unsigned char* ptr, unsigned size) #endif /* READONLY_CHECK */ +#ifdef HAVE_USE_DTRACE + +#define MESSAGE_BUFSIZ 1024 + +static void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term, + char **ptr, char *buf, int bufsiz) +{ + ErlNifBinary str_bin; + + if (!enif_inspect_iolist_as_binary(env, term, &str_bin) || + str_bin.size > bufsiz) { + *ptr = NULL; + } else { + memcpy(buf, (char *) str_bin.data, str_bin.size); + buf[str_bin.size] = '\0'; + *ptr = buf; + } +} + +ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc, + const ERL_NIF_TERM argv[]) +{ + ErlNifBinary message_bin; + DTRACE_CHARBUF(messagebuf, MESSAGE_BUFSIZ + 1); + + if (DTRACE_ENABLED(user_trace_s1)) { + if (!enif_inspect_iolist_as_binary(env, argv[0], &message_bin) || + message_bin.size > MESSAGE_BUFSIZ) { + return am_badarg; + } + memcpy(messagebuf, (char *) message_bin.data, message_bin.size); + messagebuf[message_bin.size] = '\0'; + DTRACE1(user_trace_s1, messagebuf); + return am_true; + } else { + return am_false; + } +} + +ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc, + const ERL_NIF_TERM argv[]) +{ + DTRACE_CHARBUF(procbuf, 32 + 1); + DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1); + char *utbuf = NULL; + ErlNifSInt64 i1, i2, i3, i4; + DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1); + DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1); + DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1); + DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1); + char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL; + + if (DTRACE_ENABLED(user_trace_i4s4)) { + dtrace_nifenv_str(env, procbuf); + get_string_maybe(env, argv[0], &utbuf, user_tagbuf, MESSAGE_BUFSIZ); + if (! enif_get_int64(env, argv[1], &i1)) + i1 = 0; + if (! enif_get_int64(env, argv[2], &i2)) + i2 = 0; + if (! enif_get_int64(env, argv[3], &i3)) + i3 = 0; + if (! enif_get_int64(env, argv[4], &i4)) + i4 = 0; + get_string_maybe(env, argv[5], &mbuf1, messagebuf1, MESSAGE_BUFSIZ); + get_string_maybe(env, argv[6], &mbuf2, messagebuf2, MESSAGE_BUFSIZ); + get_string_maybe(env, argv[7], &mbuf3, messagebuf3, MESSAGE_BUFSIZ); + get_string_maybe(env, argv[8], &mbuf4, messagebuf4, MESSAGE_BUFSIZ); + DTRACE10(user_trace_i4s4, procbuf, utbuf, + i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4); + return am_true; + } else { + return am_false; + } +} + +#define DTRACE10_LABEL(name, label, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ + erlang_##name##label((a0), (a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9)) +#define N_STATEMENT(the_label) \ + case the_label: \ + if (DTRACE_ENABLED(user_trace_n##the_label)) { \ + dtrace_nifenv_str(env, procbuf); \ + get_string_maybe(env, argv[1], &utbuf, user_tagbuf, MESSAGE_BUFSIZ); \ + if (! enif_get_int64(env, argv[2], &i1)) \ + i1 = 0; \ + if (! enif_get_int64(env, argv[3], &i2)) \ + i2 = 0; \ + if (! enif_get_int64(env, argv[4], &i3)) \ + i3 = 0; \ + if (! enif_get_int64(env, argv[5], &i4)) \ + i4 = 0; \ + get_string_maybe(env, argv[6], &mbuf1, messagebuf1, MESSAGE_BUFSIZ); \ + get_string_maybe(env, argv[7], &mbuf2, messagebuf2, MESSAGE_BUFSIZ); \ + get_string_maybe(env, argv[8], &mbuf3, messagebuf3, MESSAGE_BUFSIZ); \ + get_string_maybe(env, argv[9], &mbuf4, messagebuf4, MESSAGE_BUFSIZ); \ + DTRACE10_LABEL(user_trace_n, the_label, procbuf, utbuf, \ + i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4); \ + return am_true; \ + } else { \ + return am_false; \ + } \ + break + +ERL_NIF_TERM erl_nif_user_trace_n(ErlNifEnv* env, int argc, + const ERL_NIF_TERM argv[]) +{ + DTRACE_CHARBUF(procbuf, 32 + 1); + DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1); + char *utbuf = NULL; + ErlNifSInt64 i1, i2, i3, i4; + DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1); + DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1); + DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1); + DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1); + char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL; + ErlNifSInt64 label = 0; + + if (! enif_get_int64(env, argv[0], &label) || label < 0 || label > 1023) { + return am_badarg; + } + switch (label) { + N_STATEMENT(0); + N_STATEMENT(1); + N_STATEMENT(2); + N_STATEMENT(3); + N_STATEMENT(4); + N_STATEMENT(5); + N_STATEMENT(6); + N_STATEMENT(7); + N_STATEMENT(8); + N_STATEMENT(9); + N_STATEMENT(10); + N_STATEMENT(11); + N_STATEMENT(12); + N_STATEMENT(13); + N_STATEMENT(14); + N_STATEMENT(15); + N_STATEMENT(16); + N_STATEMENT(17); + N_STATEMENT(18); + N_STATEMENT(19); + N_STATEMENT(20); + N_STATEMENT(21); + N_STATEMENT(22); + N_STATEMENT(23); + N_STATEMENT(24); + N_STATEMENT(25); + N_STATEMENT(26); + N_STATEMENT(27); + N_STATEMENT(28); + N_STATEMENT(29); + N_STATEMENT(30); + N_STATEMENT(31); + N_STATEMENT(32); + N_STATEMENT(33); + N_STATEMENT(34); + N_STATEMENT(35); + N_STATEMENT(36); + N_STATEMENT(37); + N_STATEMENT(38); + N_STATEMENT(39); + N_STATEMENT(40); + N_STATEMENT(41); + N_STATEMENT(42); + N_STATEMENT(43); + N_STATEMENT(44); + N_STATEMENT(45); + N_STATEMENT(46); + N_STATEMENT(47); + N_STATEMENT(48); + N_STATEMENT(49); + N_STATEMENT(50); + N_STATEMENT(51); + N_STATEMENT(52); + N_STATEMENT(53); + N_STATEMENT(54); + N_STATEMENT(55); + N_STATEMENT(56); + N_STATEMENT(57); + N_STATEMENT(58); + N_STATEMENT(59); + N_STATEMENT(60); + N_STATEMENT(61); + N_STATEMENT(62); + N_STATEMENT(63); + N_STATEMENT(64); + N_STATEMENT(65); + N_STATEMENT(66); + N_STATEMENT(67); + N_STATEMENT(68); + N_STATEMENT(69); + N_STATEMENT(70); + N_STATEMENT(71); + N_STATEMENT(72); + N_STATEMENT(73); + N_STATEMENT(74); + N_STATEMENT(75); + N_STATEMENT(76); + N_STATEMENT(77); + N_STATEMENT(78); + N_STATEMENT(79); + N_STATEMENT(80); + N_STATEMENT(81); + N_STATEMENT(82); + N_STATEMENT(83); + N_STATEMENT(84); + N_STATEMENT(85); + N_STATEMENT(86); + N_STATEMENT(87); + N_STATEMENT(88); + N_STATEMENT(89); + N_STATEMENT(90); + N_STATEMENT(91); + N_STATEMENT(92); + N_STATEMENT(93); + N_STATEMENT(94); + N_STATEMENT(95); + N_STATEMENT(96); + N_STATEMENT(97); + N_STATEMENT(98); + N_STATEMENT(99); + N_STATEMENT(100); + N_STATEMENT(101); + N_STATEMENT(102); + N_STATEMENT(103); + N_STATEMENT(104); + N_STATEMENT(105); + N_STATEMENT(106); + N_STATEMENT(107); + N_STATEMENT(108); + N_STATEMENT(109); + N_STATEMENT(110); + N_STATEMENT(111); + N_STATEMENT(112); + N_STATEMENT(113); + N_STATEMENT(114); + N_STATEMENT(115); + N_STATEMENT(116); + N_STATEMENT(117); + N_STATEMENT(118); + N_STATEMENT(119); + N_STATEMENT(120); + N_STATEMENT(121); + N_STATEMENT(122); + N_STATEMENT(123); + N_STATEMENT(124); + N_STATEMENT(125); + N_STATEMENT(126); + N_STATEMENT(127); + N_STATEMENT(128); + N_STATEMENT(129); + N_STATEMENT(130); + N_STATEMENT(131); + N_STATEMENT(132); + N_STATEMENT(133); + N_STATEMENT(134); + N_STATEMENT(135); + N_STATEMENT(136); + N_STATEMENT(137); + N_STATEMENT(138); + N_STATEMENT(139); + N_STATEMENT(140); + N_STATEMENT(141); + N_STATEMENT(142); + N_STATEMENT(143); + N_STATEMENT(144); + N_STATEMENT(145); + N_STATEMENT(146); + N_STATEMENT(147); + N_STATEMENT(148); + N_STATEMENT(149); + N_STATEMENT(150); + N_STATEMENT(151); + N_STATEMENT(152); + N_STATEMENT(153); + N_STATEMENT(154); + N_STATEMENT(155); + N_STATEMENT(156); + N_STATEMENT(157); + N_STATEMENT(158); + N_STATEMENT(159); + N_STATEMENT(160); + N_STATEMENT(161); + N_STATEMENT(162); + N_STATEMENT(163); + N_STATEMENT(164); + N_STATEMENT(165); + N_STATEMENT(166); + N_STATEMENT(167); + N_STATEMENT(168); + N_STATEMENT(169); + N_STATEMENT(170); + N_STATEMENT(171); + N_STATEMENT(172); + N_STATEMENT(173); + N_STATEMENT(174); + N_STATEMENT(175); + N_STATEMENT(176); + N_STATEMENT(177); + N_STATEMENT(178); + N_STATEMENT(179); + N_STATEMENT(180); + N_STATEMENT(181); + N_STATEMENT(182); + N_STATEMENT(183); + N_STATEMENT(184); + N_STATEMENT(185); + N_STATEMENT(186); + N_STATEMENT(187); + N_STATEMENT(188); + N_STATEMENT(189); + N_STATEMENT(190); + N_STATEMENT(191); + N_STATEMENT(192); + N_STATEMENT(193); + N_STATEMENT(194); + N_STATEMENT(195); + N_STATEMENT(196); + N_STATEMENT(197); + N_STATEMENT(198); + N_STATEMENT(199); + N_STATEMENT(200); + N_STATEMENT(201); + N_STATEMENT(202); + N_STATEMENT(203); + N_STATEMENT(204); + N_STATEMENT(205); + N_STATEMENT(206); + N_STATEMENT(207); + N_STATEMENT(208); + N_STATEMENT(209); + N_STATEMENT(210); + N_STATEMENT(211); + N_STATEMENT(212); + N_STATEMENT(213); + N_STATEMENT(214); + N_STATEMENT(215); + N_STATEMENT(216); + N_STATEMENT(217); + N_STATEMENT(218); + N_STATEMENT(219); + N_STATEMENT(220); + N_STATEMENT(221); + N_STATEMENT(222); + N_STATEMENT(223); + N_STATEMENT(224); + N_STATEMENT(225); + N_STATEMENT(226); + N_STATEMENT(227); + N_STATEMENT(228); + N_STATEMENT(229); + N_STATEMENT(230); + N_STATEMENT(231); + N_STATEMENT(232); + N_STATEMENT(233); + N_STATEMENT(234); + N_STATEMENT(235); + N_STATEMENT(236); + N_STATEMENT(237); + N_STATEMENT(238); + N_STATEMENT(239); + N_STATEMENT(240); + N_STATEMENT(241); + N_STATEMENT(242); + N_STATEMENT(243); + N_STATEMENT(244); + N_STATEMENT(245); + N_STATEMENT(246); + N_STATEMENT(247); + N_STATEMENT(248); + N_STATEMENT(249); + N_STATEMENT(250); + N_STATEMENT(251); + N_STATEMENT(252); + N_STATEMENT(253); + N_STATEMENT(254); + N_STATEMENT(255); + N_STATEMENT(256); + N_STATEMENT(257); + N_STATEMENT(258); + N_STATEMENT(259); + N_STATEMENT(260); + N_STATEMENT(261); + N_STATEMENT(262); + N_STATEMENT(263); + N_STATEMENT(264); + N_STATEMENT(265); + N_STATEMENT(266); + N_STATEMENT(267); + N_STATEMENT(268); + N_STATEMENT(269); + N_STATEMENT(270); + N_STATEMENT(271); + N_STATEMENT(272); + N_STATEMENT(273); + N_STATEMENT(274); + N_STATEMENT(275); + N_STATEMENT(276); + N_STATEMENT(277); + N_STATEMENT(278); + N_STATEMENT(279); + N_STATEMENT(280); + N_STATEMENT(281); + N_STATEMENT(282); + N_STATEMENT(283); + N_STATEMENT(284); + N_STATEMENT(285); + N_STATEMENT(286); + N_STATEMENT(287); + N_STATEMENT(288); + N_STATEMENT(289); + N_STATEMENT(290); + N_STATEMENT(291); + N_STATEMENT(292); + N_STATEMENT(293); + N_STATEMENT(294); + N_STATEMENT(295); + N_STATEMENT(296); + N_STATEMENT(297); + N_STATEMENT(298); + N_STATEMENT(299); + N_STATEMENT(300); + N_STATEMENT(301); + N_STATEMENT(302); + N_STATEMENT(303); + N_STATEMENT(304); + N_STATEMENT(305); + N_STATEMENT(306); + N_STATEMENT(307); + N_STATEMENT(308); + N_STATEMENT(309); + N_STATEMENT(310); + N_STATEMENT(311); + N_STATEMENT(312); + N_STATEMENT(313); + N_STATEMENT(314); + N_STATEMENT(315); + N_STATEMENT(316); + N_STATEMENT(317); + N_STATEMENT(318); + N_STATEMENT(319); + N_STATEMENT(320); + N_STATEMENT(321); + N_STATEMENT(322); + N_STATEMENT(323); + N_STATEMENT(324); + N_STATEMENT(325); + N_STATEMENT(326); + N_STATEMENT(327); + N_STATEMENT(328); + N_STATEMENT(329); + N_STATEMENT(330); + N_STATEMENT(331); + N_STATEMENT(332); + N_STATEMENT(333); + N_STATEMENT(334); + N_STATEMENT(335); + N_STATEMENT(336); + N_STATEMENT(337); + N_STATEMENT(338); + N_STATEMENT(339); + N_STATEMENT(340); + N_STATEMENT(341); + N_STATEMENT(342); + N_STATEMENT(343); + N_STATEMENT(344); + N_STATEMENT(345); + N_STATEMENT(346); + N_STATEMENT(347); + N_STATEMENT(348); + N_STATEMENT(349); + N_STATEMENT(350); + N_STATEMENT(351); + N_STATEMENT(352); + N_STATEMENT(353); + N_STATEMENT(354); + N_STATEMENT(355); + N_STATEMENT(356); + N_STATEMENT(357); + N_STATEMENT(358); + N_STATEMENT(359); + N_STATEMENT(360); + N_STATEMENT(361); + N_STATEMENT(362); + N_STATEMENT(363); + N_STATEMENT(364); + N_STATEMENT(365); + N_STATEMENT(366); + N_STATEMENT(367); + N_STATEMENT(368); + N_STATEMENT(369); + N_STATEMENT(370); + N_STATEMENT(371); + N_STATEMENT(372); + N_STATEMENT(373); + N_STATEMENT(374); + N_STATEMENT(375); + N_STATEMENT(376); + N_STATEMENT(377); + N_STATEMENT(378); + N_STATEMENT(379); + N_STATEMENT(380); + N_STATEMENT(381); + N_STATEMENT(382); + N_STATEMENT(383); + N_STATEMENT(384); + N_STATEMENT(385); + N_STATEMENT(386); + N_STATEMENT(387); + N_STATEMENT(388); + N_STATEMENT(389); + N_STATEMENT(390); + N_STATEMENT(391); + N_STATEMENT(392); + N_STATEMENT(393); + N_STATEMENT(394); + N_STATEMENT(395); + N_STATEMENT(396); + N_STATEMENT(397); + N_STATEMENT(398); + N_STATEMENT(399); + N_STATEMENT(400); + N_STATEMENT(401); + N_STATEMENT(402); + N_STATEMENT(403); + N_STATEMENT(404); + N_STATEMENT(405); + N_STATEMENT(406); + N_STATEMENT(407); + N_STATEMENT(408); + N_STATEMENT(409); + N_STATEMENT(410); + N_STATEMENT(411); + N_STATEMENT(412); + N_STATEMENT(413); + N_STATEMENT(414); + N_STATEMENT(415); + N_STATEMENT(416); + N_STATEMENT(417); + N_STATEMENT(418); + N_STATEMENT(419); + N_STATEMENT(420); + N_STATEMENT(421); + N_STATEMENT(422); + N_STATEMENT(423); + N_STATEMENT(424); + N_STATEMENT(425); + N_STATEMENT(426); + N_STATEMENT(427); + N_STATEMENT(428); + N_STATEMENT(429); + N_STATEMENT(430); + N_STATEMENT(431); + N_STATEMENT(432); + N_STATEMENT(433); + N_STATEMENT(434); + N_STATEMENT(435); + N_STATEMENT(436); + N_STATEMENT(437); + N_STATEMENT(438); + N_STATEMENT(439); + N_STATEMENT(440); + N_STATEMENT(441); + N_STATEMENT(442); + N_STATEMENT(443); + N_STATEMENT(444); + N_STATEMENT(445); + N_STATEMENT(446); + N_STATEMENT(447); + N_STATEMENT(448); + N_STATEMENT(449); + N_STATEMENT(450); + N_STATEMENT(451); + N_STATEMENT(452); + N_STATEMENT(453); + N_STATEMENT(454); + N_STATEMENT(455); + N_STATEMENT(456); + N_STATEMENT(457); + N_STATEMENT(458); + N_STATEMENT(459); + N_STATEMENT(460); + N_STATEMENT(461); + N_STATEMENT(462); + N_STATEMENT(463); + N_STATEMENT(464); + N_STATEMENT(465); + N_STATEMENT(466); + N_STATEMENT(467); + N_STATEMENT(468); + N_STATEMENT(469); + N_STATEMENT(470); + N_STATEMENT(471); + N_STATEMENT(472); + N_STATEMENT(473); + N_STATEMENT(474); + N_STATEMENT(475); + N_STATEMENT(476); + N_STATEMENT(477); + N_STATEMENT(478); + N_STATEMENT(479); + N_STATEMENT(480); + N_STATEMENT(481); + N_STATEMENT(482); + N_STATEMENT(483); + N_STATEMENT(484); + N_STATEMENT(485); + N_STATEMENT(486); + N_STATEMENT(487); + N_STATEMENT(488); + N_STATEMENT(489); + N_STATEMENT(490); + N_STATEMENT(491); + N_STATEMENT(492); + N_STATEMENT(493); + N_STATEMENT(494); + N_STATEMENT(495); + N_STATEMENT(496); + N_STATEMENT(497); + N_STATEMENT(498); + N_STATEMENT(499); + N_STATEMENT(500); + N_STATEMENT(501); + N_STATEMENT(502); + N_STATEMENT(503); + N_STATEMENT(504); + N_STATEMENT(505); + N_STATEMENT(506); + N_STATEMENT(507); + N_STATEMENT(508); + N_STATEMENT(509); + N_STATEMENT(510); + N_STATEMENT(511); + N_STATEMENT(512); + N_STATEMENT(513); + N_STATEMENT(514); + N_STATEMENT(515); + N_STATEMENT(516); + N_STATEMENT(517); + N_STATEMENT(518); + N_STATEMENT(519); + N_STATEMENT(520); + N_STATEMENT(521); + N_STATEMENT(522); + N_STATEMENT(523); + N_STATEMENT(524); + N_STATEMENT(525); + N_STATEMENT(526); + N_STATEMENT(527); + N_STATEMENT(528); + N_STATEMENT(529); + N_STATEMENT(530); + N_STATEMENT(531); + N_STATEMENT(532); + N_STATEMENT(533); + N_STATEMENT(534); + N_STATEMENT(535); + N_STATEMENT(536); + N_STATEMENT(537); + N_STATEMENT(538); + N_STATEMENT(539); + N_STATEMENT(540); + N_STATEMENT(541); + N_STATEMENT(542); + N_STATEMENT(543); + N_STATEMENT(544); + N_STATEMENT(545); + N_STATEMENT(546); + N_STATEMENT(547); + N_STATEMENT(548); + N_STATEMENT(549); + N_STATEMENT(550); + N_STATEMENT(551); + N_STATEMENT(552); + N_STATEMENT(553); + N_STATEMENT(554); + N_STATEMENT(555); + N_STATEMENT(556); + N_STATEMENT(557); + N_STATEMENT(558); + N_STATEMENT(559); + N_STATEMENT(560); + N_STATEMENT(561); + N_STATEMENT(562); + N_STATEMENT(563); + N_STATEMENT(564); + N_STATEMENT(565); + N_STATEMENT(566); + N_STATEMENT(567); + N_STATEMENT(568); + N_STATEMENT(569); + N_STATEMENT(570); + N_STATEMENT(571); + N_STATEMENT(572); + N_STATEMENT(573); + N_STATEMENT(574); + N_STATEMENT(575); + N_STATEMENT(576); + N_STATEMENT(577); + N_STATEMENT(578); + N_STATEMENT(579); + N_STATEMENT(580); + N_STATEMENT(581); + N_STATEMENT(582); + N_STATEMENT(583); + N_STATEMENT(584); + N_STATEMENT(585); + N_STATEMENT(586); + N_STATEMENT(587); + N_STATEMENT(588); + N_STATEMENT(589); + N_STATEMENT(590); + N_STATEMENT(591); + N_STATEMENT(592); + N_STATEMENT(593); + N_STATEMENT(594); + N_STATEMENT(595); + N_STATEMENT(596); + N_STATEMENT(597); + N_STATEMENT(598); + N_STATEMENT(599); + N_STATEMENT(600); + N_STATEMENT(601); + N_STATEMENT(602); + N_STATEMENT(603); + N_STATEMENT(604); + N_STATEMENT(605); + N_STATEMENT(606); + N_STATEMENT(607); + N_STATEMENT(608); + N_STATEMENT(609); + N_STATEMENT(610); + N_STATEMENT(611); + N_STATEMENT(612); + N_STATEMENT(613); + N_STATEMENT(614); + N_STATEMENT(615); + N_STATEMENT(616); + N_STATEMENT(617); + N_STATEMENT(618); + N_STATEMENT(619); + N_STATEMENT(620); + N_STATEMENT(621); + N_STATEMENT(622); + N_STATEMENT(623); + N_STATEMENT(624); + N_STATEMENT(625); + N_STATEMENT(626); + N_STATEMENT(627); + N_STATEMENT(628); + N_STATEMENT(629); + N_STATEMENT(630); + N_STATEMENT(631); + N_STATEMENT(632); + N_STATEMENT(633); + N_STATEMENT(634); + N_STATEMENT(635); + N_STATEMENT(636); + N_STATEMENT(637); + N_STATEMENT(638); + N_STATEMENT(639); + N_STATEMENT(640); + N_STATEMENT(641); + N_STATEMENT(642); + N_STATEMENT(643); + N_STATEMENT(644); + N_STATEMENT(645); + N_STATEMENT(646); + N_STATEMENT(647); + N_STATEMENT(648); + N_STATEMENT(649); + N_STATEMENT(650); + N_STATEMENT(651); + N_STATEMENT(652); + N_STATEMENT(653); + N_STATEMENT(654); + N_STATEMENT(655); + N_STATEMENT(656); + N_STATEMENT(657); + N_STATEMENT(658); + N_STATEMENT(659); + N_STATEMENT(660); + N_STATEMENT(661); + N_STATEMENT(662); + N_STATEMENT(663); + N_STATEMENT(664); + N_STATEMENT(665); + N_STATEMENT(666); + N_STATEMENT(667); + N_STATEMENT(668); + N_STATEMENT(669); + N_STATEMENT(670); + N_STATEMENT(671); + N_STATEMENT(672); + N_STATEMENT(673); + N_STATEMENT(674); + N_STATEMENT(675); + N_STATEMENT(676); + N_STATEMENT(677); + N_STATEMENT(678); + N_STATEMENT(679); + N_STATEMENT(680); + N_STATEMENT(681); + N_STATEMENT(682); + N_STATEMENT(683); + N_STATEMENT(684); + N_STATEMENT(685); + N_STATEMENT(686); + N_STATEMENT(687); + N_STATEMENT(688); + N_STATEMENT(689); + N_STATEMENT(690); + N_STATEMENT(691); + N_STATEMENT(692); + N_STATEMENT(693); + N_STATEMENT(694); + N_STATEMENT(695); + N_STATEMENT(696); + N_STATEMENT(697); + N_STATEMENT(698); + N_STATEMENT(699); + N_STATEMENT(700); + N_STATEMENT(701); + N_STATEMENT(702); + N_STATEMENT(703); + N_STATEMENT(704); + N_STATEMENT(705); + N_STATEMENT(706); + N_STATEMENT(707); + N_STATEMENT(708); + N_STATEMENT(709); + N_STATEMENT(710); + N_STATEMENT(711); + N_STATEMENT(712); + N_STATEMENT(713); + N_STATEMENT(714); + N_STATEMENT(715); + N_STATEMENT(716); + N_STATEMENT(717); + N_STATEMENT(718); + N_STATEMENT(719); + N_STATEMENT(720); + N_STATEMENT(721); + N_STATEMENT(722); + N_STATEMENT(723); + N_STATEMENT(724); + N_STATEMENT(725); + N_STATEMENT(726); + N_STATEMENT(727); + N_STATEMENT(728); + N_STATEMENT(729); + N_STATEMENT(730); + N_STATEMENT(731); + N_STATEMENT(732); + N_STATEMENT(733); + N_STATEMENT(734); + N_STATEMENT(735); + N_STATEMENT(736); + N_STATEMENT(737); + N_STATEMENT(738); + N_STATEMENT(739); + N_STATEMENT(740); + N_STATEMENT(741); + N_STATEMENT(742); + N_STATEMENT(743); + N_STATEMENT(744); + N_STATEMENT(745); + N_STATEMENT(746); + N_STATEMENT(747); + N_STATEMENT(748); + N_STATEMENT(749); + N_STATEMENT(750); + N_STATEMENT(751); + N_STATEMENT(752); + N_STATEMENT(753); + N_STATEMENT(754); + N_STATEMENT(755); + N_STATEMENT(756); + N_STATEMENT(757); + N_STATEMENT(758); + N_STATEMENT(759); + N_STATEMENT(760); + N_STATEMENT(761); + N_STATEMENT(762); + N_STATEMENT(763); + N_STATEMENT(764); + N_STATEMENT(765); + N_STATEMENT(766); + N_STATEMENT(767); + N_STATEMENT(768); + N_STATEMENT(769); + N_STATEMENT(770); + N_STATEMENT(771); + N_STATEMENT(772); + N_STATEMENT(773); + N_STATEMENT(774); + N_STATEMENT(775); + N_STATEMENT(776); + N_STATEMENT(777); + N_STATEMENT(778); + N_STATEMENT(779); + N_STATEMENT(780); + N_STATEMENT(781); + N_STATEMENT(782); + N_STATEMENT(783); + N_STATEMENT(784); + N_STATEMENT(785); + N_STATEMENT(786); + N_STATEMENT(787); + N_STATEMENT(788); + N_STATEMENT(789); + N_STATEMENT(790); + N_STATEMENT(791); + N_STATEMENT(792); + N_STATEMENT(793); + N_STATEMENT(794); + N_STATEMENT(795); + N_STATEMENT(796); + N_STATEMENT(797); + N_STATEMENT(798); + N_STATEMENT(799); + N_STATEMENT(800); + N_STATEMENT(801); + N_STATEMENT(802); + N_STATEMENT(803); + N_STATEMENT(804); + N_STATEMENT(805); + N_STATEMENT(806); + N_STATEMENT(807); + N_STATEMENT(808); + N_STATEMENT(809); + N_STATEMENT(810); + N_STATEMENT(811); + N_STATEMENT(812); + N_STATEMENT(813); + N_STATEMENT(814); + N_STATEMENT(815); + N_STATEMENT(816); + N_STATEMENT(817); + N_STATEMENT(818); + N_STATEMENT(819); + N_STATEMENT(820); + N_STATEMENT(821); + N_STATEMENT(822); + N_STATEMENT(823); + N_STATEMENT(824); + N_STATEMENT(825); + N_STATEMENT(826); + N_STATEMENT(827); + N_STATEMENT(828); + N_STATEMENT(829); + N_STATEMENT(830); + N_STATEMENT(831); + N_STATEMENT(832); + N_STATEMENT(833); + N_STATEMENT(834); + N_STATEMENT(835); + N_STATEMENT(836); + N_STATEMENT(837); + N_STATEMENT(838); + N_STATEMENT(839); + N_STATEMENT(840); + N_STATEMENT(841); + N_STATEMENT(842); + N_STATEMENT(843); + N_STATEMENT(844); + N_STATEMENT(845); + N_STATEMENT(846); + N_STATEMENT(847); + N_STATEMENT(848); + N_STATEMENT(849); + N_STATEMENT(850); + N_STATEMENT(851); + N_STATEMENT(852); + N_STATEMENT(853); + N_STATEMENT(854); + N_STATEMENT(855); + N_STATEMENT(856); + N_STATEMENT(857); + N_STATEMENT(858); + N_STATEMENT(859); + N_STATEMENT(860); + N_STATEMENT(861); + N_STATEMENT(862); + N_STATEMENT(863); + N_STATEMENT(864); + N_STATEMENT(865); + N_STATEMENT(866); + N_STATEMENT(867); + N_STATEMENT(868); + N_STATEMENT(869); + N_STATEMENT(870); + N_STATEMENT(871); + N_STATEMENT(872); + N_STATEMENT(873); + N_STATEMENT(874); + N_STATEMENT(875); + N_STATEMENT(876); + N_STATEMENT(877); + N_STATEMENT(878); + N_STATEMENT(879); + N_STATEMENT(880); + N_STATEMENT(881); + N_STATEMENT(882); + N_STATEMENT(883); + N_STATEMENT(884); + N_STATEMENT(885); + N_STATEMENT(886); + N_STATEMENT(887); + N_STATEMENT(888); + N_STATEMENT(889); + N_STATEMENT(890); + N_STATEMENT(891); + N_STATEMENT(892); + N_STATEMENT(893); + N_STATEMENT(894); + N_STATEMENT(895); + N_STATEMENT(896); + N_STATEMENT(897); + N_STATEMENT(898); + N_STATEMENT(899); + N_STATEMENT(900); + N_STATEMENT(901); + N_STATEMENT(902); + N_STATEMENT(903); + N_STATEMENT(904); + N_STATEMENT(905); + N_STATEMENT(906); + N_STATEMENT(907); + N_STATEMENT(908); + N_STATEMENT(909); + N_STATEMENT(910); + N_STATEMENT(911); + N_STATEMENT(912); + N_STATEMENT(913); + N_STATEMENT(914); + N_STATEMENT(915); + N_STATEMENT(916); + N_STATEMENT(917); + N_STATEMENT(918); + N_STATEMENT(919); + N_STATEMENT(920); + N_STATEMENT(921); + N_STATEMENT(922); + N_STATEMENT(923); + N_STATEMENT(924); + N_STATEMENT(925); + N_STATEMENT(926); + N_STATEMENT(927); + N_STATEMENT(928); + N_STATEMENT(929); + N_STATEMENT(930); + N_STATEMENT(931); + N_STATEMENT(932); + N_STATEMENT(933); + N_STATEMENT(934); + N_STATEMENT(935); + N_STATEMENT(936); + N_STATEMENT(937); + N_STATEMENT(938); + N_STATEMENT(939); + N_STATEMENT(940); + N_STATEMENT(941); + N_STATEMENT(942); + N_STATEMENT(943); + N_STATEMENT(944); + N_STATEMENT(945); + N_STATEMENT(946); + N_STATEMENT(947); + N_STATEMENT(948); + N_STATEMENT(949); + N_STATEMENT(950); + } + return am_error; /* NOTREACHED, shut up the compiler */ +} + +#endif /* HAVE_USE_DTRACE */ diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index e5d99dc4f1..33e221b44c 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -226,6 +226,15 @@ ERL_NIF_INIT_DECL(NAME) \ } \ ERL_NIF_INIT_EPILOGUE +#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP)) +#define HAVE_USE_DTRACE 1 +#endif + +#ifdef HAVE_USE_DTRACE +ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM erl_nif_user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#endif #endif /* __ERL_NIF_H__ */ diff --git a/erts/emulator/beam/erl_nmgc.c b/erts/emulator/beam/erl_nmgc.c deleted file mode 100644 index 2a8c819360..0000000000 --- a/erts/emulator/beam/erl_nmgc.c +++ /dev/null @@ -1,1401 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2004-2011. 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% - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include "global.h" -#include "erl_gc.h" -#include "erl_binary.h" -#include "erl_nmgc.h" -#include "erl_debug.h" -#if HIPE -#include "hipe_stack.h" -#endif - - -#ifdef INCREMENTAL -/*************************************************************************** - * * - * Incremental Garbage Collector for the Message Area * - * * - ***************************************************************************/ - -/* - * The heap pointers are declared in erl_init.c - * global_heap is the nursery - * global_old_heap is the old generation - */ -unsigned char *blackmap = NULL; -INC_Page *inc_used_mem = NULL; -INC_MemBlock *inc_free_list = NULL; -Eterm *inc_fromspc; -Eterm *inc_fromend; -Eterm *inc_nursery_scn_ptr; -Eterm **fwdptrs; -Eterm *inc_alloc_limit; -Process *inc_active_proc; -Process *inc_active_last; -int inc_words_to_go; - -static Eterm *inc_last_nursery; -static int inc_pages = INC_NoPAGES; -static INC_Page *inc_bibop = NULL; -static int inc_used_pages; - -/* Used when growing the old generation */ -/* -#define INC_ROOTSAVE 16384 -static Eterm *root_save[INC_ROOTSAVE]; -static int roots_saved = 0; -*/ - -INC_STORAGE_DECLARATION(,gray); - -static void inc_minor_gc(Process *p, int need, Eterm* objv, int nobj); -static void inc_major_gc(Process *p, int need, Eterm* objv, int nobj); - -#ifdef INC_TIME_BASED -#if USE_PERFCTR - -/* - * This uses the Linux perfctr extension to virtualise the - * time-stamp counter. - */ -#include "libperfctr.h" -static struct vperfctr *vperfctr; -static double cpu_khz; -static double tsc_to_cpu_mult; - -static void inc_start_hrvtime(void) -{ - struct perfctr_info info; - struct vperfctr_control control; - - if( vperfctr != NULL ) - return; - vperfctr = vperfctr_open(); - if( vperfctr == NULL ) - return; - if( vperfctr_info(vperfctr, &info) >= 0 ) { - cpu_khz = (double)info.cpu_khz; - tsc_to_cpu_mult = (double)(info.tsc_to_cpu_mult ? : 1); - if( info.cpu_features & PERFCTR_FEATURE_RDTSC ) { - memset(&control, 0, sizeof control); - control.cpu_control.tsc_on = 1; - if( vperfctr_control(vperfctr, &control) >= 0 ) - return; - } - } - vperfctr_close(vperfctr); - vperfctr = NULL; -} - -#define inc_get_hrvtime() (((double)vperfctr_read_tsc(vperfctr) * tsc_to_cpu_mult) / cpu_khz) - -#endif /* USE_PERFCTR */ -#endif /* INC_TIME_BASED */ - -#ifdef INC_TIME_BASED -# define timeslice 1 /* milli seconds */ -# define WORK_MORE (inc_get_hrvtime() < start_time + timeslice) -#else -//# define inc_min_work 100 /* words */ -# define inc_min_work global_heap_sz + inc_pages * INC_FULLPAGE /* words */ -# define WORK_MORE (inc_words_to_go > 0) -#endif - -void erts_init_incgc(void) -{ - int i; - int size = inc_pages * INC_FULLPAGE; - - /* Young generation */ - global_heap = (Eterm *)erts_alloc(ERTS_ALC_T_MESSAGE_AREA, - sizeof(Eterm) * global_heap_sz); - global_hend = global_heap + global_heap_sz; - global_htop = global_heap; - inc_alloc_limit = global_hend; - - /* Fromspace */ - inc_last_nursery = (Eterm *) erts_alloc(ERTS_ALC_T_MESSAGE_AREA, - global_heap_sz * sizeof(Eterm)); - inc_fromspc = inc_fromend = NULL; - - /* Forward-pointers */ - fwdptrs = erts_alloc(ERTS_ALC_T_MESSAGE_AREA, - global_heap_sz * sizeof(Eterm*)); - /* Old generation */ - global_old_heap = (Eterm *)erts_alloc(ERTS_ALC_T_MESSAGE_AREA, - size * sizeof(Eterm)); - global_old_hend = global_old_heap + size; - - /* Pages i BiBOP */ - for (i = 0; i < inc_pages; i++) - { - INC_Page *this = (INC_Page*)(global_old_heap + i * INC_FULLPAGE); - this->next = (INC_Page*)((Eterm*)this + INC_FULLPAGE); - } - - inc_bibop = (INC_Page*)global_old_heap; - ((INC_Page*)(global_old_heap + (inc_pages - 1) * INC_FULLPAGE))->next = - NULL; - - inc_used_mem = inc_bibop; - inc_bibop = inc_bibop->next; - inc_used_mem->next = NULL; - inc_used_pages = 1; - - /* Free-list */ - inc_free_list = (INC_MemBlock*)inc_used_mem->start; - inc_free_list->size = INC_PAGESIZE; - inc_free_list->prev = NULL; - inc_free_list->next = NULL; - - /* Blackmap */ - blackmap = (unsigned char*)erts_alloc(ERTS_ALC_T_MESSAGE_AREA, - INC_FULLPAGE * inc_pages); - /* Gray stack */ - INC_STORAGE_INIT(gray); - - inc_active_proc = NULL; - inc_active_last = NULL; - -#ifdef INC_TIME_BASED - inc_start_hrvtime(); -#endif -} - -void erts_cleanup_incgc(void) -{ - INC_STORAGE_ERASE(gray); - - if (inc_fromspc) - inc_last_nursery = inc_fromspc; - - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)global_heap); - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)inc_last_nursery); - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)global_old_heap); - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)blackmap); - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)fwdptrs); -} - -void erts_incremental_gc(Process* p, int need, Eterm* objv, int nobj) -{ - int repeat_minor; -#ifdef INC_TIME_BASED - double start_time = inc_get_hrvtime(); - int work_left_before = inc_words_to_go; -#endif - /* Used when growing the fromspace */ - static char inc_growing_nurs = 0; - - BM_STOP_TIMER(system); - //BM_MMU_READ(); - BM_RESET_TIMER(gc); - BM_START_TIMER(gc); - - VERBOSE(DEBUG_HYBRID_GC, - ("INCGC: Incremental GC START Caused by: %T Need: %d\n", - p->id,need)); - - ma_gc_flags |= GC_GLOBAL; - ma_gc_flags &= ~GC_CYCLE_START; - -#ifndef INC_TIME_BASED - /* Decide how much work to do this GC stage. The work is meassured - * in number of words copied from the young generation to the old - * plus number of work marked in the old generation. - */ - if (ma_gc_flags & GC_MAJOR) { - int wm = (need > inc_min_work) ? need : inc_min_work; - inc_words_to_go = (int)((wm * (((inc_used_pages * INC_PAGESIZE) / - (double)global_heap_sz) + 1)) + 0.5); - } - else - inc_words_to_go = (need > inc_min_work) ? need : inc_min_work; -#endif - - do { - if (ma_gc_flags & GC_MAJOR) { - /* This is a major collection cycle. */ - inc_major_gc(p,need,objv,nobj); - } else if (ma_gc_flags & GC_CYCLE) { - /* This is a minor collection cycle. */ - inc_minor_gc(p,need,objv,nobj); - } else { - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Collection cycle START\n")); - ma_gc_flags |= (GC_CYCLE | GC_CYCLE_START); - inc_fromspc = global_heap; - inc_fromend = global_htop; - global_heap = global_htop = inc_last_nursery; - global_hend = global_heap + global_heap_sz; - inc_nursery_scn_ptr = global_heap; -#ifdef INC_TIME_BASED - work_left_before = inc_words_to_go = global_heap_sz; -#endif -#ifdef DEBUG - inc_last_nursery = NULL; -#endif - memset(fwdptrs,0,global_heap_sz * sizeof(Eterm)); - - { - /* TODO: Alla processer ska v�l egentligen inte aktiveras h�r... */ - int i; - for (i = 0; i < erts_num_active_procs; i++) { - Process *cp = erts_active_procs[i]; - INC_ACTIVATE(cp); - cp->scan_top = cp->high_water; - } - } - - if (ma_gc_flags & GC_NEED_MAJOR) { - /* The previous collection cycle caused the old generation to - * overflow. This collection cycle will therefore be a major - * one. - */ - BM_COUNT(major_gc_cycles); - VERBOSE(DEBUG_HYBRID_GC,("INCGC: MAJOR cycle\n")); - inc_major_gc(p,need,objv,nobj); - } else { - BM_COUNT(minor_gc_cycles); - VERBOSE(DEBUG_HYBRID_GC,("INCGC: MINOR cycle\n")); - inc_minor_gc(p,need,objv,nobj); - } - } - - repeat_minor = 0; - if (!(ma_gc_flags & GC_CYCLE)) { - inc_alloc_limit = global_hend; - inc_last_nursery = inc_fromspc; - inc_fromspc = inc_fromend = NULL; - ASSERT(INC_STORAGE_EMPTY(gray)); - - if (inc_growing_nurs) { - /* - * The previous collection cycle caused the nursery to - * grow, now we have to grow the from-space as well. - */ - inc_last_nursery = - (Eterm*) erts_realloc(ERTS_ALC_T_MESSAGE_AREA, - (void*)inc_last_nursery, - sizeof(Eterm) * global_heap_sz); - inc_growing_nurs = 0; - } - - if (global_hend - global_htop <= need) { - /* - * Initiate a new GC cycle immediately and, if necessary, - * enlarge the nursery. - */ - if (global_heap_sz <= need) { - VERBOSE(DEBUG_HYBRID_GC, - ("INCGC: Allocating a larger nursery\n")); - global_heap_sz = erts_next_heap_size(need * 1.5,0); - inc_last_nursery = - (Eterm*) erts_realloc(ERTS_ALC_T_MESSAGE_AREA, - (void*)inc_last_nursery, - sizeof(Eterm) * global_heap_sz); - fwdptrs = erts_realloc(ERTS_ALC_T_MESSAGE_AREA,fwdptrs, - global_heap_sz * sizeof(Eterm*)); - inc_growing_nurs = 1; - } - repeat_minor = 1; - } - -#ifdef DEBUG - /* Fill the from-space with bad things */ - memset(inc_last_nursery,DEBUG_BAD_BYTE, - global_heap_sz * sizeof(Eterm)); -#endif - } - } while (repeat_minor); - - - /* Clean up after garbage collection ********************************/ - - if (inc_alloc_limit != global_hend) { - -#ifdef INC_TIME_BASED - if ((work_left_before - inc_words_to_go) == 0) { - inc_alloc_limit = global_htop + need; - } else { - inc_alloc_limit = (global_hend - global_htop) / - (inc_words_to_go / (work_left_before - inc_words_to_go)) + - global_htop; - if (inc_alloc_limit > global_hend) - inc_alloc_limit = global_hend; - } -#else - inc_alloc_limit = (Eterm*)(global_htop + (need > inc_min_work) ? - need : inc_min_work); - if (inc_alloc_limit > global_hend) - inc_alloc_limit = global_hend; -#endif - } - - ma_gc_flags &= ~GC_GLOBAL; - - /* INC_TIME_BASED: If this fails we have to increase the timeslice! */ - ASSERT(inc_alloc_limit - global_htop > need); - - BM_STOP_TIMER(gc); -#ifdef BM_TIMERS - minor_global_gc_time += gc_time; - if (gc_time > max_global_minor_time) - max_global_minor_time = gc_time; - - pause_times[(((gc_time * 1000) < MAX_PAUSE_TIME) ? - (int)(gc_time * 1000) : - MAX_PAUSE_TIME - 1)]++; -#endif - //BM_MMU_INIT(); - { static long long verif = 0; - //erts_printf("innan verify: %d\n",++verif); - if (verif==168) print_memory(NULL); - verify_everything(); - //erts_printf("efter verify: %d\n",verif); - } - BM_START_TIMER(system); - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Incremental GC END\n")); -} - - -/*************************************************************************** - * * - * Minor collection - Copy live data from young generation to old * - * * - ***************************************************************************/ - -#define MINOR_SCAN(PTR,END) do { \ - ASSERT(PTR <= END); \ - while (WORK_MORE && PTR < END) { \ - Eterm val = *PTR; \ - Eterm *obj_ptr = ptr_val(val); \ - switch (primary_tag(val)) { \ - case TAG_PRIMARY_LIST: \ - if (ptr_within(obj_ptr,inc_fromspc,inc_fromend)) { \ - if (INC_IS_FORWARDED(obj_ptr)) { \ - *PTR = make_list(INC_FORWARD_VALUE(obj_ptr)); \ - } \ - else { \ - Eterm *hp = erts_inc_alloc(2); \ - INC_STORE(gray,hp,2); \ - INC_COPY_CONS(obj_ptr,hp,PTR); \ - } \ - } \ - break; \ - case TAG_PRIMARY_BOXED: \ - if (ptr_within(obj_ptr,inc_fromspc,inc_fromend)) { \ - if (INC_IS_FORWARDED(obj_ptr)) { \ - *PTR = make_boxed(INC_FORWARD_VALUE(obj_ptr)); \ - } \ - else { \ - Eterm *hp = erts_inc_alloc(BOXED_NEED(obj_ptr,*obj_ptr)); \ - INC_STORE(gray,hp,BOXED_NEED(obj_ptr,*obj_ptr)); \ - INC_COPY_BOXED(obj_ptr,hp,PTR); \ - } \ - } \ - break; \ - case TAG_PRIMARY_HEADER: \ - switch (val & _TAG_HEADER_MASK) { \ - case ARITYVAL_SUBTAG: break; \ - default: PTR += thing_arityval(val); break; \ - } \ - break; \ - } \ - PTR++; \ - } \ -} while(0) - - -/* Returns: TRUE (1) if the need is greater than the available space - * and the garbage collector needs to be restarted immediately. FALSE - * (0) otherwise. - */ -static void inc_minor_gc(Process* p, int need, Eterm* objv, int nobj) -{ - BM_COUNT(minor_gc_stages); - - /* Start with looking at gray objects found in earlier collection - * stages. - */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Rescue gray found from nursery\n")); - { - INC_Object *obj = NULL; - Eterm *ptr; - - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - MINOR_SCAN(ptr,obj->this + obj->size); - } - /* TODO: Se f�reg�ende uppdatering av gr� objekt */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Scan root-set\n")); - while (WORK_MORE && inc_active_proc) { - Rootset rootset; - Process *cp = inc_active_proc; - - ASSERT(INC_IS_ACTIVE(cp)); - - /* TODO: Hur dyrt �r det att bygga nytt rootset varje g�ng? */ - - /* TODO: Fundera p� ordningen! Rootset, Heap, Old heap... */ - - /* TODO: Scanna stacken fr�n p->send till p->stop! [Brooks84] */ - /* Notera: Vi GC:ar inte de yngsta objekten - de som allokeras - under GC-cykeln. Detta ger ynglingarna en chans att d� innan - GC:n b�rjar kopiera dem. [StefanovicMcKinleyMoss@OOPSLA99] */ - - /* TODO: N�r rootset �r scannat borde processen inte vara - aktiv mer. Den b�r aktiveras i schedule, endast om en - process har k�rt beh�ver vi scanna rootset igen. */ - - /* MT: In a multithreaded system the process cp needs to be - * locked here. - */ - - if (cp == p) - rootset.n = setup_rootset(cp, objv, nobj, &rootset); - else - rootset.n = setup_rootset(cp, cp->arg_reg, cp->arity, &rootset); - - //MA_GENSWEEP_NSTACK(cp, old_htop, n_htop, objv, nobj); - - while (WORK_MORE && rootset.n--) { - Eterm *g_ptr = rootset.v[rootset.n]; - Uint g_sz = rootset.sz[rootset.n]; - - while (WORK_MORE && g_sz--) { - Eterm gval = *g_ptr; - switch (primary_tag(gval)) { - case TAG_PRIMARY_LIST: { - Eterm *ptr = list_val(gval); - if (ptr_within(ptr,inc_fromspc,inc_fromend)) { - if (INC_IS_FORWARDED(ptr)) { - *g_ptr++ = make_list(INC_FORWARD_VALUE(ptr)); - } - else { - Eterm *hp = erts_inc_alloc(2); - INC_STORE(gray,hp,2); - INC_COPY_CONS(ptr,hp,g_ptr++); - } - } - else - ++g_ptr; - continue; - } - - case TAG_PRIMARY_BOXED: { - Eterm *ptr = boxed_val(gval); - if (ptr_within(ptr,inc_fromspc,inc_fromend)) { - if (INC_IS_FORWARDED(ptr)) { - *g_ptr++ = make_boxed(INC_FORWARD_VALUE(ptr)); - } - else { - Eterm *hp = erts_inc_alloc(BOXED_NEED(ptr,*ptr)); - INC_STORE(gray,hp,BOXED_NEED(ptr,*ptr)); - INC_COPY_BOXED(ptr,hp,g_ptr++); - } - } - else - ++g_ptr; - continue; - } - - default: - g_ptr++; - continue; - } - } - } - - restore_one_rootset(cp, &rootset); - - /* MT: cp can be unlocked now. */ - - /* VERBOSE(DEBUG_HYBRID_GC,("INCGC: Scan private nursery\n")); */ - if (cp->scan_top != HEAP_TOP(cp)) { - Eterm *ptr = cp->scan_top; - MINOR_SCAN(ptr,HEAP_TOP(cp)); - /* TODO: F�r att spara scan_top h�r m�ste alla ma-pekare - * som hittas l�ggas till i cp->rrma. - */ - //cp->scan_top = ptr; - } - - /* VERBOSE(DEBUG_HYBRID_GC,("INCGC: Scan heap fragments\n")); */ - { - ErlHeapFragment* bp = MBUF(cp); - - while (WORK_MORE && bp) { - Eterm *ptr = bp->mem; - if ((ARITH_HEAP(cp) >= bp->mem) && - (ARITH_HEAP(cp) < bp->mem + bp->size)) { - MINOR_SCAN(ptr,ARITH_HEAP(cp)); - } else { - MINOR_SCAN(ptr,bp->mem + bp->size); - } - bp = bp->next; - } - } - - /* VERBOSE(DEBUG_HYBRID_GC,("INCGC: Scan gray\n")); */ - { - INC_Object *obj = NULL; - Eterm *ptr; - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - MINOR_SCAN(ptr,obj->this + obj->size); - } - /* TODO: INC_STORE(gray,ptr,obj->size-(ptr-obj->this)); Typ.. */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - if (WORK_MORE) { - //printf("Rootset after:\r\n"); - //print_one_rootset(&rootset); - INC_DEACTIVATE(cp); - } - } - - /* Update new pointers in the nursery to new copies in old generation. */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Update nursery\n")); - { - Eterm *ptr = inc_nursery_scn_ptr; - MINOR_SCAN(ptr,global_htop); - inc_nursery_scn_ptr = ptr; - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Rescue gray found from nursery\n")); - { - INC_Object *obj = NULL; - Eterm *ptr; - - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - MINOR_SCAN(ptr,obj->this + obj->size); - } - /* TODO: Se f�reg�ende uppdatering av gr� objekt */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Update copy stack\n")); - { - Uint i; - for (i = 0; i < ma_dst_top; i++) { - if (ptr_within(ma_dst_stack[i],inc_fromspc,inc_fromend)) { - if (INC_IS_FORWARDED(ma_dst_stack[i])) - ma_dst_stack[i] = INC_FORWARD_VALUE(ma_dst_stack[i]); - } - } - } - - if (WORK_MORE) { - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Update offheap-lists\n")); - { - ExternalThing **prev = &erts_global_offheap.externals; - ExternalThing *ptr = erts_global_offheap.externals; - - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep proc externals\n")); - while (ptr) { - Eterm *ppt = (Eterm*) ptr; - - if (ptr_within(ppt,global_old_heap,global_old_hend)) { - prev = &ptr->next; - ptr = ptr->next; - } else if (ptr_within(ppt, inc_fromspc, inc_fromend) && - INC_IS_FORWARDED(ppt)) { - ExternalThing *ro = (ExternalThing*)INC_FORWARD_VALUE(ppt); - *prev = ro; /* Patch to moved pos */ - prev = &ro->next; - ptr = ro->next; - } else { - erts_deref_node_entry(ptr->node); - *prev = ptr = ptr->next; - } - } - ASSERT(*prev == NULL); - } - - { - ProcBin **prev = &erts_global_offheap.mso; - ProcBin *ptr = erts_global_offheap.mso; - - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep proc bins\n")); - while (ptr) { - Eterm *ppt = (Eterm*)ptr; - - if (ptr_within(ppt,global_old_heap,global_old_hend)) { - prev = &ptr->next; - ptr = ptr->next; - } else if (ptr_within(ppt, inc_fromspc, inc_fromend) && - INC_IS_FORWARDED(ppt)) { - ProcBin *ro = (ProcBin*)INC_FORWARD_VALUE(ppt); - *prev = ro; /* Patch to moved pos */ - prev = &ro->next; - ptr = ro->next; - } else { - Binary *bptr; - *prev = ptr->next; - bptr = ptr->val; - if (erts_refc_dectest(&bptr->refc, 0) == 0) - erts_bin_free(bptr); - ptr = *prev; - } - } - ASSERT(*prev == NULL); - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Minor collection cycle END\n")); - ma_gc_flags &= ~GC_CYCLE; - } -} - - - - -/*************************************************************************** - * * - * Major collection - CopyMark - Copy young to old, Mark-Sweep old * - * * - ***************************************************************************/ - -#define COPYMARK(PTR,END) do { \ - ASSERT(PTR <= END); \ - while (WORK_MORE && PTR < END) { \ - Eterm val = *PTR; \ - Eterm *obj_ptr = ptr_val(val); \ - switch (primary_tag(val)) { \ - case TAG_PRIMARY_LIST: \ - COPYMARK_CONS(obj_ptr,aging_htop,PTR,aging_end); break; \ - case TAG_PRIMARY_BOXED: \ - COPYMARK_BOXED(obj_ptr,aging_htop,PTR,aging_end); break; \ - case TAG_PRIMARY_HEADER: \ - switch (val & _TAG_HEADER_MASK) { \ - case ARITYVAL_SUBTAG: break; \ - default: \ - PTR += thing_arityval(val); \ - break; \ - } \ - break; \ - default: break; \ - } \ - PTR++; \ - } \ -} while(0); -/* TODO: - if (aging_htop + 10 > aging + INC_FULLPAGE) { - aging->next = inc_used_mem; - inc_used_mem = aging; - } -*/ - -static void inc_major_gc(Process *p, int need, Eterm* objv, int nobj) -{ - Eterm *free_start = NULL; - Uint live = 0; - Uint old_gen_sz = 0; - static INC_Page *aging; - static Eterm *aging_htop; - static Eterm *aging_end; - BM_NEW_TIMER(old_gc); - - BM_SWAP_TIMER(gc,old_gc); - BM_COUNT(major_gc_stages); - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Major collection START\n")); - - ma_gc_flags |= GC_INCLUDE_ALL; - - if (ma_gc_flags & GC_NEED_MAJOR) - { - INC_Page *page = inc_used_mem; - - ma_gc_flags |= GC_MAJOR; - ma_gc_flags &= ~GC_NEED_MAJOR; - - while (page) - { - memset(blackmap + - ((void*)page - (void*)global_old_heap) / sizeof(void*), - 0, INC_FULLPAGE); - page = page->next; - } - - if (inc_bibop) { - aging = inc_bibop; - inc_bibop = inc_bibop->next; - aging->next = NULL; - memset(blackmap + - ((void*)aging - (void*)global_old_heap) / sizeof(void*), - 1, INC_FULLPAGE); - aging_htop = aging->start; - aging_end = aging->start + INC_PAGESIZE; - } - else { - /* There are no free pages.. Either fragmentation is a - * problem or we are simply out of memory. Allocation in - * the old generation will be done through the free-list - * this GC cycle. - */ - aging = NULL; - aging_htop = aging_end = NULL; - } - } - - /* Start with looking at gray objects found in earlier collection - * stages. - */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark gray\n")); - { - INC_Object *obj = NULL; - - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - Eterm *ptr; - - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - COPYMARK(ptr,obj->this + obj->size); - } - /* TODO: Titta p� motsvarande i minor. */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark roots\n")); - while (WORK_MORE && inc_active_proc) - { - /* For each process: Scan all areas containing pointers to the - * message area. When a process is done here, all it's - * message-pointers should be to the old generation. - */ - Rootset rootset; - Process *cp = inc_active_proc; - - ASSERT(INC_IS_ACTIVE(cp)); - - /* MT: In a multithreaded system the process cp needs to be - * locked here. - */ - if (cp == p) - rootset.n = setup_rootset(cp, objv, nobj, &rootset); - else - rootset.n = setup_rootset(cp, cp->arg_reg, cp->arity, &rootset); - - while (WORK_MORE && rootset.n--) - { - Eterm *ptr = rootset.v[rootset.n]; - Eterm *end = ptr + rootset.sz[rootset.n]; - - while (WORK_MORE && ptr < end) { - Eterm val = *ptr; - Eterm *obj_ptr = ptr_val(val); - - switch (primary_tag(val)) { - case TAG_PRIMARY_LIST: - { - COPYMARK_CONS(obj_ptr,aging_htop,ptr,aging_end); - break; - } - - case TAG_PRIMARY_BOXED: - { - COPYMARK_BOXED(obj_ptr,aging_htop,ptr,aging_end); - break; - } - } - ptr++; - } - } - -#ifdef HIPE - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Native stack scan: %T\n",cp->id)); - aging_htop = ma_fullsweep_nstack(cp,aging_htop,aging_end); -#endif - restore_one_rootset(cp, &rootset); - - /* MT: cp can be unlocked now. But beware!! The message queue - * might be updated with new pointers to the fromspace while - * we work below. The send operation can not assume that all - * active processes will look through their message queue - * before deactivating as is the case in non-MT incremental - * collection. - */ - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark process heap\n")); - { - Eterm *ptr = cp->scan_top; - COPYMARK(ptr,cp->htop); - //cp->scan_top = ptr; - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark heap fragments\n")); - { - ErlHeapFragment* bp = MBUF(cp); - - while (WORK_MORE && bp) { - Eterm *ptr = bp->mem; - Eterm *end; - - if ((ARITH_HEAP(cp) >= bp->mem) && - (ARITH_HEAP(cp) < bp->mem + bp->size)) { - end = ARITH_HEAP(cp); - } else { - end = bp->mem + bp->size; - } - - COPYMARK(ptr,end); - bp = bp->next; - } - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark gray stack\n")); - { - INC_Object *obj = NULL; - - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - Eterm *ptr; - - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - COPYMARK(ptr,obj->this + obj->size); - } - /* TODO: Titta p� motsvarande i minor. */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - if (WORK_MORE) { - INC_DEACTIVATE(cp); - } - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark nursery\n")); - { - Eterm *ptr = inc_nursery_scn_ptr; - COPYMARK(ptr,global_htop); - inc_nursery_scn_ptr = ptr; - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark gray found in nursery\n")); - { - INC_Object *obj = NULL; - - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - Eterm *ptr; - - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - COPYMARK(ptr,obj->this + obj->size); - } - /* TODO: Titta p� motsvarande i minor. */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - - /**********************************************************************/ - if (WORK_MORE) { - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep phase\n")); - - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep externals in old generation\n")); - { - ExternalThing** prev = &erts_global_offheap.externals; - ExternalThing* ptr = erts_global_offheap.externals; - - while (ptr) { - Eterm* ppt = (Eterm *) ptr; - - if ((ptr_within(ppt, global_old_heap, global_old_hend) && - blackmap[ppt - global_old_heap] == 0) || - (ptr_within(ppt, inc_fromspc, inc_fromend) && - !INC_IS_FORWARDED(ppt))) - { - erts_deref_node_entry(ptr->node); - *prev = ptr = ptr->next; - } else if (ptr_within(ppt, inc_fromspc, inc_fromend)) { - ExternalThing* ro = (ExternalThing*)INC_FORWARD_VALUE(ppt); - *prev = ro; /* Patch to moved pos */ - prev = &ro->next; - ptr = ro->next; - } else { - prev = &ptr->next; - ptr = ptr->next; - } - } - ASSERT(*prev == NULL); - } - - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep refc bins in old generation\n")); - { - ProcBin** prev = &erts_global_offheap.mso; - ProcBin* ptr = erts_global_offheap.mso; - - while (ptr) { - Eterm *ppt = (Eterm*)ptr; - - if ((ptr_within(ppt, global_old_heap, global_old_hend) && - blackmap[ppt - global_old_heap] == 0) || - (ptr_within(ppt, inc_fromspc, inc_fromend) && - !INC_IS_FORWARDED(ppt))) - { - Binary* bptr; - *prev = ptr->next; - bptr = ptr->val; - if (erts_refc_dectest(&bptr->refc, 0) == 0) - erts_bin_free(bptr); - ptr = *prev; - } else if (ptr_within(ppt, inc_fromspc, inc_fromend)) { - ProcBin* ro = (ProcBin*)INC_FORWARD_VALUE(ppt); - *prev = ro; /* Patch to moved pos */ - prev = &ro->next; - ptr = ro->next; - } else { - prev = &ptr->next; - ptr = ptr->next; - } - } - ASSERT(*prev == NULL); - } - - /* TODO: Currently atomic phase - Can not be later of course. */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep old generation\n")); - { - INC_Page *page = inc_used_mem; - INC_Page *prev = NULL; - inc_free_list = NULL; - - while (page) { - int scavenging = 0; - int n = page->start - global_old_heap; - int stop = n + INC_PAGESIZE; - - old_gen_sz += INC_PAGESIZE; - while (n < stop) { - if (blackmap[n] != 0) { - if (scavenging) { - Eterm *ptr = global_old_heap + n; - scavenging = 0; - if ((ptr - free_start) * sizeof(Eterm) >= - sizeof(INC_MemBlock)) - { - INC_MemBlock *new = (INC_MemBlock*)free_start; - new->size = ptr - free_start; - new->prev = NULL; - new->next = inc_free_list; - if (inc_free_list) - inc_free_list->prev = new; - inc_free_list = new; - } - } - if (blackmap[n] == 255) { - unsigned int size = - *(unsigned int*)(((long)&blackmap[n]+4) & ~3); - live += size; - n += size; - } - else { - live += blackmap[n]; - n += blackmap[n]; - } - } - else if (!scavenging) { - free_start = global_old_heap + n; - scavenging = 1; - n++; - } - else { - n++; - } - } - - if (scavenging) { - if ((global_old_heap + n - free_start) * sizeof(Eterm) > - sizeof(INC_MemBlock)) - { - INC_MemBlock *new = (INC_MemBlock*)free_start; - new->size = global_old_heap + n - free_start; - new->prev = NULL; - new->next = inc_free_list; - if (inc_free_list) - inc_free_list->prev = new; - inc_free_list = new; - } - else if (free_start == page->start) { - INC_Page *next = page->next; - - if (prev) - prev->next = page->next; - else - inc_used_mem = page->next; - - page->next = inc_bibop; - inc_bibop = page; - inc_used_pages--; - page = next; - continue; - } - } - prev = page; - page = page->next; - } - } - } - - ASSERT(inc_bibop); - /* - This code is not expected to work right now. - if (!inc_bibop) { - int i; - int new_pages = inc_pages * 2; - int size = sizeof(Eterm) * new_pages * INC_FULLPAGE; - Eterm *new_heap = erts_alloc(ERTS_ALC_T_MESSAGE_AREA,size); - Eterm *new_hend = new_heap + size; - Eterm *new_htop; - Eterm *last_page_end; - INC_Page *new_used_mem; - INC_Page *page; - - erts_printf("The last page has been allocated..\n"); - erts_printf("We need to copy things!\n"); - - / * Create new, bigger bag of pages * / - for (i = 0; i < new_pages; i++) - { - INC_Page *this = - (INC_Page*)(new_heap + i * INC_FULLPAGE); - this->next = (INC_Page*)((Eterm*)this + INC_FULLPAGE); - } - inc_bibop = (INC_Page*)new_heap; - ((INC_Page*)(new_heap + (new_pages - 1) * - INC_FULLPAGE))->next = NULL; - - new_used_mem = inc_bibop; - inc_bibop = inc_bibop->next; - new_used_mem->next = NULL; - - / * Move stuff from old bag to new * / - inc_free_list = NULL; - new_htop = new_used_mem->start; - last_page_end = new_htop + INC_PAGESIZE; - page = inc_used_mem; - while (page) - { - Eterm *ptr = page->start; - Eterm *page_end = ptr + INC_PAGESIZE; - int n = offsetof(INC_Page,start) / sizeof(void*) + - ((Eterm*)page - global_old_heap); - while (ptr < page_end) - { - if (blackmap[n] > 0) - { - if (last_page_end - new_htop < blackmap[n]) - { - INC_Page *new_page = inc_bibop; - inc_bibop = inc_bibop->next; - new_page->next = new_used_mem; - new_used_mem = new_page; - new_htop = new_page->start; - last_page_end = new_htop + INC_PAGESIZE; - } - - memcpy(new_htop,ptr,blackmap[n] * sizeof(Eterm)); - for (i = 0; i < blackmap[n]; i++) - { - *ptr++ = (Eterm)new_htop++; - } - //new_htop += blackmap[n]; - //ptr += blackmap[n]; - / * - if (blackmap[n] == 255) Do the right thing... - * / - n += blackmap[n]; - } - else - { - n++; ptr++; - } - } - page = page->next; - } - - page = inc_used_mem; - while (page) - { - Eterm *ptr = page->start; - Eterm *page_end = ptr + INC_PAGESIZE; - - / * TODO: If inc_used_mem is sorted in address order, this - * pass can be done at the same time as copying. * / - while (ptr < page_end) - { - if (ptr_within(ptr_val(*ptr),global_old_heap,global_old_hend)) - { - *ptr = *((Eterm*)ptr_val(*ptr)); - } - ptr++; - } - page = page->next; - } - - printf("Restore rootset after heap move. Roots: %d\r\n",roots_saved); - while (roots_saved--) - { - Eterm *ptr = root_save[roots_saved]; - *ptr = *((Eterm*)ptr_val(*ptr)); - } - - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)global_old_heap); - - global_old_heap = new_heap; - global_old_hend = new_hend; - inc_used_mem = new_used_mem; - inc_pages = new_pages; - - if ((last_page_end - new_htop) * sizeof(Eterm) >= - sizeof(INC_MemBlock)) - { - inc_free_list = (INC_MemBlock*)(new_htop); - inc_free_list->size = last_page_end - new_htop; - inc_free_list->prev = NULL; - inc_free_list->next = NULL; - } - } - */ - - /* I vilka l�gen kan vi vilja sl�nga p� en extra sida.. ( < 25% kvar?) - if () - { - INC_Page *new_page = inc_bibop; - INC_MemBlock *new_free = - (INC_MemBlock*)new_page->start; - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Fetching new page\n")); - inc_bibop = inc_bibop->next; - - new_page->next = inc_used_mem; - if (inc_used_mem) - inc_used_mem->prev = new_page; - inc_used_mem = new_page; - - // kolla detta med normal sidstorlek! old_gen_sz += INC_PAGESIZE; - //BM_SWAP_TIMER(gc,misc1); - memset(blackmap + - ((void*)new_page - (void*)global_old_heap) / sizeof(void*), - 0, INC_FULLPAGE); - //BM_SWAP_TIMER(misc1,gc); - - new_free->prev = NULL; - new_free->next = inc_free_list; - new_free->size = INC_PAGESIZE; - if (inc_free_list) - inc_free_list->prev = new_free; - inc_free_list = new_free; - //printf("Snatched a new page @ 0x%08x\r\n",(int)new_page); - //print_free_list(); - found = new_free; - } - */ - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Update copy stack\n")); - { - Uint i; - for (i = 0; i < ma_dst_top; i++) { - if (ptr_within(ma_dst_stack[i],inc_fromspc,inc_fromend)) { - if (INC_IS_FORWARDED(ma_dst_stack[i])) - ma_dst_stack[i] = INC_FORWARD_VALUE(ma_dst_stack[i]); - } - } - } - - if (WORK_MORE) - { - int size_left = INC_PAGESIZE - (aging_htop - aging->start); - - if (size_left > sizeof(INC_MemBlock)) - { - ((INC_MemBlock*)aging_htop)->size = size_left; - ((INC_MemBlock*)aging_htop)->prev = NULL; - ((INC_MemBlock*)aging_htop)->next = inc_free_list; - if (inc_free_list) - inc_free_list->prev = (INC_MemBlock*)aging_htop; - inc_free_list = (INC_MemBlock*)aging_htop; - } - aging->next = inc_used_mem; - inc_used_mem = aging; - inc_used_pages++; - - ma_gc_flags &= ~GC_MAJOR; - ma_gc_flags &= ~GC_CYCLE; - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Major collection cycle END\n")); - } - - ma_gc_flags &= ~GC_INCLUDE_ALL; - - BM_STOP_TIMER(old_gc); -#ifdef BM_TIMER - major_global_gc_time += old_gc_time; - if (old_gc_time > max_global_major_time) - max_global_major_time = old_gc_time; - - if ((old_gc_time * 1000) < MAX_PAUSE_TIME) - pause_times_old[(int)(old_gc_time * 1000)]++; - else - pause_times_old[MAX_PAUSE_TIME - 1]++; -#endif - BM_START_TIMER(gc); -} - - - -/*************************************************************************** - * * - * Allocation in the old generation. Used in minor colection and when * - * copying the rest of a message after a GC. * - * * - ***************************************************************************/ - - -Eterm *erts_inc_alloc(int need) -{ - INC_MemBlock *this = inc_free_list; - - ASSERT(need < INC_PAGESIZE); - while (this && (this->size) < need) - { - this = this->next; - } - - if (!this) - { - /* If a free block large enough is not found, a new page is - * allocated. GC_NEED_MAJOR is set so that the next garbage - * collection cycle will be a major one, that is, both - * generations will be garbage collected. - */ - INC_Page *new_page = inc_bibop; - INC_MemBlock *new_free = (INC_MemBlock*)new_page->start; - - if (new_page) - { - VERBOSE(DEBUG_HYBRID_GC, - ("INCGC: Allocation grabs a new page\n")); - inc_bibop = inc_bibop->next; - new_page->next = inc_used_mem; - inc_used_mem = new_page; - inc_used_pages++; - - new_free->prev = NULL; - new_free->next = inc_free_list; - new_free->size = INC_PAGESIZE; - if (inc_free_list) - inc_free_list->prev = new_free; - inc_free_list = new_free; - - this = new_free; - if (!(ma_gc_flags & GC_MAJOR)) - ma_gc_flags |= GC_NEED_MAJOR; - } - else - { - erl_exit(-1, "inc_alloc ran out of pages!\n"); - } - } - - if (((this->size) - need) * sizeof(Eterm) >= sizeof(INC_MemBlock)) - { - INC_MemBlock *rest = (INC_MemBlock*)((Eterm*)this + need); - - /* The order here IS important! */ - rest->next = this->next; - - if (rest->next) - rest->next->prev = rest; - - rest->prev = this->prev; - - if (rest->prev) - rest->prev->next = rest; - else - inc_free_list = rest; - - rest->size = this->size - need; - } - else - { - if (this->prev) - this->prev->next = this->next; - else - inc_free_list = this->next; - - if (this->next) - this->next->prev = this->prev; - } - - if (ma_gc_flags & GC_MAJOR) { - if (need > 254) { - blackmap[(Eterm*)this - global_old_heap] = 255; - *(int*)((UWord)(&blackmap[(Eterm*)this - global_old_heap]+4) & ~3) = - need; - } else - blackmap[(Eterm*)this - global_old_heap] = need; - } - return (Eterm*)this; -} -#endif /* INCREMENTAL */ diff --git a/erts/emulator/beam/erl_nmgc.h b/erts/emulator/beam/erl_nmgc.h deleted file mode 100644 index b207dd37fa..0000000000 --- a/erts/emulator/beam/erl_nmgc.h +++ /dev/null @@ -1,364 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2004-2009. 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% - */ - -#ifndef __ERL_NMGC_H__ -#define __ERL_NMGC_H__ - -#ifdef INCREMENTAL -#include <stddef.h> /* offsetof() */ -#include "erl_process.h" - -#define INC_FULLPAGE (INC_PAGESIZE + offsetof(INC_Page,start) / sizeof(void*)) - -#define BOXED_NEED(PTR,HDR) \ - (((HDR) & _HEADER_SUBTAG_MASK) == SUB_BINARY_SUBTAG ? \ - header_arity(HDR) + 2 : \ - ((HDR) & _HEADER_SUBTAG_MASK) == FUN_SUBTAG ? \ - header_arity(HDR) + ((ErlFunThing*)(PTR))->num_free + 2 : \ - header_arity(HDR) + 1) - - -#define INC_DECREASE_WORK(n) inc_words_to_go -= (n); - -#define INC_COPY_CONS(FROM,TO,PTR) \ -do { \ - TO[0] = FROM[0]; \ - TO[1] = FROM[1]; \ - INC_MARK_FORWARD(FROM,TO); \ - *(PTR) = make_list(TO); \ - INC_DECREASE_WORK(2); \ - (TO) += 2; \ -} while(0) - -#define INC_COPY_BOXED(FROM,TO,PTR) \ -do { \ - Sint nelts; \ - Eterm hdr = *(FROM); \ - \ - ASSERT(is_header(hdr)); \ - INC_MARK_FORWARD(FROM,TO); \ - *(PTR) = make_boxed(TO); \ - *(TO)++ = *(FROM)++; \ - nelts = header_arity(hdr); \ - switch ((hdr) & _HEADER_SUBTAG_MASK) { \ - case SUB_BINARY_SUBTAG: nelts++; break; \ - case FUN_SUBTAG: nelts+=((ErlFunThing*)(FROM-1))->num_free+1; break;\ - } \ - INC_DECREASE_WORK(nelts + 1); \ - while (nelts--) \ - *(TO)++ = *(FROM)++; \ -} while(0) - - -/* Things copied to the old generation are not marked in the blackmap. - * This is ok since the page they are copied to (aging) is not part of - * the sweep. - */ -#define COPYMARK_CONS(FROM,TO,PTR,LIMIT) \ -do { \ - if (ptr_within(FROM,inc_fromspc,inc_fromend)) { \ - if (INC_IS_FORWARDED(FROM)) { \ - *PTR = make_list(INC_FORWARD_VALUE(FROM)); \ - } else if (TO + 2 <= LIMIT) { \ - INC_STORE(gray,TO,2); \ - INC_COPY_CONS(FROM,TO,PTR); \ - } else { \ - Eterm *hp = erts_inc_alloc(2); \ - INC_STORE(gray,hp,2); \ - INC_COPY_CONS(FROM,hp,PTR); \ - } \ - } else if (ptr_within(FROM,global_old_heap,global_old_hend) && \ - (blackmap[FROM - global_old_heap] == 0)) { \ - blackmap[FROM - global_old_heap] = 2; \ - INC_DECREASE_WORK(2); \ - INC_STORE(gray,FROM,2); \ - } \ -} while(0) - -#define COPYMARK_BOXED(FROM,TO,PTR,LIMIT) \ -do { \ - if (ptr_within(FROM,inc_fromspc,inc_fromend)) { \ - int size = BOXED_NEED(FROM,*FROM); \ - if (INC_IS_FORWARDED(FROM)) { \ - *PTR = make_boxed(INC_FORWARD_VALUE(FROM)); \ - } else if (TO + size <= LIMIT) { \ - INC_STORE(gray,TO,size); \ - INC_COPY_BOXED(FROM,TO,PTR); \ - } else { \ - Eterm *hp = erts_inc_alloc(size); \ - INC_STORE(gray,hp,size); \ - INC_COPY_BOXED(FROM,hp,PTR); \ - } \ - } else if (ptr_within(FROM,global_old_heap,global_old_hend) && \ - (blackmap[FROM - global_old_heap] == 0)) { \ - int size = BOXED_NEED(FROM,*FROM); \ - if (size > 254) { \ - blackmap[FROM - global_old_heap] = 255; \ - *(int*)((long)(&blackmap[FROM - \ - global_old_heap] + 4) & ~3) = size; \ - } else \ - blackmap[FROM - global_old_heap] = size; \ - INC_DECREASE_WORK(size); \ - INC_STORE(gray,FROM,size); \ - } \ -} while(0) - -#define INC_MARK_FORWARD(ptr,dst) fwdptrs[(ptr) - inc_fromspc] = (dst); -#define INC_IS_FORWARDED(ptr) (fwdptrs[(ptr) - inc_fromspc] != 0) -#define INC_FORWARD_VALUE(ptr) fwdptrs[(ptr) - inc_fromspc] - -/* Note for BM_TIMER: Active timer should always be 'system' when IncAlloc - * is called! - */ -#define IncAlloc(p, sz, objv, nobj) \ - (ASSERT_EXPR((sz) >= 0), \ - (((inc_alloc_limit - global_htop) <= (sz)) ? \ - erts_incremental_gc((p),(sz),(objv),(nobj)) : 0), \ - ASSERT_EXPR(global_hend - global_htop > (sz)), \ - global_htop += (sz), global_htop - (sz)) - - -/************************************************************************ - * INC_STORAGE, a dynamic circular storage for objects (INC_Object). * - * Use INC_STORE to add objects to the storage. The storage can then * - * be used either as a queue, using INC_STORAGE_GET to retreive * - * values, or as a stack, using INC_STORAGE_POP. It is OK to mix calls * - * to GET and POP if that is desired. * - * An iterator can be declared to traverse the storage without removing * - * any elements, and INC_STORAGE_STEP will then return each element in * - * turn, oldest first. * - ***********************************************************************/ - -/* Declare a new storage; must be in the beginning of a block. Give - * the storage a name that is used in all later calls to the storage. - * If this is an external declaration of the storage, pass the keyword - * external as the first argument, otherwise leave it empty. - */ -#define INC_STORAGE_DECLARATION(ext,name) \ - ext INC_Storage *name##head; \ - ext INC_Storage *name##tail; \ - ext INC_Object *name##free; \ - ext INC_Object *name##last_free; \ - ext int name##size; - - -/* Initialize the storage. Note that memory allocation is involved - - * don't forget to erase the storage when you are done. - */ -#define INC_STORAGE_INIT(name) do { \ - name##head = (INC_Storage*)erts_alloc(ERTS_ALC_T_OBJECT_STACK, \ - sizeof(INC_Storage)); \ - name##head->next = name##head; \ - name##head->prev = name##head; \ - name##tail = name##head; \ - name##free = name##head->data; \ - name##last_free = name##free + INC_STORAGE_SIZE - 1; \ - name##size = 0; \ -} while(0) - - -/* -#define INC_STORAGE_SWAP(s1,s2) do { \ - INC_Storage *tmphead = s1##head; \ - INC_Storage *tmptail = s1##tail; \ - INC_Object *tmpfree = s1##free; \ - INC_Object *tmplast = s1##last_free; \ - int tmpsize = s1##size; \ - s1##head = s2##head; \ - s1##tail = s2##tail; \ - s1##free = s2##free; \ - s1##last_free = s2##last_free; \ - s1##size = s2##size; \ - s2##head = tmphead; \ - s2##tail = tmptail; \ - s2##free = tmpfree; \ - s2##last_free = tmplast; \ - s2##size = tmpsize; \ -} while(0) -*/ - - -/* Return and remove the youngest element - treat the storage as a - * stack. Always check that there are elements in the queue before - * using INC_STORAGE_POP! - */ -#define INC_STORAGE_POP(name) (ASSERT_EXPR(name##size != 0), \ - name##size--, \ - (--name##free != name##head->data - 1) ? \ - name##free : (name##head = name##head->prev, \ - name##free = name##head->data + INC_STORAGE_SIZE - 1)) - - -/* Return and remove the oldest element - treat the storage as a - * queue. Always check that there are elements in the queue before - * using INC_STORAGE_GET! - */ -#define INC_STORAGE_GET(name) (ASSERT_EXPR(name##size != 0), \ - name##size--, \ - (++name##last_free != name##tail->data + INC_STORAGE_SIZE) ? \ - name##last_free : (name##tail = name##tail->next, \ - name##last_free = name##tail->data)) - - -/* Advance the head to the next free location. If the storage is full, - * a new storage is allocated and linked into the list. - */ -#define INC_STORAGE_NEXT(name) do { \ - if (name##free == name##last_free) { \ - name##tail = (INC_Storage*)erts_alloc(ERTS_ALC_T_OBJECT_STACK, \ - sizeof(INC_Storage)); \ - memcpy(name##tail->data,name##head->data, \ - INC_STORAGE_SIZE * sizeof(INC_Object)); \ - name##tail->next = name##head->next; \ - name##head->next = name##tail; \ - name##tail->prev = name##tail->next->prev; \ - name##tail->next->prev = name##tail; \ - name##last_free = ((void*)name##tail + \ - ((void*)name##last_free - (void*)name##head)); \ - } \ - name##free++; \ - name##size++; \ - if (name##free == name##head->data + INC_STORAGE_SIZE) { \ - name##head = name##head->next; \ - name##free = name##head->data; \ - } \ -} while(0) - - -/* The head of this storage is the next free location. This is where - * the next element will be stored. - */ -#define INC_STORAGE_HEAD(name) (name##free) - - -/* Return the top - the youngest element in the storage. */ -/* #define INC_STORAGE_TOP(name) (name##free - 1 with some magic..) */ - - -/* True if the storage is empty, false otherwise */ -#define INC_STORAGE_EMPTY(name) (name##size == 0) - - -/* Store a new element in the head of the storage and advance the head - * to the next free location. - */ -#define INC_STORE(name,ptr,sz) do { \ - INC_STORAGE_HEAD(name)->this = ptr; \ - INC_STORAGE_HEAD(name)->size = sz; \ - INC_STORAGE_NEXT(name); \ -} while(0) - - -/* An iterator. Use it together with INC_STORAGE_STEP to browse throuh - * the storage. Please note that it is not possible to remove an entry - * in the middle of the storage, use GET or POP to remove enties. - */ -#define INC_STORAGE_ITERATOR(name) \ - INC_Storage *name##iterator_head = name##tail; \ - INC_Object *name##iterator_current = name##last_free; \ - int name##iterator_left = name##size; - - -/* Return the next element in the storage (sorted by age, oldest - * first) or NULL if the storage is empty or the last element has been - * returned already. - */ -#define INC_STORAGE_STEP(name) (name##iterator_left == 0 ? NULL : \ - (name##iterator_left--, \ - (++name##iterator_current != name##iterator_head->data + \ - INC_STORAGE_SIZE) ? name##iterator_current : \ - (name##iterator_head = name##iterator_head->next, \ - name##iterator_current = name##iterator_head->data))) - - -/* Erase the storage. */ -#define INC_STORAGE_ERASE(name)do { \ - name##head->prev->next = NULL; \ - while (name##head != NULL) { \ - name##tail = name##head; \ - name##head = name##head->next; \ - erts_free(ERTS_ALC_T_OBJECT_STACK,(void*)name##tail); \ - } \ - name##tail = NULL; \ - name##free = NULL; \ - name##last_free = NULL; \ - name##size = 0; \ -} while(0) - -/* - * Structures used by the non-moving memory manager - */ - -typedef struct -{ - Eterm *this; - unsigned long size; -} INC_Object; - -typedef struct inc_storage { - struct inc_storage *next; - struct inc_storage *prev; - INC_Object data[INC_STORAGE_SIZE]; -} INC_Storage; - -typedef struct inc_mem_block -{ - unsigned long size; - struct inc_mem_block *prev; - struct inc_mem_block *next; -} INC_MemBlock; - -typedef struct inc_page -{ - struct inc_page *next; - Eterm start[1]; /* Has to be last in struct, this is where the data start */ -} INC_Page; - - -/* - * Heap pointers for the non-moving memory area. - */ -extern INC_Page *inc_used_mem; -extern INC_MemBlock *inc_free_list; -extern unsigned char *blackmap; - -extern Eterm **fwdptrs; -extern Eterm *inc_fromspc; -extern Eterm *inc_fromend; -extern Process *inc_active_proc; -extern Process *inc_active_last; -extern Eterm *inc_alloc_limit; -extern int inc_words_to_go; - -INC_STORAGE_DECLARATION(extern,gray); -INC_STORAGE_DECLARATION(extern,root); - -void erts_init_incgc(void); -void erts_cleanup_incgc(void); -void erts_incremental_gc(Process *p, int sz, Eterm* objv, int nobj); -Eterm *erts_inc_alloc(int need); - -#else -# define INC_STORE(lst,ptr,sz) -# define INC_MARK_FORWARD(ptr) -# define INC_IS_FORWARDED(ptr) -# define INC_FORWARD_VALUE(ptr) -#endif /* INCREMENTAL */ - -#endif /* _ERL_NMGC_H_ */ diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 1481f66b55..c7fd379367 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -849,9 +849,6 @@ static Eterm AM_dist_references; static Eterm AM_node_references; static Eterm AM_system; static Eterm AM_timer; -#ifdef HYBRID -static Eterm AM_processes; -#endif static void setup_reference_table(void); static Eterm reference_table_term(Uint **hpp, Uint *szp); @@ -936,9 +933,6 @@ erts_get_node_and_dist_references(struct process *proc) INIT_AM(node_references); INIT_AM(timer); INIT_AM(system); -#ifdef HYBRID - INIT_AM(processes); -#endif references_atoms_need_init = 0; } @@ -1301,12 +1295,6 @@ setup_reference_table(void) SYSTEM_REF, TUPLE2(&heap[0], AM_system, am_undefined)); -#ifdef HYBRID - /* Insert Heap */ - insert_offheap(&erts_global_offheap, - HEAP_REF, - TUPLE2(&heap[0], AM_processes, am_undefined)); -#endif UnUseTmpHeapNoproc(3); /* Insert all processes */ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 95d408f79d..0fa2def5af 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -28,7 +28,6 @@ #include "erl_vm.h" #include "global.h" #include "erl_process.h" -#include "erl_nmgc.h" #include "error.h" #include "bif.h" #include "erl_db.h" @@ -52,21 +51,22 @@ #define ERTS_SCHED_SPIN_UNTIL_YIELD 100 -#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT 10 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_LONG 40 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_LONG 1000 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_LONG 20 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_LONG 1000 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM 10 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM 1000 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_SHORT 10 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_SHORT 0 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_SHORT 5 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_SHORT 0 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE 0 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_NONE 0 + #define ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT 1000 -#define ERTS_SCHED_TSE_SLEEP_SPINCOUNT \ - (ERTS_SCHED_SYS_SLEEP_SPINCOUNT*ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT) #define ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT 0 -#define ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH (200*CONTEXT_REDS) -#define ERTS_WAKEUP_OTHER_LIMIT_HIGH (50*CONTEXT_REDS) -#define ERTS_WAKEUP_OTHER_LIMIT_MEDIUM (10*CONTEXT_REDS) -#define ERTS_WAKEUP_OTHER_LIMIT_LOW (CONTEXT_REDS) -#define ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW (CONTEXT_REDS/10) - -#define ERTS_WAKEUP_OTHER_DEC 10 -#define ERTS_WAKEUP_OTHER_FIXED_INC (CONTEXT_REDS/10) - #if 0 || defined(DEBUG) #define ERTS_FAKE_SCHED_BIND_PRINT_SORTED_CPU_DATA #endif @@ -123,14 +123,18 @@ Uint erts_no_schedulers; Uint erts_max_processes = ERTS_DEFAULT_MAX_PROCESSES; Uint erts_process_tab_index_mask; -static int wakeup_other_limit; - int erts_sched_thread_suggested_stack_size = -1; #ifdef ERTS_ENABLE_LOCK_CHECK ErtsLcPSDLocks erts_psd_required_locks[ERTS_PSD_SIZE]; #endif +static struct { + int aux_work; + int tse; + int sys_schedule; +} sched_busy_wait; + #ifdef ERTS_SMP int erts_disable_proc_not_running_opt; @@ -240,11 +244,6 @@ struct erts_system_monitor_flags_t erts_system_monitor_flags; Eterm erts_system_profile; struct erts_system_profile_flags_t erts_system_profile_flags; -#ifdef HYBRID -Uint erts_num_active_procs; -Process** erts_active_procs; -#endif - #if ERTS_MAX_PROCESSES > 0x7fffffff #error "Need to store process_count in another type" #endif @@ -360,6 +359,7 @@ dbg_chk_aux_work_val(erts_aint32_t value) valid |= ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN; #endif #ifdef ERTS_SMP + valid |= ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP; valid |= ERTS_SSI_AUX_WORK_MISC_THR_PRGR; valid |= ERTS_SSI_AUX_WORK_DD; valid |= ERTS_SSI_AUX_WORK_DD_THR_PRGR; @@ -467,12 +467,6 @@ erts_init_process(int ncpu) process_tab = (Process**) erts_alloc(ERTS_ALC_T_PROC_TABLE, erts_max_processes*sizeof(Process*)); sys_memzero(process_tab, erts_max_processes * sizeof(Process*)); -#ifdef HYBRID - erts_active_procs = (Process**) - erts_alloc(ERTS_ALC_T_ACTIVE_PROCS, - erts_max_processes * sizeof(Process*)); - erts_num_active_procs = 0; -#endif erts_smp_mtx_init(&proc_tab_mtx, "proc_tab"); p_last = -1; @@ -906,13 +900,13 @@ unset_aux_work_flags(ErtsSchedulerSleepInfo *ssi, erts_aint32_t flgs) #ifdef ERTS_SMP static ERTS_INLINE void -thr_prgr_current_reset(ErtsAuxWorkData *awdp) +haw_thr_prgr_current_reset(ErtsAuxWorkData *awdp) { awdp->current_thr_prgr = ERTS_THR_PRGR_INVALID; } static ERTS_INLINE ErtsThrPrgrVal -thr_prgr_current(ErtsAuxWorkData *awdp) +haw_thr_prgr_current(ErtsAuxWorkData *awdp) { ErtsThrPrgrVal current = awdp->current_thr_prgr; if (current == ERTS_THR_PRGR_INVALID) { @@ -922,6 +916,60 @@ thr_prgr_current(ErtsAuxWorkData *awdp) return current; } +static ERTS_INLINE void +haw_thr_prgr_current_check_progress(ErtsAuxWorkData *awdp) +{ + ErtsThrPrgrVal current = awdp->current_thr_prgr; + if (current != ERTS_THR_PRGR_INVALID + && !erts_thr_progress_equal(current, erts_thr_progress_current())) { + /* + * We have used a previouly read current value that isn't the + * latest; need to poke ourselfs in order to guarantee no loss + * of wakeups. + */ + erts_sched_poke(awdp->ssi); + } +} + +static ERTS_INLINE erts_aint32_t +handle_delayed_aux_work_wakeup(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) +{ + int jix, max_jix; + unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP); + + ERTS_THR_MEMORY_BARRIER; + + max_jix = awdp->delayed_wakeup.jix; + awdp->delayed_wakeup.jix = -1; + for (jix = 0; jix <= max_jix; jix++) { + int sched = awdp->delayed_wakeup.job[jix].sched; + erts_aint32_t aux_work = awdp->delayed_wakeup.job[jix].aux_work; + + ASSERT(awdp->delayed_wakeup.sched2jix[sched] == jix); + awdp->delayed_wakeup.sched2jix[sched] = -1; + set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(sched-1), + aux_work); + } + return aux_work & ~ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP; +} + +static ERTS_INLINE void +schedule_aux_work_wakeup(ErtsAuxWorkData *awdp, int sched, erts_aint32_t aux_work) +{ + int jix = awdp->delayed_wakeup.sched2jix[sched]; + if (jix >= 0) { + ASSERT(awdp->delayed_wakeup.job[jix].sched == sched); + awdp->delayed_wakeup.job[jix].aux_work |= aux_work; + } + else { + jix = ++awdp->delayed_wakeup.jix; + awdp->delayed_wakeup.sched2jix[sched] = jix; + awdp->delayed_wakeup.job[jix].sched = sched; + awdp->delayed_wakeup.job[jix].aux_work = aux_work; + } + set_aux_work_flags_wakeup_nob(awdp->ssi, ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP); +} + #endif typedef struct erts_misc_aux_work_t_ erts_misc_aux_work_t; @@ -1020,7 +1068,7 @@ static ERTS_INLINE erts_aint32_t handle_misc_aux_work_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { - if (!erts_thr_progress_has_reached_this(thr_prgr_current(awdp), + if (!erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp), awdp->misc.thr_prgr)) return aux_work & ~ERTS_SSI_AUX_WORK_MISC_THR_PRGR; @@ -1125,7 +1173,7 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp, #ifdef ERTS_SMP if (awdp->async_ready.need_thr_prgr - && !erts_thr_progress_has_reached_this(thr_prgr_current(awdp), + && !erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp), awdp->async_ready.thr_prgr)) { return aux_work & ~ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN; } @@ -1178,8 +1226,14 @@ handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) void erts_alloc_notify_delayed_dealloc(int ix) { - set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(ix-1), - ERTS_SSI_AUX_WORK_DD); + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + if (esdp) + schedule_aux_work_wakeup(&esdp->aux_work_data, + ix, + ERTS_SSI_AUX_WORK_DD); + else + set_aux_work_flags_wakeup_relb(ERTS_SCHED_SLEEP_INFO_IX(ix-1), + ERTS_SSI_AUX_WORK_DD); } static ERTS_INLINE erts_aint32_t @@ -1206,7 +1260,7 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) if (need_thr_progress) { if (wakeup == ERTS_THR_PRGR_INVALID) - wakeup = erts_thr_progress_later_than(thr_prgr_current(awdp)); + wakeup = erts_thr_progress_later(awdp->esdp); awdp->dd.thr_prgr = wakeup; set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD_THR_PRGR); awdp->dd.thr_prgr = wakeup; @@ -1227,7 +1281,7 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) int need_thr_progress; int more_work; ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID; - ErtsThrPrgrVal current = thr_prgr_current(awdp); + ErtsThrPrgrVal current = haw_thr_prgr_current(awdp); if (!erts_thr_progress_has_reached_this(current, awdp->dd.thr_prgr)) return aux_work & ~ERTS_SSI_AUX_WORK_DD_THR_PRGR; @@ -1249,7 +1303,7 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) if (need_thr_progress) { if (wakeup == ERTS_THR_PRGR_INVALID) - wakeup = erts_thr_progress_later_than(current); + wakeup = erts_thr_progress_later(awdp->esdp); awdp->dd.thr_prgr = wakeup; erts_thr_progress_wakeup(awdp->esdp, wakeup); } @@ -1438,7 +1492,7 @@ handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) } static erts_aint32_t -handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) +handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting) { #undef HANDLE_AUX_WORK #define HANDLE_AUX_WORK(FLG, HNDLR) \ @@ -1456,7 +1510,7 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) erts_aint32_t ignore = 0; #ifdef ERTS_SMP - thr_prgr_current_reset(awdp); + haw_thr_prgr_current_reset(awdp); #endif ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); @@ -1477,6 +1531,8 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) * eachother. Most frequent first. */ #ifdef ERTS_SMP + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP, + handle_delayed_aux_work_wakeup); HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DD, handle_delayed_dealloc); /* DD must be before DD_THR_PRGR */ @@ -1522,6 +1578,11 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); +#ifdef ERTS_SMP + if (waiting && !aux_work) + haw_thr_prgr_current_check_progress(awdp); +#endif + return aux_work; #undef HANDLE_AUX_WORK @@ -1950,7 +2011,7 @@ thr_prgr_fin_wait(void *vssi) | ERTS_SSI_FLG_TSE_SLEEPING)); } -static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp); +static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp); static void * aux_thread(void *unused) @@ -1970,7 +2031,7 @@ aux_thread(void *unused) callbacks.finalize_wait = thr_prgr_fin_wait; erts_thr_progress_register_managed_thread(NULL, &callbacks, 1); - init_aux_work_data(awdp, NULL); + init_aux_work_data(awdp, NULL, NULL); awdp->ssi = ssi; sched_prep_spin_wait(ssi); @@ -1982,7 +2043,7 @@ aux_thread(void *unused) if (aux_work) { if (!thr_prgr_active) erts_thr_progress_active(NULL, thr_prgr_active = 1); - aux_work = handle_aux_work(awdp, aux_work); + aux_work = handle_aux_work(awdp, aux_work, 1); if (aux_work && erts_thr_progress_update(NULL)) erts_thr_progress_leader_update(NULL); } @@ -2046,7 +2107,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_smp_runq_unlock(rq); - spincount = ERTS_SCHED_TSE_SLEEP_SPINCOUNT; + spincount = sched_busy_wait.tse; tse_wait: @@ -2061,7 +2122,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } - aux_work = handle_aux_work(&esdp->aux_work_data, aux_work); + aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); } @@ -2097,7 +2158,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } flgs = sched_prep_cont_spin_wait(ssi); - spincount = ERTS_SCHED_TSE_SLEEP_SPINCOUNT; + spincount = sched_busy_wait.aux_work; if (!(flgs & ERTS_SSI_FLG_WAITING)) { ASSERT(!(flgs & ERTS_SSI_FLG_SLEEPING)); @@ -2134,7 +2195,9 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ASSERT(working); sched_wall_time_change(esdp, working = 0); - spincount = ERTS_SCHED_SYS_SLEEP_SPINCOUNT; + spincount = sched_busy_wait.sys_schedule; + if (spincount == 0) + goto sys_aux_work; while (spincount-- > 0) { @@ -2163,7 +2226,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) if (!thr_prgr_active) erts_thr_progress_active(esdp, thr_prgr_active = 1); #endif - aux_work = handle_aux_work(&esdp->aux_work_data, aux_work); + aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); #ifdef ERTS_SMP if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); @@ -3560,33 +3623,282 @@ erts_debug_nbalance(void) #endif } +/* Wakeup other schedulers */ + +typedef enum { + ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH, + ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH, + ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM, + ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW, + ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW +} ErtsSchedWakeupOtherThreshold; + +typedef enum { + ERTS_SCHED_WAKEUP_OTHER_TYPE_PROPOSAL, + ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY +} ErtsSchedWakeupOtherType; + +/* First proposal */ + +#define ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH (200*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_HIGH (50*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_MEDIUM (10*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_LOW (CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW (CONTEXT_REDS/10) + +#define ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_HIGH 3 +#define ERTS_WAKEUP_OTHER_DEC_SHIFT_HIGH 1 +#define ERTS_WAKEUP_OTHER_DEC_SHIFT_MEDIUM 0 +#define ERTS_WAKEUP_OTHER_DEC_SHIFT_LOW -2 +#define ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_LOW -5 + +#define ERTS_WAKEUP_OTHER_DEC_SHIFT 2 +#define ERTS_WAKEUP_OTHER_FIXED_INC (CONTEXT_REDS/10) + +/* To be legacy */ + +#define ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH_LEGACY (200*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_HIGH_LEGACY (50*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_MEDIUM_LEGACY (10*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_LOW_LEGACY (CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW_LEGACY (CONTEXT_REDS/10) + +#define ERTS_WAKEUP_OTHER_DEC_LEGACY 10 +#define ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY (CONTEXT_REDS/10) + +#ifdef ERTS_SMP + +static struct { + ErtsSchedWakeupOtherThreshold threshold; + ErtsSchedWakeupOtherType type; + int limit; + int dec_shift; + int dec_mask; + void (*check)(ErtsRunQueue *rq); +} wakeup_other; + +static void +wakeup_other_check(ErtsRunQueue *rq) +{ + int wo_reds = rq->wakeup_other_reds; + if (wo_reds) { + int left_len = rq->len - 1; + if (left_len < 1) { + int wo_reduce = wo_reds << wakeup_other.dec_shift; + wo_reduce &= wakeup_other.dec_mask; + rq->wakeup_other -= wo_reduce; + if (rq->wakeup_other < 0) + rq->wakeup_other = 0; + } + else { + rq->wakeup_other += (left_len*wo_reds + + ERTS_WAKEUP_OTHER_FIXED_INC); + if (rq->wakeup_other > wakeup_other.limit) { + int empty_rqs = + erts_smp_atomic32_read_acqb(&no_empty_run_queues); + if (empty_rqs != 0) + wake_scheduler_on_empty_runq(rq); + rq->wakeup_other = 0; + } + } + rq->wakeup_other_reds = 0; + } +} + +static void +wakeup_other_set_limit(void) +{ + switch (wakeup_other.threshold) { + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH; + wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_HIGH; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH; + wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_HIGH; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM; + wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_MEDIUM; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_LOW; + wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_LOW; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW; + wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_LOW; + break; + } + if (wakeup_other.dec_shift < 0) + wakeup_other.dec_mask = (1 << (sizeof(wakeup_other.dec_mask)*8 + + wakeup_other.dec_shift)) - 1; + else { + wakeup_other.dec_mask = 0; + wakeup_other.dec_mask = ~wakeup_other.dec_mask; + } +} + +static void +wakeup_other_check_legacy(ErtsRunQueue *rq) +{ + int wo_reds = rq->wakeup_other_reds; + if (wo_reds) { + if (rq->len < 2) { + rq->wakeup_other -= ERTS_WAKEUP_OTHER_DEC_LEGACY*wo_reds; + if (rq->wakeup_other < 0) + rq->wakeup_other = 0; + } + else if (rq->wakeup_other < wakeup_other.limit) + rq->wakeup_other += rq->len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY; + else { + if (erts_smp_atomic32_read_acqb(&no_empty_run_queues) != 0) { + wake_scheduler_on_empty_runq(rq); + rq->wakeup_other = 0; + } + rq->wakeup_other = 0; + } + } + rq->wakeup_other_reds = 0; +} + +static void +wakeup_other_set_limit_legacy(void) +{ + switch (wakeup_other.threshold) { + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH_LEGACY; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH_LEGACY; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM_LEGACY; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_LOW_LEGACY; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW_LEGACY; + break; + } +} + +static void +set_wakeup_other_data(void) +{ + switch (wakeup_other.type) { + case ERTS_SCHED_WAKEUP_OTHER_TYPE_PROPOSAL: + wakeup_other.check = wakeup_other_check; + wakeup_other_set_limit(); + break; + case ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY: + wakeup_other.check = wakeup_other_check_legacy; + wakeup_other_set_limit_legacy(); + break; + } +} + +#endif + void erts_early_init_scheduling(int no_schedulers) { aux_work_timeout_early_init(no_schedulers); - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM; +#ifdef ERTS_SMP + wakeup_other.threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM; + wakeup_other.type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY; +#endif + sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM; + sched_busy_wait.tse = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM + * ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT); + sched_busy_wait.aux_work = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM + * ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM); } int -erts_sched_set_wakeup_limit(char *str) +erts_sched_set_wakeup_other_thresold(char *str) { + ErtsSchedWakeupOtherThreshold threshold; if (sys_strcmp(str, "very_high") == 0) - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH; + threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH; else if (sys_strcmp(str, "high") == 0) - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH; + threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH; else if (sys_strcmp(str, "medium") == 0) - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM; + threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM; else if (sys_strcmp(str, "low") == 0) - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_LOW; + threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW; else if (sys_strcmp(str, "very_low") == 0) - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW; + threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW; else return EINVAL; +#ifdef ERTS_SMP + wakeup_other.threshold = threshold; + set_wakeup_other_data(); +#endif return 0; } +int +erts_sched_set_wakeup_other_type(char *str) +{ + ErtsSchedWakeupOtherType type; + if (sys_strcmp(str, "proposal") == 0) + type = ERTS_SCHED_WAKEUP_OTHER_TYPE_PROPOSAL; + else if (sys_strcmp(str, "default") == 0) + type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY; + else if (sys_strcmp(str, "legacy") == 0) + type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY; + else + return EINVAL; +#ifdef ERTS_SMP + wakeup_other.type = type; +#endif + return 0; +} + +int +erts_sched_set_busy_wait_threshold(char *str) +{ + int sys_sched; + int aux_work_fact; + + if (sys_strcmp(str, "very_long") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_LONG; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_LONG; + } + else if (sys_strcmp(str, "long") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_LONG; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_LONG; + } + else if (sys_strcmp(str, "medium") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM; + } + else if (sys_strcmp(str, "short") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_SHORT; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_SHORT; + } + else if (sys_strcmp(str, "very_short") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_SHORT; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_SHORT; + } + else if (sys_strcmp(str, "none") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_NONE; + } + else { + return EINVAL; + } + + sched_busy_wait.sys_schedule = sys_sched; + sched_busy_wait.tse = sys_sched*ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT; + sched_busy_wait.aux_work = sys_sched*aux_work_fact; + + return 0; +} static void -init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp) +init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp) { awdp->sched_id = esdp ? (int) esdp->no : 0; awdp->esdp = esdp; @@ -3604,15 +3916,39 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp) #endif awdp->async_ready.queue = NULL; #endif +#ifdef ERTS_SMP + if (!dawwp) { + awdp->delayed_wakeup.job = NULL; + awdp->delayed_wakeup.sched2jix = NULL; + awdp->delayed_wakeup.jix = -1; + } + else { + int i; + awdp->delayed_wakeup.job = (ErtsDelayedAuxWorkWakeupJob *) dawwp; + dawwp += sizeof(ErtsDelayedAuxWorkWakeupJob)*(erts_no_schedulers+1); + awdp->delayed_wakeup.sched2jix = (int *) dawwp; + awdp->delayed_wakeup.jix = -1; + for (i = 0; i <= erts_no_schedulers; i++) + awdp->delayed_wakeup.sched2jix[i] = -1; + } +#endif } void erts_init_scheduling(int no_schedulers, int no_schedulers_online) { int ix, n, no_ssi; + char *daww_ptr; +#ifdef ERTS_SMP + size_t daww_sz; +#endif init_misc_op_list_alloc(); +#ifdef ERTS_SMP + set_wakeup_other_data(); +#endif + ASSERT(no_schedulers_online <= no_schedulers); ASSERT(no_schedulers_online >= 1); ASSERT(no_schedulers >= 1); @@ -3738,6 +4074,15 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) /* Create and initialize scheduler specific data */ +#ifdef ERTS_SMP + daww_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE((sizeof(ErtsDelayedAuxWorkWakeupJob) + + sizeof(int))*(n+1)); + daww_ptr = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA, + daww_sz*n); +#else + daww_ptr = NULL; +#endif + erts_aligned_scheduler_data = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA, n*sizeof(ErtsAlignedSchedulerData)); @@ -3772,7 +4117,10 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) esdp->run_queue = ERTS_RUNQ_IX(ix); esdp->run_queue->scheduler = esdp; - init_aux_work_data(&esdp->aux_work_data, esdp); + init_aux_work_data(&esdp->aux_work_data, esdp, daww_ptr); +#ifdef ERTS_SMP + daww_ptr += daww_sz; +#endif init_sched_wall_time(&esdp->sched_wall_time); } @@ -4170,7 +4518,9 @@ suspend_scheduler(ErtsSchedulerData *esdp) erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } - aux_work = handle_aux_work(&esdp->aux_work_data, aux_work); + aux_work = handle_aux_work(&esdp->aux_work_data, + aux_work, + 1); if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); } @@ -6412,7 +6762,7 @@ Process *schedule(Process *p, int calls) if (leader_update) erts_thr_progress_leader_update(esdp); if (aux_work) - handle_aux_work(&esdp->aux_work_data, aux_work); + handle_aux_work(&esdp->aux_work_data, aux_work, 0); erts_smp_runq_lock(rq); } } @@ -6425,7 +6775,7 @@ Process *schedule(Process *p, int calls) erts_aint32_t aux_work; aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work); if (aux_work) - handle_aux_work(&esdp->aux_work_data, aux_work); + handle_aux_work(&esdp->aux_work_data, aux_work, 0); } #endif /* ERTS_SMP */ @@ -6502,26 +6852,7 @@ Process *schedule(Process *p, int calls) exec_misc_ops(rq); #ifdef ERTS_SMP - { - int wo_reds = rq->wakeup_other_reds; - if (wo_reds) { - if (rq->len < 2) { - rq->wakeup_other -= ERTS_WAKEUP_OTHER_DEC*wo_reds; - if (rq->wakeup_other < 0) - rq->wakeup_other = 0; - } - else if (rq->wakeup_other < wakeup_other_limit) - rq->wakeup_other += rq->len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC; - else { - if (erts_smp_atomic32_read_acqb(&no_empty_run_queues) != 0) { - wake_scheduler_on_empty_runq(rq); - rq->wakeup_other = 0; - } - rq->wakeup_other = 0; - } - } - rq->wakeup_other_reds = 0; - } + wakeup_other.check(rq); #endif /* @@ -6685,7 +7016,6 @@ Process *schedule(Process *p, int calls) #endif ASSERT(p->status != P_SUSPENDED); /* Never run a suspended process */ - ACTIVATE(p); reds = context_reds; if (IS_TRACED(p)) { @@ -6726,7 +7056,6 @@ Process *schedule(Process *p, int calls) } p->fcalls = reds; - ASSERT(IS_ACTIVE(p)); ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); return p; } @@ -7074,9 +7403,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). ErtsRunQueue *rq, *notify_runq; Process *p; Sint arity; /* Number of arguments. */ -#ifndef HYBRID Uint arg_size; /* Size of arguments. */ -#endif Uint sz; /* Needed words on heap. */ Uint heap_need; /* Size needed on heap. */ Eterm res = THE_NON_VALUE; @@ -7085,17 +7412,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). erts_smp_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR); #endif -#ifdef HYBRID - /* - * Copy the arguments to the global heap - * Since global GC might occur we want to do this before adding the - * new process to the process_tab. - */ - BM_SWAP_TIMER(system,copy); - LAZY_COPY(parent,args); - BM_SWAP_TIMER(copy,system); - heap_need = 0; -#endif /* HYBRID */ /* * Check for errors. */ @@ -7118,12 +7434,10 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). #endif BM_COUNT(processes_spawned); -#ifndef HYBRID BM_SWAP_TIMER(system,size); arg_size = size_object(args); BM_SWAP_TIMER(size,system); heap_need = arg_size; -#endif p->flags = erts_default_process_flags; @@ -7174,9 +7488,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*sz); p->old_hend = p->old_htop = p->old_heap = NULL; p->high_water = p->heap; -#ifdef INCREMENTAL - p->scan_top = p->high_water; -#endif p->gen_gcs = 0; p->stop = p->hend = p->heap + sz; p->htop = p->heap; @@ -7202,19 +7513,10 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). BM_STOP_TIMER(system); BM_MESSAGE(args,p,parent); BM_START_TIMER(system); -#ifdef HYBRID - p->arg_reg[2] = args; -#ifdef INCREMENTAL - p->active = 0; - if (ptr_val(args) >= inc_fromspc && ptr_val(args) < inc_fromend) - INC_ACTIVATE(p); -#endif -#else BM_SWAP_TIMER(system,copy); p->arg_reg[2] = copy_struct(args, arg_size, &p->htop, &p->off_heap); BM_MESSAGE_COPIED(arg_size); BM_SWAP_TIMER(copy,system); -#endif p->arity = 3; p->fvalue = NIL; @@ -7272,13 +7574,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). #endif p->parent = parent->id == ERTS_INVALID_PID ? NIL : parent->id; -#ifdef HYBRID - p->rrma = NULL; - p->rrsrc = NULL; - p->nrr = 0; - p->rrsz = 0; -#endif - INIT_HOLE_CHECK(p); #ifdef DEBUG p->last_old_htop = NULL; @@ -7347,15 +7642,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). so->mref = mref; } -#ifdef HYBRID - /* - * Add process to the array of active processes. - */ - ACTIVATE(p); - p->active_index = erts_num_active_procs++; - erts_active_procs[p->active_index] = p; -#endif - #ifdef ERTS_SMP p->scheduler_data = NULL; p->is_exiting = 0; @@ -7466,9 +7752,6 @@ void erts_init_empty_process(Process *p) p->reg = NULL; p->heap_sz = 0; p->high_water = NULL; -#ifdef INCREMENTAL - p->scan_top = NULL; -#endif p->old_hend = NULL; p->old_htop = NULL; p->old_heap = NULL; @@ -7520,14 +7803,6 @@ void erts_init_empty_process(Process *p) #endif #endif - ACTIVATE(p); - -#ifdef HYBRID - p->rrma = NULL; - p->rrsrc = NULL; - p->nrr = 0; - p->rrsz = 0; -#endif INIT_HOLE_CHECK(p); #ifdef DEBUG p->last_old_htop = NULL; @@ -7575,9 +7850,6 @@ erts_debug_verify_clean_empty_process(Process* p) ASSERT(p->reg == NULL); ASSERT(p->heap_sz == 0); ASSERT(p->high_water == NULL); -#ifdef INCREMENTAL - ASSERT(p->scan_top == NULL); -#endif ASSERT(p->old_hend == NULL); ASSERT(p->old_htop == NULL); ASSERT(p->old_heap == NULL); @@ -7725,22 +7997,6 @@ delete_process(Process* p) ASSERT(!p->suspend_monitors); p->fvalue = NIL; - -#ifdef HYBRID - erts_active_procs[p->active_index] = - erts_active_procs[--erts_num_active_procs]; - erts_active_procs[p->active_index]->active_index = p->active_index; -#ifdef INCREMENTAL - if (INC_IS_ACTIVE(p)) - INC_DEACTIVATE(p); -#endif - - if (p->rrma != NULL) { - erts_free(ERTS_ALC_T_ROOTSET,p->rrma); - erts_free(ERTS_ALC_T_ROOTSET,p->rrsrc); - } -#endif - } static ERTS_INLINE void @@ -8112,7 +8368,6 @@ send_exit_signal(Process *c_p, /* current process if and only set_proc_exiting(rp, is_immed(rsn) ? rsn : copy_object(rsn, rp), NULL); - ACTIVATE(rp); if (old_status != P_RUNABLE && old_status != P_RUNNING) erts_add_to_runq(rp); } diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index cff0783bc4..9e7a5a5c74 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -253,18 +253,19 @@ typedef enum { * eachother. Most frequent - lowest bit number. */ -#define ERTS_SSI_AUX_WORK_DD (((erts_aint32_t) 1) << 0) -#define ERTS_SSI_AUX_WORK_DD_THR_PRGR (((erts_aint32_t) 1) << 1) -#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC (((erts_aint32_t) 1) << 2) -#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM (((erts_aint32_t) 1) << 3) -#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 4) -#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 5) -#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 6) -#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 7) -#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 8) -#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 9) -#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 10) -#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 11) +#define ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP (((erts_aint32_t) 1) << 0) +#define ERTS_SSI_AUX_WORK_DD (((erts_aint32_t) 1) << 1) +#define ERTS_SSI_AUX_WORK_DD_THR_PRGR (((erts_aint32_t) 1) << 2) +#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC (((erts_aint32_t) 1) << 3) +#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM (((erts_aint32_t) 1) << 4) +#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 5) +#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 6) +#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 7) +#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 8) +#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 9) +#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 10) +#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 11) +#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 12) typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; @@ -403,6 +404,11 @@ typedef struct { } ErtsSchedWallTime; typedef struct { + int sched; + erts_aint32_t aux_work; +} ErtsDelayedAuxWorkWakeupJob; + +typedef struct { int sched_id; ErtsSchedulerData *esdp; ErtsSchedulerSleepInfo *ssi; @@ -431,6 +437,13 @@ typedef struct { void *queue; } async_ready; #endif +#ifdef ERTS_SMP + struct { + int *sched2jix; + int jix; + ErtsDelayedAuxWorkWakeupJob *job; + } delayed_wakeup; +#endif } ErtsAuxWorkData; struct ErtsSchedulerData_ { @@ -464,7 +477,6 @@ struct ErtsSchedulerData_ { int virtual_reds; int cpu_id; /* >= 0 when bound */ ErtsAuxWorkData aux_work_data; - ErtsAtomCacheMap atom_cache_map; ErtsSchedAllocData alloc_data; @@ -749,24 +761,6 @@ struct process { #endif #endif -#ifdef HYBRID - Eterm *rrma; /* Remembered roots to Message Area */ - Eterm **rrsrc; /* The source of the root */ - Uint nrr; /* Number of remembered roots */ - Uint rrsz; /* Size of root array */ -#endif - -#ifdef HYBRID - Uint active; /* Active since last major collection? */ - Uint active_index; /* Index in the active process array */ -#endif - -#ifdef INCREMENTAL - Process *active_next; /* Active processes to scan for roots */ - Process *active_prev; /* in collection of the message area */ - Eterm *scan_top; -#endif - #ifdef CHECK_FOR_HOLES Eterm* last_htop; /* No need to scan the heap below this point. */ ErlHeapFragment* last_mbuf; /* No need to scan beyond this mbuf. */ @@ -888,10 +882,6 @@ Eterm* erts_set_hole_marker(Eterm* ptr, Uint sz); #endif extern Process** process_tab; -#ifdef HYBRID -extern Uint erts_num_active_procs; -extern Process** erts_active_procs; -#endif extern Uint erts_max_processes; extern Uint erts_process_tab_index_mask; extern Uint erts_default_process_flags; @@ -1096,7 +1086,9 @@ ErtsProcList *erts_proclist_create(Process *); void erts_proclist_destroy(ErtsProcList *); int erts_proclist_same(ErtsProcList *, Process *); -int erts_sched_set_wakeup_limit(char *str); +int erts_sched_set_wakeup_other_thresold(char *str); +int erts_sched_set_wakeup_other_type(char *str); +int erts_sched_set_busy_wait_threshold(char *str); #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) int erts_dbg_check_halloc_lock(Process *p); @@ -1400,10 +1392,14 @@ ERTS_GLB_INLINE Eterm erts_get_current_pid(void); ERTS_GLB_INLINE Uint erts_get_scheduler_id(void); ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_proc(Process *p); ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_current(ErtsSchedulerData *esdp); +#ifndef ERTS_ENABLE_LOCK_COUNT ERTS_GLB_INLINE void erts_smp_runq_lock(ErtsRunQueue *rq); +#endif ERTS_GLB_INLINE int erts_smp_runq_trylock(ErtsRunQueue *rq); ERTS_GLB_INLINE void erts_smp_runq_unlock(ErtsRunQueue *rq); +#ifndef ERTS_ENABLE_LOCK_COUNT ERTS_GLB_INLINE void erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq); +#endif ERTS_GLB_INLINE void erts_smp_xrunq_unlock(ErtsRunQueue *rq, ErtsRunQueue *xrq); ERTS_GLB_INLINE void erts_smp_runqs_lock(ErtsRunQueue *rq1, ErtsRunQueue *rq2); ERTS_GLB_INLINE void erts_smp_runqs_unlock(ErtsRunQueue *rq1, ErtsRunQueue *rq2); @@ -1492,6 +1488,12 @@ erts_get_runq_current(ErtsSchedulerData *esdp) #endif } +#ifdef ERTS_ENABLE_LOCK_COUNT + +#define erts_smp_runq_lock(rq) erts_smp_mtx_lock_x(&(rq)->mtx, __FILE__, __LINE__) + +#else + ERTS_GLB_INLINE void erts_smp_runq_lock(ErtsRunQueue *rq) { @@ -1500,6 +1502,8 @@ erts_smp_runq_lock(ErtsRunQueue *rq) #endif } +#endif + ERTS_GLB_INLINE int erts_smp_runq_trylock(ErtsRunQueue *rq) { @@ -1518,6 +1522,31 @@ erts_smp_runq_unlock(ErtsRunQueue *rq) #endif } +#ifdef ERTS_ENABLE_LOCK_COUNT + +#define erts_smp_xrunq_lock(rq, xrq) erts_smp_xrunq_lock_x((rq), (xrq), __FILE__, __LINE__) + +ERTS_GLB_INLINE void +erts_smp_xrunq_lock_x(ErtsRunQueue *rq, ErtsRunQueue *xrq, char* file, int line) +{ +#ifdef ERTS_SMP + ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&rq->mtx)); + if (xrq != rq) { + if (erts_smp_mtx_trylock(&xrq->mtx) == EBUSY) { + if (rq < xrq) + erts_smp_mtx_lock_x(&xrq->mtx, file, line); + else { + erts_smp_mtx_unlock(&rq->mtx); + erts_smp_mtx_lock_x(&xrq->mtx, file, line); + erts_smp_mtx_lock_x(&rq->mtx, file, line); + } + } + } +#endif +} + +#else + ERTS_GLB_INLINE void erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq) { @@ -1537,6 +1566,8 @@ erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq) #endif } +#endif + ERTS_GLB_INLINE void erts_smp_xrunq_unlock(ErtsRunQueue *rq, ErtsRunQueue *xrq) { diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index a5a753b798..34d591df40 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -90,16 +90,6 @@ static void check_queue(erts_proc_lock_t *lck); #error "The size of the 'uflgs' field of the erts_tse_t type is too small" #endif -struct erts_proc_lock_queues_t_ { - erts_proc_lock_queues_t *next; - erts_tse_t *queue[ERTS_PROC_LOCK_MAX_BIT+1]; -}; - -static erts_proc_lock_queues_t zeroqs = {0}; - -static erts_smp_spinlock_t qs_lock; -static erts_proc_lock_queues_t *queue_free_list; - #ifdef ERTS_ENABLE_LOCK_CHECK static struct { Sint16 proc_lock_main; @@ -120,7 +110,6 @@ void erts_init_proc_lock(int cpus) { int i; - erts_smp_spinlock_init(&qs_lock, "proc_lck_qs_alloc"); for (i = 0; i < ERTS_NO_OF_PIX_LOCKS; i++) { #ifdef ERTS_ENABLE_LOCK_COUNT erts_mtx_init_x(&erts_pix_locks[i].u.mtx, @@ -129,7 +118,6 @@ erts_init_proc_lock(int cpus) erts_mtx_init(&erts_pix_locks[i].u.mtx, "pix_lock"); #endif } - queue_free_list = NULL; erts_thr_install_exit_handler(cleanup_tse); #ifdef ERTS_ENABLE_LOCK_CHECK lc_id.proc_lock_main = erts_lc_get_lock_order_id("proc_main"); @@ -156,16 +144,7 @@ erts_init_proc_lock(int cpus) } #ifdef ERTS_ENABLE_LOCK_CHECK -static void -check_unused_tse(erts_tse_t *wtr) -{ - int i; - erts_proc_lock_queues_t *queues = wtr->udata; - ERTS_LC_ASSERT(wtr->uflgs == 0); - for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) - ERTS_LC_ASSERT(!queues->queue[i]); -} -#define CHECK_UNUSED_TSE(W) check_unused_tse((W)) +#define CHECK_UNUSED_TSE(W) ERTS_LC_ASSERT((W)->uflgs == 0) #else #define CHECK_UNUSED_TSE(W) #endif @@ -174,56 +153,21 @@ static ERTS_INLINE erts_tse_t * tse_fetch(erts_pix_lock_t *pix_lock) { erts_tse_t *tse = erts_tse_fetch(); - if (!tse->udata) { - erts_proc_lock_queues_t *qs; -#if ERTS_PROC_LOCK_SPINLOCK_IMPL && !ERTS_PROC_LOCK_ATOMIC_IMPL - if (pix_lock) - erts_pix_unlock(pix_lock); -#endif - erts_smp_spin_lock(&qs_lock); - qs = queue_free_list; - if (qs) { - queue_free_list = queue_free_list->next; - erts_smp_spin_unlock(&qs_lock); - } - else { - erts_smp_spin_unlock(&qs_lock); - qs = erts_alloc(ERTS_ALC_T_PROC_LCK_QS, - sizeof(erts_proc_lock_queues_t)); - sys_memcpy((void *) qs, - (void *) &zeroqs, - sizeof(erts_proc_lock_queues_t)); - } - tse->udata = qs; -#if ERTS_PROC_LOCK_SPINLOCK_IMPL && !ERTS_PROC_LOCK_ATOMIC_IMPL - if (pix_lock) - erts_pix_lock(pix_lock); -#endif - } tse->uflgs = 0; return tse; } static ERTS_INLINE void -tse_return(erts_tse_t *tse, int force_free_q) +tse_return(erts_tse_t *tse) { CHECK_UNUSED_TSE(tse); - if (force_free_q || erts_tse_is_tmp(tse)) { - erts_proc_lock_queues_t *qs = tse->udata; - ASSERT(qs); - erts_smp_spin_lock(&qs_lock); - qs->next = queue_free_list; - queue_free_list = qs; - erts_smp_spin_unlock(&qs_lock); - tse->udata = NULL; - } erts_tse_return(tse); } void erts_proc_lock_prepare_proc_lock_waiter(void) { - tse_return(tse_fetch(NULL), 0); + tse_return(tse_fetch(NULL)); } @@ -231,55 +175,49 @@ static void cleanup_tse(void) { erts_tse_t *tse = erts_tse_fetch(); - if (tse) { - if (tse->udata) - tse_return(tse, 1); - else - erts_tse_return(tse); - } + if (tse) + erts_tse_return(tse); } /* * Waiters are queued in a circular double linked list; - * where qs->queue[lock_ix] is the first waiter in queue, and - * qs->queue[lock_ix]->prev is the last waiter in queue. + * where lck->queue[lock_ix] is the first waiter in queue, and + * lck->queue[lock_ix]->prev is the last waiter in queue. */ static ERTS_INLINE void -enqueue_waiter(erts_proc_lock_queues_t *qs, - int ix, - erts_tse_t *wtr) +enqueue_waiter(erts_proc_lock_t *lck, int ix, erts_tse_t *wtr) { - if (!qs->queue[ix]) { - qs->queue[ix] = wtr; + if (!lck->queue[ix]) { + lck->queue[ix] = wtr; wtr->next = wtr; wtr->prev = wtr; } else { - ERTS_LC_ASSERT(qs->queue[ix]->next && qs->queue[ix]->prev); - wtr->next = qs->queue[ix]; - wtr->prev = qs->queue[ix]->prev; + ERTS_LC_ASSERT(lck->queue[ix]->next && lck->queue[ix]->prev); + wtr->next = lck->queue[ix]; + wtr->prev = lck->queue[ix]->prev; wtr->prev->next = wtr; - qs->queue[ix]->prev = wtr; + lck->queue[ix]->prev = wtr; } } static erts_tse_t * -dequeue_waiter(erts_proc_lock_queues_t *qs, int ix) +dequeue_waiter(erts_proc_lock_t *lck, int ix) { - erts_tse_t *wtr = qs->queue[ix]; - ERTS_LC_ASSERT(qs->queue[ix]); + erts_tse_t *wtr = lck->queue[ix]; + ERTS_LC_ASSERT(lck->queue[ix]); if (wtr->next == wtr) { - ERTS_LC_ASSERT(qs->queue[ix]->prev == wtr); - qs->queue[ix] = NULL; + ERTS_LC_ASSERT(lck->queue[ix]->prev == wtr); + lck->queue[ix] = NULL; } else { ERTS_LC_ASSERT(wtr->next != wtr); ERTS_LC_ASSERT(wtr->prev != wtr); wtr->next->prev = wtr->prev; wtr->prev->next = wtr->next; - qs->queue[ix] = wtr->next; + lck->queue[ix] = wtr->next; } return wtr; } @@ -300,19 +238,18 @@ try_aquire(erts_proc_lock_t *lck, erts_tse_t *wtr) ErtsProcLocks locks = wtr->uflgs; int lock_no; - ERTS_LC_ASSERT(lck->queues); ERTS_LC_ASSERT(got_locks != locks); for (lock_no = 0; lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) { ErtsProcLocks lock = ((ErtsProcLocks) 1) << lock_no; if (locks & lock) { ErtsProcLocks wflg, old_lflgs; - if (lck->queues->queue[lock_no]) { + if (lck->queue[lock_no]) { /* Others already waiting */ enqueue: ERTS_LC_ASSERT(ERTS_PROC_LOCK_FLGS_READ_(lck) & (lock << ERTS_PROC_LOCK_WAITER_SHIFT)); - enqueue_waiter(lck->queues, lock_no, wtr); + enqueue_waiter(lck, lock_no, wtr); break; } wflg = lock << ERTS_PROC_LOCK_WAITER_SHIFT; @@ -364,7 +301,6 @@ transfer_locks(Process *p, for (lock_no = 0; tlocks && lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) { ErtsProcLocks lock = ((ErtsProcLocks) 1) << lock_no; if (tlocks & lock) { - erts_proc_lock_queues_t *qs = p->lock.queues; /* Transfer lock */ #ifdef ERTS_ENABLE_LOCK_CHECK tlocks &= ~lock; @@ -372,9 +308,9 @@ transfer_locks(Process *p, ERTS_LC_ASSERT(ERTS_PROC_LOCK_FLGS_READ_(&p->lock) & (lock << ERTS_PROC_LOCK_WAITER_SHIFT)); transferred++; - wtr = dequeue_waiter(qs, lock_no); + wtr = dequeue_waiter(&p->lock, lock_no); ERTS_LC_ASSERT(wtr); - if (!qs->queue[lock_no]) + if (!p->lock.queue[lock_no]) unset_waiter |= lock; ERTS_LC_ASSERT(wtr->uflgs & lock); wtr->uflgs &= ~lock; @@ -463,7 +399,6 @@ wait_for_locks(Process *p, { erts_pix_lock_t *pix_lock = pixlck ? pixlck : ERTS_PID2PIXLOCK(p->id); erts_tse_t *wtr; - erts_proc_lock_queues_t *qs; /* Acquire a waiter object on which this thread can wait. */ wtr = tse_fetch(pix_lock); @@ -479,18 +414,6 @@ wait_for_locks(Process *p, ERTS_LC_ASSERT(erts_lc_pix_lock_is_locked(pix_lock)); - qs = wtr->udata; - ASSERT(qs); - /* Provide the process with waiter queues, if it doesn't have one. */ - if (!p->lock.queues) { - qs->next = NULL; - p->lock.queues = qs; - } - else { - qs->next = p->lock.queues->next; - p->lock.queues->next = qs; - } - #ifdef ERTS_PROC_LOCK_HARD_DEBUG check_queue(&p->lock); #endif @@ -504,7 +427,9 @@ wait_for_locks(Process *p, check_queue(&p->lock); #endif - if (wtr->uflgs) { + if (wtr->uflgs == 0) + erts_pix_unlock(pix_lock); + else { /* We didn't get them all; need to wait... */ ASSERT((wtr->uflgs & ~ERTS_PROC_LOCKS_ALL) == 0); @@ -529,28 +454,12 @@ wait_for_locks(Process *p, } while (res != 0); } - erts_pix_lock(pix_lock); - ASSERT(wtr->uflgs == 0); } - /* Recover some queues to store in the waiter. */ - ERTS_LC_ASSERT(p->lock.queues); - if (p->lock.queues->next) { - qs = p->lock.queues->next; - p->lock.queues->next = qs->next; - } - else { - qs = p->lock.queues; - p->lock.queues = NULL; - } - wtr->udata = qs; - - erts_pix_unlock(pix_lock); - ERTS_LC_ASSERT(locks == (ERTS_PROC_LOCK_FLGS_READ_(&p->lock) & locks)); - tse_return(wtr, 0); + tse_return(wtr); } /* @@ -971,6 +880,7 @@ erts_pid2proc_safelock(Process *c_p, void erts_proc_lock_init(Process *p) { + int i; /* We always start with all locks locked */ #if ERTS_PROC_LOCK_ATOMIC_IMPL erts_smp_atomic32_init_nob(&p->lock.flags, @@ -978,7 +888,8 @@ erts_proc_lock_init(Process *p) #else p->lock.flags = ERTS_PROC_LOCKS_ALL; #endif - p->lock.queues = NULL; + for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) + p->lock.queue[i] = NULL; p->lock.refc = 1; #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_proc_lock_init(p); @@ -990,11 +901,8 @@ erts_proc_lock_init(Process *p) erts_proc_lc_trylock(p, ERTS_PROC_LOCKS_ALL, 1); #endif #ifdef ERTS_PROC_LOCK_DEBUG - { - int i; - for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) - erts_smp_atomic32_init_nob(&p->lock.locked[i], (erts_aint32_t) 1); - } + for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) + erts_smp_atomic32_init_nob(&p->lock.locked[i], (erts_aint32_t) 1); #endif } @@ -1002,7 +910,7 @@ erts_proc_lock_init(Process *p) #ifdef ERTS_ENABLE_LOCK_COUNT void erts_lcnt_proc_lock_init(Process *p) { - + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { if (p->id != ERTS_INVALID_PID) { erts_lcnt_init_lock_x(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK, p->id); erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK, p->id); @@ -1014,6 +922,12 @@ void erts_lcnt_proc_lock_init(Process *p) { erts_lcnt_init_lock(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK); erts_lcnt_init_lock(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK); } + } else { + sys_memzero(&(p->lock.lcnt_main), sizeof(p->lock.lcnt_main)); + sys_memzero(&(p->lock.lcnt_msgq), sizeof(p->lock.lcnt_msgq)); + sys_memzero(&(p->lock.lcnt_link), sizeof(p->lock.lcnt_link)); + sys_memzero(&(p->lock.lcnt_status), sizeof(p->lock.lcnt_status)); + } } @@ -1108,6 +1022,26 @@ void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res } } + +void erts_lcnt_enable_proc_lock_count(int enable) { + int i; + + for (i = 0; i < erts_max_processes; ++i) { + Process* p = process_tab[i]; + if (p) { + if (enable) { + if (!ERTS_LCNT_LOCK_TYPE(&(p->lock.lcnt_main))) { + erts_lcnt_proc_lock_init(p); + } + } else { + if (ERTS_LCNT_LOCK_TYPE(&(p->lock.lcnt_main))) { + erts_lcnt_proc_lock_destroy(p); + } + } + } + } +} + #endif /* ifdef ERTS_ENABLE_LOCK_COUNT */ @@ -1437,21 +1371,21 @@ check_queue(erts_proc_lock_t *lck) if (lflgs & wtr) { int n; erts_tse_t *wtr; - ERTS_LC_ASSERT(lck->queues && lck->queues->queue[lock_no]); - wtr = lck->queues->queue[lock_no]; + ERTS_LC_ASSERT(lck->queue[lock_no]); + wtr = lck->queue[lock_no]; n = 0; do { wtr = wtr->next; n++; - } while (wtr != lck->queues->queue[lock_no]); + } while (wtr != lck->queue[lock_no]); do { wtr = wtr->prev; n--; - } while (wtr != lck->queues->queue[lock_no]); + } while (wtr != lck->queue[lock_no]); ERTS_LC_ASSERT(n == 0); } else { - ERTS_LC_ASSERT(!lck->queues || !lck->queues->queue[lock_no]); + ERTS_LC_ASSERT(!lck->queue[lock_no]); } } } diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 8dbdaccc68..290084d8ca 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -56,15 +56,13 @@ typedef erts_aint32_t ErtsProcLocks; -typedef struct erts_proc_lock_queues_t_ erts_proc_lock_queues_t; - typedef struct erts_proc_lock_t_ { #if ERTS_PROC_LOCK_ATOMIC_IMPL erts_smp_atomic32_t flags; #else ErtsProcLocks flags; #endif - erts_proc_lock_queues_t *queues; + erts_tse_t *queue[ERTS_PROC_LOCK_MAX_BIT+1]; Sint32 refc; #ifdef ERTS_PROC_LOCK_DEBUG erts_smp_atomic32_t locked[ERTS_PROC_LOCK_MAX_BIT+1]; @@ -215,6 +213,8 @@ void erts_lcnt_proc_lock_unaquire(erts_proc_lock_t *lock, ErtsProcLocks locks); void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks); void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res); +void erts_lcnt_enable_proc_lock_count(int enable); + #endif /* ERTS_ENABLE_LOCK_COUNT*/ diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c index bff9d246a3..a490aec734 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c @@ -116,54 +116,84 @@ erts_sspa_create(size_t blk_sz, int pa_size) return data; } -static ERTS_INLINE erts_aint_t +static ERTS_INLINE void enqueue_remote_managed_thread(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *this, - int want_last) + int cinit) { - erts_aint_t ilast, itmp; + erts_aint_t itmp; + erts_sspa_blk_t *enq; erts_atomic_init_nob(&this->next_atmc, ERTS_AINT_NULL); - /* Enqueue at end of list... */ - ilast = erts_atomic_read_nob(&chdr->tail.data.last); - while (1) { - erts_sspa_blk_t *last = (erts_sspa_blk_t *) ilast; - itmp = erts_atomic_cmpxchg_mb(&last->next_atmc, - (erts_aint_t) this, - ERTS_AINT_NULL); - if (itmp == ERTS_AINT_NULL) - break; - ilast = itmp; + enq = (erts_sspa_blk_t *) erts_atomic_read_nob(&chdr->tail.data.last); + itmp = erts_atomic_cmpxchg_relb(&enq->next_atmc, + (erts_aint_t) this, + ERTS_AINT_NULL); + if (itmp == ERTS_AINT_NULL) { + /* We are required to move last pointer */ +#ifdef DEBUG + ASSERT(ERTS_AINT_NULL == erts_atomic_read_nob(&this->next_atmc)); + ASSERT(((erts_aint_t) enq) + == erts_atomic_xchg_relb(&chdr->tail.data.last, + (erts_aint_t) this)); +#else + erts_atomic_set_relb(&chdr->tail.data.last, (erts_aint_t) this); +#endif } + else { + /* + * We *need* to insert element somewhere in between the + * last element we read earlier and the actual last element. + */ + int i = cinit; - /* Move last pointer forward... */ - while (1) { - erts_aint_t itmp; - if (want_last) { - if (erts_atomic_read_rb(&this->next_atmc) != ERTS_AINT_NULL) { - /* Someone else will move it forward */ - return erts_atomic_read_nob(&chdr->tail.data.last); + while (1) { + erts_aint_t itmp2; + erts_atomic_set_nob(&this->next_atmc, itmp); + itmp2 = erts_atomic_cmpxchg_relb(&enq->next_atmc, + (erts_aint_t) this, + itmp); + if (itmp == itmp2) + break; /* inserted this */ + if ((i & 1) == 0) + itmp = itmp2; + else { + enq = (erts_sspa_blk_t *) itmp; + itmp = erts_atomic_read_acqb(&enq->next_atmc); + ASSERT(itmp != ERTS_AINT_NULL); } + i++; } - else { - if (erts_atomic_read_nob(&this->next_atmc) != ERTS_AINT_NULL) { - /* Someone else will move it forward */ - return ERTS_AINT_NULL; - } + } +} + +static ERTS_INLINE erts_aint_t +check_insert_marker(erts_sspa_chunk_header_t *chdr, erts_aint_t ilast) +{ + if (!chdr->head.used_marker + && chdr->head.unref_end == (erts_sspa_blk_t *) ilast) { + erts_aint_t itmp; + erts_sspa_blk_t *last = (erts_sspa_blk_t *) ilast; + + erts_atomic_init_nob(&chdr->tail.data.marker.next_atmc, ERTS_AINT_NULL); + itmp = erts_atomic_cmpxchg_relb(&last->next_atmc, + (erts_aint_t) &chdr->tail.data.marker, + ERTS_AINT_NULL); + if (itmp == ERTS_AINT_NULL) { + ilast = (erts_aint_t) &chdr->tail.data.marker; + chdr->head.used_marker = !0; + erts_atomic_set_relb(&chdr->tail.data.last, ilast); } - itmp = erts_atomic_cmpxchg_mb(&chdr->tail.data.last, - (erts_aint_t) this, - ilast); - if (ilast == itmp) - return want_last ? (erts_aint_t) this : ERTS_AINT_NULL; - ilast = itmp; } + return ilast; } void -erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *blk) +erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, + erts_sspa_blk_t *blk, + int cinit) { int um_refc_ix = 0; int managed_thread = erts_thr_progress_is_managed_thread(); @@ -180,7 +210,7 @@ erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *blk) } } - (void) enqueue_remote_managed_thread(chdr, blk, 0); + enqueue_remote_managed_thread(chdr, blk, cinit); if (!managed_thread) erts_atomic_dec_relb(&chdr->tail.data.um_refc[um_refc_ix]); @@ -208,26 +238,19 @@ fetch_remote(erts_sspa_chunk_header_t *chdr, int max) int um_refc_ix; chdr->head.next.thr_progress_reached = 1; um_refc_ix = chdr->head.next.um_refc_ix; - if (erts_atomic_read_acqb(&chdr->tail.data.um_refc[um_refc_ix]) == 0) { + if (erts_atomic_read_nob(&chdr->tail.data.um_refc[um_refc_ix]) == 0) { + + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); /* Move unreferenced end pointer forward... */ chdr->head.unref_end = chdr->head.next.unref_end; - if (!chdr->head.used_marker - && chdr->head.unref_end == (erts_sspa_blk_t *) ilast) { - /* Need to equeue marker */ - chdr->head.used_marker = 1; - ilast = enqueue_remote_managed_thread(chdr, - &chdr->tail.data.marker, - 1); - } + ilast = check_insert_marker(chdr, ilast); - if (chdr->head.unref_end == (erts_sspa_blk_t *) ilast) - ERTS_THR_MEMORY_BARRIER; - else { + if (chdr->head.unref_end != (erts_sspa_blk_t *) ilast) { chdr->head.next.unref_end = (erts_sspa_blk_t *) ilast; - chdr->head.next.thr_progress = erts_thr_progress_later(); + chdr->head.next.thr_progress = erts_thr_progress_later(NULL); erts_atomic32_set_relb(&chdr->tail.data.um_refc_ix, um_refc_ix); chdr->head.next.um_refc_ix = um_refc_ix == 0 ? 1 : 0; diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h index d36066c399..bccb1aba7a 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.h +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.h @@ -142,7 +142,8 @@ check_local_list(erts_sspa_chunk_header_t *chdr) erts_sspa_data_t *erts_sspa_create(size_t blk_sz, int pa_size); void erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, - erts_sspa_blk_t *blk); + erts_sspa_blk_t *blk, + int cinit); erts_sspa_blk_t *erts_sspa_process_remote_frees(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *old_res); @@ -216,7 +217,7 @@ erts_sspa_free(erts_sspa_data_t *data, int cix, char *cblk) chdr = &chnk->aligned.header; if (chnk_cix != cix) { /* Remote chunk */ - erts_sspa_remote_free(chdr, blk); + erts_sspa_remote_free(chdr, blk, chnk_cix - cix); } else { /* Local chunk */ diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 9ef83746c5..88524bdd4c 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -891,16 +891,16 @@ has_reached_wakeup(ErtsThrPrgrVal wakeup) ErtsThrPrgrVal limit; /* * erts_thr_progress_later() returns values which are - * equal to 'current + 2'. That is, users should never - * get a hold of values larger than that. + * equal to 'current + 2', or 'current + 3'. That is, users + * should never get a hold of values larger than that. * - * That is, valid values are values less than 'current + 3'. + * That is, valid values are values less than 'current + 4'. * * Values larger than this won't work with the wakeup * algorithm. */ - limit = current + 3; + limit = current + 4; if (limit == ERTS_THR_PRGR_VAL_WAITING) limit = 0; else if (limit < current) /* Wrapped */ diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h index a71724b813..89486b065b 100644 --- a/erts/emulator/beam/erl_thr_progress.h +++ b/erts/emulator/beam/erl_thr_progress.h @@ -139,11 +139,12 @@ ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_mb__(ERTS_THR_PRGR_ATOMIC *atm ERTS_GLB_INLINE int erts_thr_progress_is_managed_thread(void); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_current_to_later__(ErtsThrPrgrVal val); -ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_later_than(ErtsThrPrgrVal val); -ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_later(void); +ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_later(ErtsSchedulerData *); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_current(void); ERTS_GLB_INLINE int erts_thr_progress_has_passed__(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2); ERTS_GLB_INLINE int erts_thr_progress_has_reached_this(ErtsThrPrgrVal this, ErtsThrPrgrVal val); +ERTS_GLB_INLINE int erts_thr_progress_equal(ErtsThrPrgrVal val1, + ErtsThrPrgrVal val2); ERTS_GLB_INLINE int erts_thr_progress_cmp(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2); ERTS_GLB_INLINE int erts_thr_progress_has_reached(ErtsThrPrgrVal val); @@ -230,16 +231,23 @@ erts_thr_progress_current_to_later__(ErtsThrPrgrVal val) } ERTS_GLB_INLINE ErtsThrPrgrVal -erts_thr_progress_later_than(ErtsThrPrgrVal val) +erts_thr_progress_later(ErtsSchedulerData *esdp) { - ERTS_THR_MEMORY_BARRIER; - return erts_thr_progress_current_to_later__(val); -} - -ERTS_GLB_INLINE ErtsThrPrgrVal -erts_thr_progress_later(void) -{ - ErtsThrPrgrVal val = erts_thr_prgr_read_mb__(&erts_thr_prgr__.current); + ErtsThrPrgrData *tpd; + ErtsThrPrgrVal val; + if (esdp) { + tpd = &esdp->thr_progress_data; + managed_thread: + val = tpd->previous.local; + ERTS_THR_MEMORY_BARRIER; + } + else { + tpd = erts_tsd_get(erts_thr_prgr_data_key__); + if (tpd && tpd->is_managed) + goto managed_thread; + val = erts_thr_prgr_read_mb__(&erts_thr_prgr__.current); + } + ASSERT(val != ERTS_THR_PRGR_VAL_WAITING); return erts_thr_progress_current_to_later__(val); } @@ -279,6 +287,12 @@ erts_thr_progress_has_reached_this(ErtsThrPrgrVal this, ErtsThrPrgrVal val) } ERTS_GLB_INLINE int +erts_thr_progress_equal(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2) +{ + return val1 == val2 && val1 != ERTS_THR_PRGR_INVALID; +} + +ERTS_GLB_INLINE int erts_thr_progress_cmp(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2) { if (val1 == val2) diff --git a/erts/emulator/beam/erl_thr_queue.c b/erts/emulator/beam/erl_thr_queue.c index 70949ece76..f07964a265 100644 --- a/erts/emulator/beam/erl_thr_queue.c +++ b/erts/emulator/beam/erl_thr_queue.c @@ -422,7 +422,7 @@ clean(ErtsThrQ_t *q, int max_ops, int do_notify) else { q->head.next.unref_end = (ErtsThrQElement_t *) ilast; #ifdef ERTS_SMP - q->head.next.thr_progress = erts_thr_progress_later(); + q->head.next.thr_progress = erts_thr_progress_later(NULL); #endif erts_atomic32_set_relb(&q->tail.data.um_refc_ix, um_refc_ix); diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 1d0735aa99..a44401d7b1 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -351,7 +351,7 @@ static int clock_resolution; /* ** The clock resolution should really be the resolution of the ** time function in use, which on most platforms -** is 1. On VxWorks the resolution shold be +** is 1. On VxWorks the resolution should be ** the number of ticks per second (or 1, which would work nicely to). ** ** Setting lower resolutions is mostly interesting when timers are used @@ -717,6 +717,11 @@ int univ_to_seconds(Sint year, Sint month, Sint day, Sint hour, Sint minute, Sin return 1; } +#if defined(HAVE_TIME2POSIX) && defined(HAVE_DECL_TIME2POSIX) && \ + !HAVE_DECL_TIME2POSIX +extern time_t time2posix(time_t); +#endif + int local_to_univ(Sint *year, Sint *month, Sint *day, Sint *hour, Sint *minute, Sint *second, int isdst) @@ -757,7 +762,7 @@ local_to_univ(Sint *year, Sint *month, Sint *day, refuses to give us a DST time, we simulate the Linux/Solaris behaviour of giving the same data as if is_dst was not set. */ t.tm_isdst = 0; - if (erl_mktime(&the_clock, &t)) { + if (erl_mktime(&the_clock, &t) < 0) { /* Failed anyway, something else is bad - will be a badarg */ return 0; } @@ -766,6 +771,11 @@ local_to_univ(Sint *year, Sint *month, Sint *day, return 0; } } + +#ifdef HAVE_TIME2POSIX + the_clock = time2posix(the_clock); +#endif + #ifdef HAVE_GMTIME_R tm = gmtime_r(&the_clock, &tmbuf); #else diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index 6d5eae73b0..127db4d4f6 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -2027,12 +2027,14 @@ BIF_RETTYPE binary_to_existing_atom_2(BIF_ALIST_2) * string routines, that will certainly fail on some OS. */ -char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int allow_empty) +char *erts_convert_filename_to_native(Eterm name, char *statbuf, size_t statbuf_size, ErtsAlcType_t alloc_type, int allow_empty, int allow_atom, Sint *used) { int encoding = erts_get_native_filename_encoding(); char* name_buf = NULL; - if (is_atom(name) || is_list(name) || (allow_empty && is_nil(name))) { + if ((allow_atom && is_atom(name)) || + is_list(name) || + (allow_empty && is_nil(name))) { Sint need; if ((need = erts_native_filename_need(name,encoding)) < 0) { return NULL; @@ -2042,7 +2044,13 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int } else { ++need; } - name_buf = (char *) erts_alloc(alloc_type, need); + if (used) + *used = (Sint) need; + if (need > statbuf_size) { + name_buf = (char *) erts_alloc(alloc_type, need); + } else { + name_buf = statbuf; + } erts_native_filename_put(name,encoding,(byte *)name_buf); name_buf[need-1] = 0; if (encoding == ERL_FILENAME_WIN_WCHAR) { @@ -2058,14 +2066,26 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int bytes = erts_get_aligned_binary_bytes(name, &temp_alloc); if (encoding != ERL_FILENAME_WIN_WCHAR) { /*Add 0 termination only*/ - name_buf = (char *) erts_alloc(alloc_type, size+1); + if (used) + *used = (Sint) size+1; + if (size+1 > statbuf_size) { + name_buf = (char *) erts_alloc(alloc_type, size+1); + } else { + name_buf = statbuf; + } memcpy(name_buf,bytes,size); name_buf[size]=0; } else if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK || erts_get_user_requested_filename_encoding() == ERL_FILENAME_LATIN1) { byte *p; /* What to do now? Maybe latin1, so just take byte for byte instead */ - name_buf = (char *) erts_alloc(alloc_type, (size+1)*2); + if (used) + *used = (Sint) (size+1)*2; + if ((size+1)*2 > statbuf_size) { + name_buf = (char *) erts_alloc(alloc_type, (size+1)*2); + } else { + name_buf = statbuf; + } p = (byte *) name_buf; while (size--) { *p++ = *bytes++; @@ -2074,7 +2094,13 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int *p++ = 0; *p++ = 0; } else { /* WIN_WCHAR and valid UTF8 */ - name_buf = (char *) erts_alloc(alloc_type, (num_chars+1)*2); + if (used) + *used = (Sint) (num_chars+1)*2; + if ((num_chars+1)*2 > statbuf_size) { + name_buf = (char *) erts_alloc(alloc_type, (num_chars+1)*2); + } else { + name_buf = statbuf; + } erts_copy_utf8_to_utf16_little((byte *) name_buf, bytes, num_chars); name_buf[num_chars*2] = 0; name_buf[num_chars*2+1] = 0; @@ -2086,6 +2112,71 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int return name_buf; } +static int filename_len_16bit(byte *str) +{ + byte *p = str; + while(*p != '\0' || p[1] != '\0') { + p += 2; + } + return (p - str); +} +Eterm erts_convert_native_to_filename(Process *p, byte *bytes) +{ + Uint size,num_chars; + Eterm *hp; + byte *err_pos; + Uint num_built; /* characters */ + Uint num_eaten; /* bytes */ + Eterm ret; + int mac = 0; + + switch (erts_get_native_filename_encoding()) { + case ERL_FILENAME_LATIN1: + goto noconvert; + case ERL_FILENAME_UTF8_MAC: + mac = 1; + case ERL_FILENAME_UTF8: + size = strlen((char *) bytes); + if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK) { + goto noconvert; + } + num_built = 0; + num_eaten = 0; + if (mac) { + ret = do_utf8_to_list_normalize(p, num_chars, bytes, size); + } else { + ret = do_utf8_to_list(p, num_chars, bytes, size, num_chars, &num_built, &num_eaten, NIL); + } + return ret; + case ERL_FILENAME_WIN_WCHAR: + size=filename_len_16bit(bytes); + if ((size % 2) != 0) { /* Panic fixup to avoid crashing the emulator */ + size--; + hp = HAlloc(p, size+2); + ret = CONS(hp,make_small((Uint) bytes[size]),NIL); + hp += 2; + } else { + hp = HAlloc(p, size); + ret = NIL; + } + bytes += size-1; + while (size > 0) { + Uint x = ((Uint) *bytes--) << 8; + x |= ((Uint) *bytes--); + size -= 2; + ret = CONS(hp,make_small(x),ret); + hp += 2; + } + return ret; + default: + goto noconvert; + } + noconvert: + size = strlen((char *) bytes); + hp = HAlloc(p, 2 * size); + return erts_bin_bytes_to_list(NIL, hp, bytes, size, 0); +} + Sint erts_native_filename_need(Eterm ioterm, int encoding) { @@ -2619,3 +2710,4 @@ BIF_RETTYPE file_native_name_encoding_0(BIF_ALIST_0) BIF_RET(am_undefined); } } + diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 5dc307e383..a0e12e57f2 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -29,19 +29,10 @@ /* #define FORCE_HEAP_FRAGS */ -#if defined(HYBRID) -/* # define CHECK_FOR_HOLES */ -#endif - #if defined(DEBUG) && !defined(CHECK_FOR_HOLES) && !defined(__WIN32__) # define CHECK_FOR_HOLES #endif -#if defined(HYBRID) -/* # define INCREMENTAL 1 */ /* Incremental garbage collection */ -/* # define INC_TIME_BASED 1 */ /* Time-based incremental GC (vs Work-based) */ -#endif - #define BEAM 1 #define EMULATOR "BEAM" #define SEQ_TRACE 1 @@ -70,16 +61,6 @@ #define H_DEFAULT_SIZE 233 /* default (heap + stack) min size */ #define VH_DEFAULT_SIZE 32768 /* default virtual (bin) heap min size (words) */ -#ifdef HYBRID -# define SH_DEFAULT_SIZE 2629425 /* default message area min size */ -#endif - -#ifdef INCREMENTAL -# define INC_NoPAGES 256 /* Number of pages in the old generation */ -# define INC_PAGESIZE 32768 /* The size of each page */ -# define INC_STORAGE_SIZE 1024 /* The size of gray stack and similar */ -#endif - #define CP_SIZE 1 #define ErtsHAllocLockCheck(P) \ diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d index c1024dafc4..bdfde58845 100644 --- a/erts/emulator/beam/erlang_dtrace.d +++ b/erts/emulator/beam/erlang_dtrace.d @@ -717,6 +717,2916 @@ provider erlang { * inside the same I/O worker pool thread. * For R14B03's source, see erl_async.c lines 302-317. */ + +/* + * The set of probes for use by Erlang code ... moved to here from + * lib/runtime_tools/c_src/dtrace_user.d until a more portable solution + * is found. This move pollutes the Erlang VM with functions that are + * only used by the NIF shared library code in + * lib/runtime_tools/c_src/dyntrace.c. The reason this is necessary is + * in order to work around an issue on several platforms, including + * SystemTap 1.3 and Solaris. The Solaris issue is discussed in the + * `dtrace-discuss` mailing list thread on 01 Dec 2011 17:58:15. + */ + /** + * If you use only a single probe, but you also embed that probe + * in many different places in your code, if that probe fires 100K + * or more times per second, then it *will* hurt when you have to + * enable that probe. + * + * However, if you have any different probes, then you can ensure + * that any probe on a hot code path will use separate probe(s) + * than everyone else ... and you can then enable many non-hot + * probes in production without worry about creating too much + * measurement overhead. + * + * In an ideal world, we would use the libusdt library to be able + * to create arbitrary DTrace probes with more convenient and + * meaningful names than "user_trace-n7". But libusdt doesn't + * (yet) support all of the platforms that DTrace does, and there + * is no known (yet) equivalent for SystemTap. + */ + probe user_trace__n0(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n1(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n2(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n3(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n4(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n5(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n6(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n7(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n8(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n9(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n10(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n11(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n12(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n13(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n14(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n15(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n16(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n17(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n18(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n19(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n20(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n21(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n22(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n23(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n24(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n25(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n26(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n27(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n28(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n29(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n30(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n31(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n32(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n33(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n34(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n35(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n36(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n37(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n38(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n39(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n40(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n41(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n42(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n43(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n44(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n45(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n46(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n47(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n48(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n49(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n50(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n51(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n52(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n53(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n54(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n55(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n56(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n57(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n58(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n59(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n60(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n61(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n62(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n63(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n64(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n65(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n66(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n67(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n68(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n69(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n70(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n71(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n72(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n73(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n74(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n75(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n76(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n77(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n78(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n79(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n80(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n81(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n82(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n83(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n84(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n85(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n86(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n87(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n88(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n89(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n90(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n91(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n92(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n93(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n94(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n95(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n96(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n97(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n98(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n99(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n100(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n101(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n102(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n103(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n104(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n105(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n106(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n107(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n108(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n109(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n110(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n111(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n112(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n113(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n114(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n115(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n116(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n117(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n118(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n119(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n120(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n121(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n122(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n123(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n124(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n125(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n126(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n127(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n128(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n129(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n130(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n131(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n132(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n133(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n134(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n135(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n136(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n137(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n138(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n139(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n140(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n141(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n142(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n143(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n144(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n145(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n146(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n147(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n148(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n149(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n150(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n151(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n152(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n153(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n154(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n155(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n156(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n157(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n158(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n159(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n160(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n161(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n162(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n163(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n164(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n165(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n166(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n167(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n168(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n169(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n170(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n171(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n172(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n173(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n174(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n175(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n176(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n177(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n178(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n179(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n180(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n181(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n182(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n183(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n184(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n185(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n186(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n187(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n188(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n189(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n190(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n191(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n192(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n193(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n194(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n195(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n196(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n197(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n198(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n199(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n200(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n201(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n202(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n203(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n204(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n205(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n206(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n207(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n208(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n209(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n210(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n211(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n212(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n213(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n214(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n215(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n216(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n217(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n218(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n219(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n220(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n221(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n222(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n223(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n224(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n225(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n226(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n227(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n228(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n229(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n230(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n231(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n232(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n233(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n234(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n235(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n236(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n237(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n238(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n239(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n240(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n241(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n242(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n243(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n244(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n245(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n246(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n247(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n248(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n249(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n250(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n251(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n252(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n253(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n254(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n255(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n256(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n257(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n258(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n259(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n260(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n261(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n262(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n263(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n264(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n265(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n266(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n267(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n268(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n269(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n270(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n271(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n272(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n273(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n274(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n275(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n276(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n277(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n278(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n279(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n280(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n281(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n282(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n283(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n284(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n285(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n286(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n287(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n288(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n289(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n290(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n291(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n292(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n293(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n294(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n295(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n296(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n297(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n298(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n299(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n300(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n301(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n302(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n303(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n304(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n305(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n306(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n307(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n308(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n309(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n310(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n311(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n312(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n313(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n314(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n315(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n316(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n317(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n318(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n319(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n320(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n321(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n322(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n323(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n324(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n325(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n326(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n327(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n328(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n329(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n330(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n331(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n332(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n333(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n334(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n335(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n336(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n337(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n338(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n339(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n340(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n341(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n342(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n343(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n344(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n345(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n346(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n347(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n348(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n349(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n350(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n351(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n352(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n353(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n354(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n355(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n356(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n357(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n358(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n359(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n360(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n361(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n362(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n363(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n364(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n365(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n366(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n367(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n368(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n369(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n370(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n371(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n372(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n373(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n374(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n375(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n376(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n377(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n378(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n379(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n380(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n381(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n382(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n383(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n384(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n385(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n386(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n387(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n388(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n389(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n390(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n391(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n392(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n393(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n394(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n395(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n396(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n397(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n398(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n399(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n400(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n401(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n402(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n403(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n404(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n405(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n406(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n407(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n408(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n409(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n410(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n411(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n412(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n413(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n414(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n415(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n416(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n417(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n418(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n419(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n420(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n421(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n422(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n423(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n424(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n425(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n426(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n427(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n428(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n429(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n430(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n431(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n432(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n433(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n434(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n435(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n436(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n437(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n438(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n439(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n440(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n441(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n442(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n443(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n444(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n445(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n446(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n447(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n448(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n449(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n450(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n451(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n452(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n453(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n454(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n455(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n456(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n457(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n458(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n459(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n460(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n461(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n462(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n463(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n464(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n465(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n466(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n467(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n468(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n469(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n470(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n471(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n472(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n473(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n474(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n475(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n476(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n477(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n478(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n479(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n480(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n481(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n482(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n483(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n484(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n485(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n486(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n487(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n488(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n489(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n490(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n491(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n492(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n493(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n494(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n495(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n496(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n497(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n498(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n499(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n500(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n501(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n502(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n503(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n504(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n505(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n506(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n507(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n508(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n509(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n510(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n511(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n512(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n513(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n514(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n515(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n516(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n517(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n518(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n519(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n520(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n521(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n522(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n523(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n524(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n525(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n526(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n527(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n528(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n529(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n530(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n531(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n532(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n533(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n534(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n535(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n536(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n537(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n538(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n539(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n540(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n541(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n542(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n543(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n544(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n545(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n546(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n547(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n548(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n549(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n550(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n551(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n552(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n553(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n554(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n555(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n556(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n557(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n558(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n559(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n560(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n561(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n562(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n563(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n564(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n565(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n566(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n567(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n568(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n569(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n570(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n571(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n572(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n573(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n574(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n575(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n576(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n577(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n578(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n579(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n580(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n581(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n582(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n583(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n584(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n585(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n586(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n587(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n588(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n589(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n590(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n591(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n592(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n593(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n594(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n595(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n596(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n597(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n598(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n599(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n600(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n601(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n602(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n603(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n604(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n605(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n606(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n607(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n608(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n609(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n610(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n611(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n612(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n613(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n614(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n615(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n616(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n617(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n618(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n619(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n620(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n621(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n622(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n623(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n624(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n625(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n626(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n627(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n628(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n629(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n630(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n631(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n632(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n633(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n634(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n635(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n636(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n637(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n638(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n639(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n640(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n641(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n642(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n643(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n644(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n645(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n646(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n647(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n648(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n649(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n650(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n651(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n652(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n653(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n654(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n655(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n656(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n657(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n658(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n659(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n660(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n661(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n662(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n663(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n664(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n665(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n666(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n667(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n668(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n669(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n670(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n671(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n672(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n673(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n674(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n675(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n676(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n677(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n678(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n679(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n680(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n681(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n682(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n683(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n684(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n685(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n686(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n687(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n688(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n689(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n690(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n691(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n692(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n693(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n694(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n695(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n696(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n697(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n698(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n699(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n700(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n701(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n702(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n703(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n704(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n705(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n706(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n707(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n708(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n709(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n710(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n711(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n712(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n713(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n714(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n715(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n716(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n717(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n718(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n719(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n720(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n721(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n722(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n723(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n724(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n725(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n726(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n727(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n728(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n729(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n730(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n731(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n732(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n733(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n734(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n735(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n736(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n737(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n738(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n739(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n740(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n741(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n742(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n743(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n744(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n745(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n746(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n747(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n748(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n749(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n750(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n751(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n752(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n753(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n754(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n755(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n756(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n757(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n758(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n759(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n760(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n761(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n762(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n763(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n764(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n765(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n766(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n767(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n768(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n769(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n770(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n771(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n772(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n773(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n774(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n775(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n776(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n777(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n778(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n779(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n780(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n781(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n782(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n783(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n784(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n785(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n786(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n787(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n788(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n789(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n790(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n791(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n792(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n793(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n794(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n795(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n796(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n797(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n798(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n799(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n800(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n801(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n802(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n803(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n804(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n805(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n806(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n807(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n808(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n809(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n810(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n811(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n812(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n813(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n814(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n815(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n816(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n817(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n818(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n819(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n820(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n821(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n822(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n823(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n824(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n825(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n826(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n827(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n828(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n829(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n830(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n831(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n832(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n833(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n834(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n835(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n836(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n837(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n838(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n839(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n840(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n841(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n842(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n843(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n844(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n845(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n846(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n847(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n848(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n849(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n850(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n851(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n852(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n853(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n854(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n855(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n856(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n857(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n858(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n859(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n860(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n861(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n862(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n863(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n864(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n865(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n866(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n867(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n868(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n869(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n870(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n871(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n872(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n873(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n874(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n875(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n876(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n877(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n878(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n879(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n880(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n881(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n882(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n883(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n884(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n885(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n886(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n887(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n888(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n889(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n890(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n891(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n892(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n893(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n894(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n895(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n896(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n897(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n898(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n899(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n900(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n901(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n902(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n903(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n904(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n905(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n906(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n907(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n908(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n909(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n910(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n911(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n912(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n913(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n914(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n915(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n916(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n917(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n918(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n919(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n920(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n921(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n922(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n923(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n924(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n925(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n926(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n927(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n928(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n929(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n930(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n931(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n932(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n933(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n934(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n935(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n936(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n937(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n938(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n939(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n940(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n941(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n942(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n943(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n944(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n945(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n946(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n947(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n948(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n949(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + probe user_trace__n950(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + /** + * Send a single string to a probe. + * This probe is deprecated. + * + * @param NUL-terminated string + */ + probe user_trace__s1(char* message); + + /** + * Multi-purpose probe: up to 4 NUL-terminated strings and 4 + * 64-bit integer arguments. + * This probe is deprecated. + * + * @param proc, the PID (string form) of the sending process + * @param user_tag, the user tag of the sender + * @param i1, integer + * @param i2, integer + * @param i3, integer + * @param i4, integer + * @param s1, string/iolist. D's arg6 is NULL if not given by Erlang + * @param s2, string/iolist. D's arg7 is NULL if not given by Erlang + * @param s3, string/iolist. D's arg8 is NULL if not given by Erlang + * @param s4, string/iolist. D's arg9 is NULL if not given by Erlang + */ + probe user_trace__i4s4(char *proc, char *user_tag, + int i1, int i2, int i3, int i4, + char *s1, char *s2, char *s3, char *s4); + }; #pragma D attributes Evolving/Evolving/Common provider erlang provider diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 44abc83d6d..52f45b924f 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1889,7 +1889,9 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags, *ep++ = BINARY_INTERNAL_REF; } if (pb->flags) { + char* before_realloc = pb->val->orig_bytes; erts_emasculate_writable_binary(pb); + bytes += (pb->val->orig_bytes - before_realloc); } erts_refc_inc(&pb->val->refc, 2); @@ -2626,14 +2628,12 @@ dec_term_atom_common: } old_uniq = unsigned_val(temp); -#ifndef HYBRID /* FIND ME! */ /* * It is safe to link the fun into the fun list only when * no more validity tests can fail. */ funp->next = off_heap->first; off_heap->first = (struct erl_off_heap_header*)funp; -#endif funp->fe = erts_put_fun_entry2(module, old_uniq, old_index, uniq, index, arity); @@ -2704,14 +2704,12 @@ dec_term_atom_common: goto error; } -#ifndef HYBRID /* FIND ME! */ /* * It is safe to link the fun into the fun list only when * no more validity tests can fail. */ funp->next = off_heap->first; off_heap->first = (struct erl_off_heap_header*)funp; -#endif old_uniq = unsigned_val(temp); funp->fe = erts_put_fun_entry(module, old_uniq, old_index); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index b000e2c5d4..dbf95f5bd7 100644..100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -173,6 +173,7 @@ struct port { char *name; /* String used in the open */ erts_driver_t* drv_ptr; UWord drv_data; + SWord os_pid; /* Child process ID */ ErtsProcList *suspended; /* List of suspended processes. */ LineBuf *linebuf; /* Buffer to hold data not ready for process to get (line oriented I/O)*/ @@ -564,92 +565,6 @@ extern erts_smp_atomic32_t erts_max_gen_gcs; extern int erts_disable_tolerant_timeofday; -#ifdef HYBRID - -/* Message Area heap pointers */ -extern Eterm *global_heap; /* Heap start */ -extern Eterm *global_hend; /* Heap end */ -extern Eterm *global_htop; /* Heap top (heap pointer) */ -extern Eterm *global_saved_htop; /* Saved heap top (heap pointer) */ -extern Uint global_heap_sz; /* Heap size, in words */ -extern Eterm *global_old_heap; /* Old generation */ -extern Eterm *global_old_hend; -extern ErlOffHeap erts_global_offheap; /* Global MSO (OffHeap) list */ - -extern Uint16 global_gen_gcs; -extern Uint16 global_max_gen_gcs; -extern Uint global_gc_flags; - -#ifdef INCREMENTAL -#define ACTIVATE(p) -#define DEACTIVATE(p) -#define IS_ACTIVE(p) 1 - -#define INC_ACTIVATE(p) do { \ - if ((p)->active) { \ - if ((p)->active_next != NULL) { \ - (p)->active_next->active_prev = (p)->active_prev; \ - if ((p)->active_prev) { \ - (p)->active_prev->active_next = (p)->active_next; \ - } else { \ - inc_active_proc = (p)->active_next; \ - } \ - inc_active_last->active_next = (p); \ - (p)->active_next = NULL; \ - (p)->active_prev = inc_active_last; \ - inc_active_last = (p); \ - } \ - } else { \ - (p)->active_next = NULL; \ - (p)->active_prev = inc_active_last; \ - if (inc_active_last) { \ - inc_active_last->active_next = (p); \ - } else { \ - inc_active_proc = (p); \ - } \ - inc_active_last = (p); \ - (p)->active = 1; \ - } \ -} while(0); - -#define INC_DEACTIVATE(p) do { \ - ASSERT((p)->active == 1); \ - if ((p)->active_next == NULL) { \ - inc_active_last = (p)->active_prev; \ - } else { \ - (p)->active_next->active_prev = (p)->active_prev; \ - } \ - if ((p)->active_prev == NULL) { \ - inc_active_proc = (p)->active_next; \ - } else { \ - (p)->active_prev->active_next = (p)->active_next; \ - } \ - (p)->active = 0; \ -} while(0); - -#define INC_IS_ACTIVE(p) ((p)->active != 0) - -#else -extern Eterm *global_old_htop; -extern Eterm *global_high_water; -#define ACTIVATE(p) (p)->active = 1; -#define DEACTIVATE(p) (p)->active = 0; -#define IS_ACTIVE(p) ((p)->active != 0) -#define INC_ACTIVATE(p) -#define INC_IS_ACTIVE(p) 1 -#endif /* INCREMENTAL */ - -#else -# define ACTIVATE(p) -# define DEACTIVATE(p) -# define IS_ACTIVE(p) 1 -# define INC_ACTIVATE(p) -#endif /* HYBRID */ - -#ifdef HYBRID -extern Uint global_heap_min_sz; -#endif - extern int bif_reductions; /* reductions + fcalls (when doing call_bif) */ extern int stackdump_on_exit; @@ -905,7 +820,6 @@ __decl_noreturn void __noreturn erl_exit_flush_async(int n, char*, ...); void erl_error(char*, va_list); /* copy.c */ -void init_copy(void); Eterm copy_object(Eterm, Process*); #if HALFWORD_HEAP @@ -935,116 +849,6 @@ Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*); void move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first, Eterm* refs, unsigned nrefs); -#ifdef HYBRID -#define RRMA_DEFAULT_SIZE 256 -#define RRMA_STORE(p,ptr,src) do { \ - ASSERT((p)->rrma != NULL); \ - ASSERT((p)->rrsrc != NULL); \ - (p)->rrma[(p)->nrr] = (ptr); \ - (p)->rrsrc[(p)->nrr++] = (src); \ - if ((p)->nrr == (p)->rrsz) \ - { \ - (p)->rrsz *= 2; \ - (p)->rrma = (Eterm *) erts_realloc(ERTS_ALC_T_ROOTSET, \ - (void*)(p)->rrma, \ - sizeof(Eterm) * (p)->rrsz); \ - (p)->rrsrc = (Eterm **) erts_realloc(ERTS_ALC_T_ROOTSET, \ - (void*)(p)->rrsrc, \ - sizeof(Eterm) * (p)->rrsz); \ - } \ -} while(0) - -/* Note that RRMA_REMOVE decreases the given index after deletion. - * This is done so that a loop with an increasing index can call - * remove without having to decrease the index to see the element - * placed in the hole after the deleted element. - */ -#define RRMA_REMOVE(p,index) do { \ - p->rrsrc[index] = p->rrsrc[--p->nrr]; \ - p->rrma[index--] = p->rrma[p->nrr]; \ - } while(0); - - -/* The MessageArea STACKs are used while copying messages to the - * message area. - */ -#define MA_STACK_EXTERNAL_DECLARE(type,_s_) \ - typedef type ma_##_s_##_type; \ - extern ma_##_s_##_type *ma_##_s_##_stack; \ - extern Uint ma_##_s_##_top; \ - extern Uint ma_##_s_##_size; - -#define MA_STACK_DECLARE(_s_) \ - ma_##_s_##_type *ma_##_s_##_stack; Uint ma_##_s_##_top; Uint ma_##_s_##_size; - -#define MA_STACK_ALLOC(_s_) do { \ - ma_##_s_##_top = 0; \ - ma_##_s_##_size = 512; \ - ma_##_s_##_stack = (ma_##_s_##_type*)erts_alloc(ERTS_ALC_T_OBJECT_STACK, \ - sizeof(ma_##_s_##_type) * ma_##_s_##_size); \ -} while(0) - - -#define MA_STACK_PUSH(_s_,val) do { \ - ma_##_s_##_stack[ma_##_s_##_top++] = (val); \ - if (ma_##_s_##_top == ma_##_s_##_size) \ - { \ - ma_##_s_##_size *= 2; \ - ma_##_s_##_stack = \ - (ma_##_s_##_type*) erts_realloc(ERTS_ALC_T_OBJECT_STACK, \ - (void*)ma_##_s_##_stack, \ - sizeof(ma_##_s_##_type) * ma_##_s_##_size); \ - } \ -} while(0) - -#define MA_STACK_POP(_s_) (ma_##_s_##_top != 0 ? ma_##_s_##_stack[--ma_##_s_##_top] : 0) -#define MA_STACK_TOP(_s_) (ma_##_s_##_stack[ma_##_s_##_top - 1]) -#define MA_STACK_UPDATE(_s_,offset,value) \ - *(ma_##_s_##_stack[ma_##_s_##_top - 1] + (offset)) = (value) -#define MA_STACK_SIZE(_s_) (ma_##_s_##_top) -#define MA_STACK_ELM(_s_,i) ma_##_s_##_stack[i] - -MA_STACK_EXTERNAL_DECLARE(Eterm,src); -MA_STACK_EXTERNAL_DECLARE(Eterm*,dst); -MA_STACK_EXTERNAL_DECLARE(Uint,offset); - - -#ifdef INCREMENTAL -extern Eterm *ma_pending_stack; -extern Uint ma_pending_top; -extern Uint ma_pending_size; - -#define NO_COPY(obj) (IS_CONST(obj) || \ - (((ptr_val(obj) >= global_heap) && \ - (ptr_val(obj) < global_htop)) || \ - ((ptr_val(obj) >= inc_fromspc) && \ - (ptr_val(obj) < inc_fromend)) || \ - ((ptr_val(obj) >= global_old_heap) && \ - (ptr_val(obj) < global_old_hend)))) - -#else - -#define NO_COPY(obj) (IS_CONST(obj) || \ - (((ptr_val(obj) >= global_heap) && \ - (ptr_val(obj) < global_htop)) || \ - ((ptr_val(obj) >= global_old_heap) && \ - (ptr_val(obj) < global_old_hend)))) - -#endif /* INCREMENTAL */ - -#define LAZY_COPY(from,obj) do { \ - if (!NO_COPY(obj)) { \ - BM_LAZY_COPY_START; \ - BM_COUNT(messages_copied); \ - obj = copy_struct_lazy(from,obj,0); \ - BM_LAZY_COPY_STOP; \ - } \ -} while(0) - -Eterm copy_struct_lazy(Process*, Eterm, Uint); - -#endif /* HYBRID */ - /* Utilities */ extern void erts_delete_nodes_monitors(Process *, ErtsProcLocks); extern Eterm erts_monitor_nodes(Process *, Eterm, Eterm); @@ -1138,10 +942,6 @@ void erts_offset_heap_ptr(Eterm*, Uint, Sint, Eterm*, Eterm*); void erts_offset_heap(Eterm*, Uint, Sint, Eterm*, Eterm*); void erts_free_heap_frags(Process* p); -#ifdef HYBRID -int erts_global_garbage_collect(Process*, int, Eterm*, int); -#endif - /* io.c */ struct erl_drv_port_data_lock { @@ -1187,6 +987,10 @@ void erts_fire_port_monitor(Port *prt, Eterm ref); void erts_smp_xports_unlock(Port *); #endif +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT) +void erts_lcnt_enable_io_lock_count(int enable); +#endif + #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) int erts_lc_is_port_locked(Port *); #endif @@ -1582,8 +1386,12 @@ Sint erts_native_filename_need(Eterm ioterm, int encoding); void erts_copy_utf8_to_utf16_little(byte *target, byte *bytes, int num_chars); int erts_analyze_utf8(byte *source, Uint size, byte **err_pos, Uint *num_chars, int *left); -char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int allow_empty); - +char *erts_convert_filename_to_native(Eterm name, char *statbuf, + size_t statbuf_size, + ErtsAlcType_t alloc_type, + int allow_empty, int allow_atom, + Sint *used /* out */); +Eterm erts_convert_native_to_filename(Process *p, byte *bytes); #define ERTS_UTF8_OK 0 #define ERTS_UTF8_INCOMPLETE 1 #define ERTS_UTF8_ERROR 2 diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 8a2a43bebd..35b194f927 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -440,6 +440,7 @@ setup_port(Port* prt, Eterm pid, erts_driver_t *driver, sys_strcpy(new_name, name); erts_smp_runq_lock(runq); erts_smp_port_state_lock(prt); + prt->os_pid = -1; prt->status = ERTS_PORT_SFLG_CONNECTED | xstatus; prt->snapshot = erts_smp_atomic32_read_nob(&erts_ports_snapshot); old_name = prt->name; @@ -625,7 +626,11 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ port->lock = erts_alloc(ERTS_ALC_T_PORT_LOCK, sizeof(erts_smp_mtx_t)); erts_smp_mtx_init_x(port->lock, +#ifdef ERTS_ENABLE_LOCK_COUNT + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_lock" : NULL, +#else "port_lock", +#endif port->id); xstatus |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK; } @@ -783,7 +788,13 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */ creator_port->xports = xplp; port->lock = erts_alloc(ERTS_ALC_T_PORT_LOCK, sizeof(erts_smp_mtx_t)); - erts_smp_mtx_init_locked_x(port->lock, "port_lock", port_id); + erts_smp_mtx_init_locked_x(port->lock, +#ifdef ERTS_ENABLE_LOCK_COUNT + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_lock" : NULL, +#else + "port_lock", +#endif + port_id); xstatus |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK; } @@ -1303,7 +1314,7 @@ void init_io(void) pdl_init(); - if (erts_sys_getenv("ERL_MAX_PORTS", maxports, &maxportssize) == 0) + if (erts_sys_getenv_raw("ERL_MAX_PORTS", maxports, &maxportssize) == 0) erts_max_ports = atoi(maxports); else erts_max_ports = sys_max_files(); @@ -1347,7 +1358,13 @@ void init_io(void) erts_smp_atomic_init_nob(&erts_port[i].refc, 0); erts_port[i].lock = NULL; erts_port[i].xports = NULL; - erts_smp_spinlock_init_x(&erts_port[i].state_lck, "port_state", make_small(i)); + erts_smp_spinlock_init_x(&erts_port[i].state_lck, +#ifdef ERTS_ENABLE_LOCK_COUNT + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_state" : NULL, +#else + "port_state", +#endif + make_small(0)); #endif erts_port[i].tracer_proc = NIL; erts_port[i].trace_flags = 0; @@ -1380,6 +1397,27 @@ void init_io(void) erts_smp_mtx_unlock(&erts_driver_list_lock); } +#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP) +void erts_lcnt_enable_io_lock_count(int enable) { + int i; + + for (i = 0; i < erts_max_ports; i++) { + Port* p = &erts_port[i]; + if (enable) { + erts_lcnt_init_lock_x(&p->state_lck.lcnt, "port_state", ERTS_LCNT_LT_SPINLOCK, make_small(i)); + if (p->lock) { + erts_lcnt_init_lock_x(&p->lock->lcnt, "port_lock", ERTS_LCNT_LT_MUTEX, make_small(i)); + } + } else { + erts_lcnt_destroy_lock(&p->state_lck.lcnt); + if (p->lock) { + erts_lcnt_destroy_lock(&p->lock->lcnt); + } + } + } +} +#endif + /* * Buffering of data when using line oriented I/O on ports */ @@ -3222,6 +3260,8 @@ driver_deliver_term(ErlDrvPort port, Uint size = ptr[1]; Uint offset = ptr[2]; + erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) size); + if (size <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hbp = (ErlHeapBin *) hp; hp += heap_bin_size(size); @@ -3253,6 +3293,9 @@ driver_deliver_term(ErlDrvPort port, case ERL_DRV_BUF2BINARY: { /* char*, size */ byte *bufp = (byte *) ptr[0]; Uint size = (Uint) ptr[1]; + + erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) size); + if (size <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hbp = (ErlHeapBin *) hp; hp += heap_bin_size(size); @@ -3289,6 +3332,7 @@ driver_deliver_term(ErlDrvPort port, } case ERL_DRV_STRING: /* char*, length */ + erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) ptr[1]); mess = buf_to_intlist(&hp, (char*)ptr[0], ptr[1], NIL); ptr += 2; break; @@ -5183,11 +5227,11 @@ int null_func(void) int erl_drv_putenv(char *key, char *value) { - return erts_write_env(key, value); + return erts_sys_putenv_raw(key, value); } int erl_drv_getenv(char *key, char *value, size_t *value_size) { - return erts_sys_getenv(key, value, value_size); + return erts_sys_getenv_raw(key, value, value_size); } diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 7b2bb81f62..2406c52f14 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -703,18 +703,21 @@ int sys_double_to_chars(double, char*); void sys_get_pid(char *); /* erts_sys_putenv() returns, 0 on success and a value != 0 on failure. */ -int erts_sys_putenv(char *key_value, int sep_ix); +int erts_sys_putenv(char *key, char *value); +/* Simple variant used from drivers, raw eightbit interface */ +int erts_sys_putenv_raw(char *key, char *value); /* erts_sys_getenv() returns 0 on success (length of value string in *size), a value > 0 if value buffer is too small (*size is set to needed size), and a value < 0 on failure. */ int erts_sys_getenv(char *key, char *value, size_t *size); +/* Simple variant used from drivers, raw eightbit interface */ +int erts_sys_getenv_raw(char *key, char *value, size_t *size); /* erts_sys_getenv__() is only allowed to be used in early init phase */ int erts_sys_getenv__(char *key, char *value, size_t *size); /* Easier to use, but not as efficient, environment functions */ char *erts_read_env(char *key); void erts_free_read_env(void *value); -int erts_write_env(char *key, char *value); /* utils.c */ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index a36d15204e..bd708ceee6 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3409,7 +3409,7 @@ erts_read_env(char *key) char *value = erts_alloc(ERTS_ALC_T_TMP, value_len); int res; while (1) { - res = erts_sys_getenv(key, value, &value_len); + res = erts_sys_getenv_raw(key, value, &value_len); if (res <= 0) break; value = erts_realloc(ERTS_ALC_T_TMP, value, value_len); @@ -3428,28 +3428,6 @@ erts_free_read_env(void *value) erts_free(ERTS_ALC_T_TMP, value); } -int -erts_write_env(char *key, char *value) -{ - int ix, res; - size_t key_len = sys_strlen(key), value_len = sys_strlen(value); - char *key_value = erts_alloc_fnf(ERTS_ALC_T_TMP, - key_len + 1 + value_len + 1); - if (!key_value) { - errno = ENOMEM; - return -1; - } - sys_memcpy((void *) key_value, (void *) key, key_len); - ix = key_len; - key_value[ix++] = '='; - sys_memcpy((void *) key_value, (void *) value, value_len); - ix += value_len; - key_value[ix] = '\0'; - res = erts_sys_putenv(key_value, key_len); - erts_free(ERTS_ALC_T_TMP, key_value); - return res; -} - /* * To be used to silence unused result warnings, but do not abuse it. */ diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 603d1d47b6..f24098025e 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -522,7 +522,7 @@ static void *ef_safe_alloc(Uint s) static void *ef_safe_realloc(void *op, Uint s) { void *p = EF_REALLOC(op, s); - if (!p) erl_exit(1, "efile drv: Can't reallocate %d bytes of memory\n", s); + if (!p) erl_exit(1, "efile drv: Can't reallocate %lu bytes of memory\n", (unsigned long)s); return p; } @@ -2597,7 +2597,7 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) #ifdef USE_VM_PROBES dt_s1 = d->b; dt_s2 = d->b + namelen; - dt_utag = buf + namelen + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; + dt_utag = buf + namelen + FILENAME_BYTELEN(new_name) + FILENAME_CHARSIZE; #endif d->flags = desc->flags; d->fd = fd; diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index bf376f0494..8f4fff0f40 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1228,6 +1228,27 @@ struct erl_drv_entry inet_driver_entry = NULL, }; +#if HAVE_IN6 +# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY +# if HAVE_DECL_IN6ADDR_ANY_INIT +static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } }; +# else +static const struct in6_addr in6addr_any = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; +# endif /* HAVE_IN6ADDR_ANY_INIT */ +# endif /* ! HAVE_DECL_IN6ADDR_ANY */ + +# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK +# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT +static const struct in6_addr in6addr_loopback = + { { IN6ADDR_LOOPBACK_INIT } }; +# else +static const struct in6_addr in6addr_loopback = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }; +# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */ +# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */ +#endif /* HAVE_IN6 */ + /* XXX: is this a driver interface function ??? */ void erl_exit(int n, char*, ...); @@ -3124,6 +3145,7 @@ static int tcp_message(inet_descriptor* desc, const char* buf, int len) int i = 0; DEBUGF(("tcp_message(%ld): len = %d\r\n", (long)desc->port, len)); + /* XXX fprintf(stderr,"tcp_message send.\r\n"); */ i = LOAD_ATOM(spec, i, am_tcp); i = LOAD_PORT(spec, i, desc->dport); @@ -3705,6 +3727,9 @@ static char* inet_set_address(int family, inet_address* dst, if ((family == AF_INET) && (*len >= 2+4)) { sys_memzero((char*)dst, sizeof(struct sockaddr_in)); port = get_int16(src); +#ifndef NO_SA_LEN + dst->sai.sin_len = sizeof(struct sockaddr_in); +#endif dst->sai.sin_family = family; dst->sai.sin_port = sock_htons(port); sys_memcpy(&dst->sai.sin_addr, src+2, 4); @@ -3715,6 +3740,9 @@ static char* inet_set_address(int family, inet_address* dst, else if ((family == AF_INET6) && (*len >= 2+16)) { sys_memzero((char*)dst, sizeof(struct sockaddr_in6)); port = get_int16(src); +#ifndef NO_SA_LEN + dst->sai6.sin6_len = sizeof(struct sockaddr_in6); +#endif dst->sai6.sin6_family = family; dst->sai6.sin6_port = sock_htons(port); dst->sai6.sin6_flowinfo = 0; /* XXX this may be set as well ?? */ @@ -3725,7 +3753,7 @@ static char* inet_set_address(int family, inet_address* dst, #endif return NULL; } -#ifdef HAVE_SCTP + /* ** Set an inaddr structure, address family comes from source data, ** or from argument if source data specifies constant address. @@ -3769,6 +3797,9 @@ static char *inet_set_faddress(int family, inet_address* dst, return NULL; } sys_memzero((char*)dst, sizeof(struct sockaddr_in)); +#ifndef NO_SA_LEN + dst->sai.sin_len = sizeof(struct sockaddr_in6); +#endif dst->sai.sin_family = family; dst->sai.sin_port = sock_htons(port); dst->sai.sin_addr.s_addr = addr.s_addr; @@ -3788,6 +3819,9 @@ static char *inet_set_faddress(int family, inet_address* dst, return NULL; } sys_memzero((char*)dst, sizeof(struct sockaddr_in6)); +#ifndef NO_SA_LEN + dst->sai6.sin6_len = sizeof(struct sockaddr_in6); +#endif dst->sai6.sin6_family = family; dst->sai6.sin6_port = sock_htons(port); dst->sai6.sin6_flowinfo = 0; /* XXX this may be set as well ?? */ @@ -3805,7 +3839,7 @@ static char *inet_set_faddress(int family, inet_address* dst, } return inet_set_address(family, dst, src, len); } -#endif /* HAVE_SCTP */ + /* Get a inaddr structure ** src = inaddr structure @@ -5426,6 +5460,7 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) if (IS_SCTP(desc)) return sctp_set_opts(desc, ptr, len); #endif + /* XXX { int i; for(i=0;i<len;++i) fprintf(stderr,"0x%02X, ", (unsigned) ptr[i]); fprintf(stderr,"\r\n");} */ while(len >= 5) { opt = *ptr++; @@ -5755,10 +5790,16 @@ skip_os_setopt: if (desc->active != old_active) sock_select(desc, (FD_READ|FD_CLOSE), (desc->active>0)); + /* XXX: UDP sockets could also trigger immediate read here NIY */ if ((desc->stype==SOCK_STREAM) && desc->active) { if (!old_active || (desc->htype != old_htype)) { /* passive => active change OR header type change in active mode */ - return 1; + /* Return > 1 if only active changed to INET_ONCE -> direct read if + header type is unchanged. */ + /* XXX fprintf(stderr,"desc->htype == %d, old_htype == %d, + desc->active == %d, old_active == %d\r\n",(int)desc->htype, + (int) old_htype, (int) desc->active, (int) old_active );*/ + return 1+(desc->htype == old_htype && desc->active == INET_ONCE); } return 0; } @@ -7592,17 +7633,27 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, case INET_REQ_SETOPTS: { /* set options */ DEBUGF(("inet_ctl(%ld): SETOPTS\r\n", (long)desc->port)); + /* XXX fprintf(stderr,"inet_ctl(%ld): SETOPTS (len = %d)\r\n", (long)desc->port,(int) len); */ switch(inet_set_opts(desc, buf, len)) { case -1: return ctl_error(EINVAL, rbuf, rsize); case 0: return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); - default: /* active/passive change!! */ + case 1: /* * Let's hope that the descriptor really is a tcp_descriptor here. */ + /* fprintf(stderr,"Triggered tcp_deliver by setopt.\r\n"); */ tcp_deliver((tcp_descriptor *) desc, 0); return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); + default: + /* fprintf(stderr,"Triggered tcp_recv by setopt.\r\n"); */ + /* + * Same as above, but active changed to once w/o header type + * change, so try a read instead of just deliver. + */ + tcp_recv((tcp_descriptor *) desc, 0); + return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } } @@ -7753,7 +7804,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (desc->state != INET_STATE_OPEN) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (inet_set_address(desc->sfamily, &local, buf, &len) == NULL) + if (inet_set_faddress(desc->sfamily, &local, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len))) @@ -9196,6 +9247,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) #endif ASSERT(!INETP(desc)->is_ignored); DEBUGF(("tcp_inet_input(%ld) {s=%d\r\n", port, desc->inet.s)); + /* XXX fprintf(stderr,"tcp_inet_input(%ld) {s=%d}\r\n",(long) desc->inet.port, desc->inet.s); */ if (desc->inet.state == INET_STATE_ACCEPTING) { SOCKET s; unsigned int len; @@ -10117,12 +10169,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, case SCTP_REQ_BINDX: { /* Multi-homing bind for SCTP: */ - /* Construct the list of addresses we bind to. The curr limit is - 256 addrs. Buff structure: Flags(1), ListItem,...: + /* Add additional addresses by calling sctp_bindx with one address + at a time, since this is what some OSes promise will work. + Buff structure: Flags(1), ListItem,...: */ - struct sockaddr addrs[256]; + inet_address addr; char* curr; - int add_flag, n, rflag; + int add_flag, rflag; if (!IS_SCTP(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); @@ -10131,27 +10184,22 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, add_flag = get_int8(curr); curr++; - for(n=0; n < 256 && curr < buf+len; n++) + /* Make the real flags: */ + rflag = add_flag ? SCTP_BINDX_ADD_ADDR : SCTP_BINDX_REM_ADDR; + + while (curr < buf+len) { - /* List item format: Port(2), IP(4|16) -- compatible with - "inet_set_address": */ - inet_address tmp; + /* List item format: see "inet_set_faddress": */ ErlDrvSizeT alen = buf + len - curr; - curr = inet_set_address(desc->sfamily, &tmp, curr, &alen); + curr = inet_set_faddress(desc->sfamily, &addr, curr, &alen); if (curr == NULL) return ctl_error(EINVAL, rbuf, rsize); - /* Now: we need to squeeze "tmp" into the size of "sockaddr", - which is smaller than "tmp" for IPv6 (extra IN6 info will - be cut off): */ - memcpy(addrs + n, &tmp, sizeof(struct sockaddr)); + /* Invoke the call: */ + if (p_sctp_bindx(desc->s, (struct sockaddr *)&addr, 1, + rflag) < 0) + return ctl_error(sock_errno(), rbuf, rsize); } - /* Make the real flags: */ - rflag = add_flag ? SCTP_BINDX_ADD_ADDR : SCTP_BINDX_REM_ADDR; - - /* Invoke the call: */ - if (p_sctp_bindx(desc->s, addrs, n, rflag) < 0) - return ctl_error(sock_errno(), rbuf, rsize); desc->state = INET_STATE_BOUND; diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index ad112f7590..b250bac4dc 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -107,8 +107,8 @@ static void *ef_safe_alloc(Uint s) { void *p = EF_ALLOC(s); if (!p) erl_exit(1, - "unix efile drv: Can't allocate %d bytes of memory\n", - s); + "unix efile drv: Can't allocate %lu bytes of memory\n", + (unsigned long)s); return p; } @@ -118,8 +118,8 @@ static void *ef_safe_realloc(void *op, Uint s) { void *p = EF_REALLOC(op, s); if (!p) erl_exit(1, - "unix efile drv: Can't reallocate %d bytes of memory\n", - s); + "unix efile drv: Can't reallocate %lu bytes of memory\n", + (unsigned long)s); return p; } diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c index 6b45b92cbe..5df399aa5d 100644 --- a/erts/emulator/drivers/win32/win_con.c +++ b/erts/emulator/drivers/win32/win_con.c @@ -569,7 +569,7 @@ FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) return 1; case WM_SETFOCUS : CreateCaret(hClientWnd, NULL, cxChar, cyChar); - SetCaretPos(GetXFromCurrentY(GetDC(hwnd),0,cur_x), (cur_y-iVscrollPos)*cyChar); + SetCaretPos(GetXFromCurrentY(GetDC(hClientWnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar); ShowCaret(hClientWnd); return 0; case WM_KILLFOCUS: diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c index e20a8a7969..651d0e3a75 100644 --- a/erts/emulator/hipe/hipe_arm.c +++ b/erts/emulator/hipe/hipe_arm.c @@ -181,11 +181,9 @@ void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process * curseg.base = base; curseg.code_pos = base; curseg.tramp_pos = (unsigned int*)((char*)base + SEGMENT_NRBYTES); -#if defined(__arm__) curseg.tramp_pos -= 2; curseg.tramp_pos[0] = 0xE51FF004; /* ldr pc, [pc,#-4] */ curseg.tramp_pos[1] = (unsigned int)&nbif_callemu; -#endif address = try_alloc(nrwords, nrcallees, callees, trampvec); if (!address) { @@ -214,11 +212,9 @@ static unsigned int *alloc_stub(Uint nrwords, unsigned int **tramp_callemu) curseg.base = base; curseg.code_pos = base; curseg.tramp_pos = (unsigned int*)((char*)base + SEGMENT_NRBYTES); -#if defined(__arm__) curseg.tramp_pos -= 2; curseg.tramp_pos[0] = 0xE51FF004; /* ldr pc, [pc,#-4] */ curseg.tramp_pos[1] = (unsigned int)&nbif_callemu; -#endif address = try_alloc(nrwords, 0, NIL, NULL); if (!address) { @@ -269,10 +265,8 @@ int hipe_patch_insn(void *address, Uint32 value, Eterm type) void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) { unsigned int *code; -#if defined(__arm__) unsigned int *tramp_callemu; int callemu_offset; -#endif /* * Native code calls BEAM via a stub looking as follows: @@ -288,13 +282,6 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) * (Trampolines are allowed to modify r12, but they don't.) */ -#if !defined(__arm__) - /* verify that 'ba' can reach nbif_callemu */ - if ((unsigned long)&nbif_callemu & ~0x01FFFFFCUL) - abort(); -#endif - -#if defined(__arm__) code = alloc_stub(4, &tramp_callemu); callemu_offset = ((int)&nbif_callemu - ((int)&code[2] + 8)) >> 2; if (!(callemu_offset >= -0x00800000 && callemu_offset <= 0x007FFFFF)) { @@ -302,11 +289,7 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) if (!(callemu_offset >= -0x00800000 && callemu_offset <= 0x007FFFFF)) abort(); } -#else - code = alloc_stub(4, &trampoline); -#endif -#if defined(__arm__) /* mov r0, #beamArity */ code[0] = 0xE3A00000 | (beamArity & 0xFF); /* ldr r8, [pc,#0] // beamAddress */ @@ -315,16 +298,6 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) code[2] = 0xEA000000 | (callemu_offset & 0x00FFFFFF); /* .long beamAddress */ code[3] = (unsigned int)beamAddress; -#else - /* addi r12,0,beamAddress@l */ - code[0] = 0x39800000 | ((unsigned long)beamAddress & 0xFFFF); - /* addi r0,0,beamArity */ - code[1] = 0x38000000 | (beamArity & 0x7FFF); - /* addis r12,r12,beamAddress@ha */ - code[2] = 0x3D8C0000 | at_ha((unsigned long)beamAddress); - /* ba nbif_callemu */ - code[3] = 0x48000002 | (unsigned long)&nbif_callemu; -#endif hipe_flush_icache_range(code, 4*sizeof(int)); @@ -334,60 +307,32 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) static void patch_b(Uint32 *address, Sint32 offset, Uint32 AA) { Uint32 oldI = *address; -#if defined(__arm__) Uint32 newI = (oldI & 0xFF000000) | (offset & 0x00FFFFFF); -#else - Uint32 newI = (oldI & 0xFC000001) | ((offset & 0x00FFFFFF) << 2) | (AA & 2); -#endif *address = newI; hipe_flush_icache_word(address); } int hipe_patch_call(void *callAddress, void *destAddress, void *trampoline) { -#if !defined(__arm__) - if ((Uint32)destAddress == ((Uint32)destAddress & 0x01FFFFFC)) { - /* The destination is in the [0,32MB[ range. - We can reach it with a ba/bla instruction. - This is the typical case for BIFs and primops. - It's also common for trap-to-BEAM stubs (on ppc32). */ - patch_b((Uint32*)callAddress, (Uint32)destAddress >> 2, 2); + Sint32 destOffset = ((Sint32)destAddress - ((Sint32)callAddress+8)) >> 2; + if (destOffset >= -0x800000 && destOffset <= 0x7FFFFF) { + /* The destination is within a [-32MB,+32MB[ range from us. + We can reach it with a b/bl instruction. + This is typical for nearby Erlang code. */ + patch_b((Uint32*)callAddress, destOffset, 0); } else { -#endif -#if defined(__arm__) - Sint32 destOffset = ((Sint32)destAddress - ((Sint32)callAddress+8)) >> 2; -#else - Sint32 destOffset = ((Sint32)destAddress - (Sint32)callAddress) >> 2; -#endif - if (destOffset >= -0x800000 && destOffset <= 0x7FFFFF) { - /* The destination is within a [-32MB,+32MB[ range from us. - We can reach it with a b/bl instruction. - This is typical for nearby Erlang code. */ - patch_b((Uint32*)callAddress, destOffset, 0); - } else { - /* The destination is too distant for b/bl/ba/bla. - Must do a b/bl to the trampoline. */ -#if defined(__arm__) - Sint32 trampOffset = ((Sint32)trampoline - ((Sint32)callAddress+8)) >> 2; -#else - Sint32 trampOffset = ((Sint32)trampoline - (Sint32)callAddress) >> 2; -#endif - if (trampOffset >= -0x800000 && trampOffset <= 0x7FFFFF) { - /* Update the trampoline's address computation. - (May be redundant, but we can't tell.) */ -#if defined(__arm__) - patch_imm32((Uint32*)trampoline+1, (Uint32)destAddress); -#else - patch_li((Uint32*)trampoline, (Uint32)destAddress); -#endif - /* Update this call site. */ - patch_b((Uint32*)callAddress, trampOffset, 0); - } else - return -1; - } -#if !defined(__arm__) + /* The destination is too distant for b/bl. + Must do a b/bl to the trampoline. */ + Sint32 trampOffset = ((Sint32)trampoline - ((Sint32)callAddress+8)) >> 2; + if (trampOffset >= -0x800000 && trampOffset <= 0x7FFFFF) { + /* Update the trampoline's address computation. + (May be redundant, but we can't tell.) */ + patch_imm32((Uint32*)trampoline+1, (Uint32)destAddress); + /* Update this call site. */ + patch_b((Uint32*)callAddress, trampOffset, 0); + } else + return -1; } -#endif return 0; } diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4 index e0c6f09796..17c013f1fb 100644 --- a/erts/emulator/hipe/hipe_arm_bifs.m4 +++ b/erts/emulator/hipe/hipe_arm_bifs.m4 @@ -27,7 +27,7 @@ include(`hipe/hipe_arm_asm.m4') .p2align 2 `#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) -# define CALL_BIF(F) mov r14, #F; str r14, [r0, #P_BIF_CALLEE]; bl hipe_debug_bif_wrapper +# define CALL_BIF(F) ldr r14, =F; str r14, [r0, #P_BIF_CALLEE]; bl hipe_debug_bif_wrapper #else # define CALL_BIF(F) bl F #endif' @@ -67,6 +67,7 @@ $1: RESTORE_CONTEXT_BIF beq nbif_1_simple_exception NBIF_RET(1) + .ltorg /* needed by LDR in debug version of `CALL_BIF' */ .size $1, .-$1 .type $1, %function #endif') @@ -95,6 +96,7 @@ $1: RESTORE_CONTEXT_BIF beq nbif_2_simple_exception NBIF_RET(2) + .ltorg .size $1, .-$1 .type $1, %function #endif') @@ -125,6 +127,7 @@ $1: RESTORE_CONTEXT_BIF beq nbif_3_simple_exception NBIF_RET(3) + .ltorg .size $1, .-$1 .type $1, %function #endif') @@ -149,6 +152,7 @@ $1: RESTORE_CONTEXT_BIF beq nbif_0_simple_exception NBIF_RET(0) + .ltorg .size $1, .-$1 .type $1, %function #endif') @@ -173,7 +177,8 @@ $1: /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_GC - bl $2 + /* ignore empty BIF__ARGS */ + CALL_BIF($2) TEST_GOT_MBUF(0) /* Restore registers. */ @@ -195,7 +200,9 @@ $1: /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_GC - bl $2 + str r1, [r0, #P_ARG0] /* Store BIF__ARGS in def_arg_reg[] */ + add r1, r0, #P_ARG0 + CALL_BIF($2) TEST_GOT_MBUF(1) /* Restore registers. Check for exception. */ @@ -220,7 +227,10 @@ $1: /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_GC - bl $2 + str r1, [r0, #P_ARG0] /* Store BIF__ARGS in def_arg_reg[] */ + str r2, [r0, #P_ARG1] + add r1, r0, #P_ARG0 + CALL_BIF($2) TEST_GOT_MBUF(2) /* Restore registers. Check for exception. */ diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 26f183dc25..23ced284bf 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1093,10 +1093,8 @@ BIF_RETTYPE hipe_bifs_make_fun_3(BIF_ALIST_3) if (is_not_nil(free_vars)) BIF_ERROR(BIF_P, BADARG); -#ifndef HYBRID /* FIND ME! */ funp->next = MSO(BIF_P).funs; MSO(BIF_P).funs = funp; -#endif BIF_RET(make_fun(funp)); } @@ -1757,6 +1755,41 @@ BIF_RETTYPE hipe_bifs_mark_referred_from_1(BIF_ALIST_1) /* get_refs_from */ BIF_RET(NIL); } +/* Called by init:restart after unloading all hipe compiled modules + * to work around bug causing execution of deallocated beam code. + * Can be removed when delete/purge of native modules works better. + * Test: Do init:restart in debug compiled vm with hipe compiled kernel. + */ +static void hipe_purge_all_refs(void) +{ + struct hipe_mfa_info **bucket; + unsigned int i, nrbuckets; + + hipe_mfa_info_table_lock(); + + bucket = hipe_mfa_info_table.bucket; + nrbuckets = 1 << hipe_mfa_info_table.log2size; + for (i = 0; i < nrbuckets; ++i) { + while (bucket[i] != NULL) { + struct hipe_mfa_info* mfa = bucket[i]; + bucket[i] = mfa->bucket.next; + + while (mfa->refers_to) { + struct hipe_mfa_info_list *to = mfa->refers_to; + mfa->refers_to = to->next; + erts_free(ERTS_ALC_T_HIPE, to); + } + while (mfa->referred_from) { + struct ref* from = mfa->referred_from; + mfa->referred_from = from->next; + erts_free(ERTS_ALC_T_HIPE, from); + } + erts_free(ERTS_ALC_T_HIPE, mfa); + } + } + hipe_mfa_info_table_unlock(); +} + BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1) { struct mfa mfa; @@ -1764,6 +1797,11 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1) struct hipe_mfa_info_list *refers_to, *tmp_refers_to; struct ref **prev, *ref; + if (BIF_ARG_1 == am_all) { + hipe_purge_all_refs(); + BIF_RET(NIL); + } + if (!term_to_mfa(BIF_ARG_1, &mfa)) BIF_ERROR(BIF_P, BADARG); hipe_mfa_info_table_lock(); @@ -1801,6 +1839,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1) BIF_RET(NIL); } + /* redirect_referred_from(CalleeMFA) * Redirect all pending-redirect refs in CalleeMFA's referred_from. * Then remove any pending-redirect && pending-remove refs from CalleeMFA's referred_from. diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c index 87cdfb8c7a..64de754e18 100644 --- a/erts/emulator/hipe/hipe_bif1.c +++ b/erts/emulator/hipe/hipe_bif1.c @@ -449,7 +449,7 @@ BIF_RETTYPE hipe_bifs_gc_info_0(BIF_ALIST_0) BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ -#if !(defined(BM_COUNTERS) && defined(HYBRID)) +#if !(defined(BM_COUNTERS)) Uint minor_global_gc = 0; Uint major_global_gc = 0; #endif @@ -459,17 +459,9 @@ BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0) #endif Eterm *hp; -#if defined(HYBRID) - Uint tmp_used_heap = (Uint)((BIF_P->htop - BIF_P->heap) + - (OLD_HTOP(BIF_P) - OLD_HEAP(BIF_P)) + - MBUF_SIZE(BIF_P)); - Uint tmp_allocated_heap = (Uint)((BIF_P->hend - BIF_P->heap) + - (OLD_HEND(BIF_P) - OLD_HEAP(BIF_P)) + - MBUF_SIZE(BIF_P)); -#else Uint tmp_used_heap = 0; Uint tmp_allocated_heap = 0; -#endif + hp = HAlloc(BIF_P, 7); BIF_RET(TUPLE6(hp, make_small((uint)minor_global_gc), @@ -486,7 +478,7 @@ BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0) BIF_RETTYPE hipe_bifs_incremental_gc_info_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ -#if !(defined(BM_COUNTERS) && defined(INCREMENTAL)) +#if !defined(BM_COUNTERS) Uint minor_gc_cycles = 0; Uint major_gc_cycles = 0; Uint minor_gc_stages = 0; @@ -512,17 +504,6 @@ BIF_RETTYPE hipe_bifs_gc_info_clear_0(BIF_ALIST_0) #ifdef BM_COUNTERS minor_gc = 0; major_gc = 0; -#ifdef HYBRID - minor_global_gc = 0; - major_global_gc = 0; - gc_in_copy = 0; -#ifdef INCREMENTAL - minor_gc_cycles = 0; - major_gc_cycles = 0; - minor_gc_stages = 0; - major_gc_stages = 0; -#endif -#endif #endif #ifdef BM_HEAP_SIZES diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index ee97541e15..37a1cc193b 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -151,22 +151,6 @@ BIF_RETTYPE hipe_bifs_modeswitch_debug_off_0(BIF_ALIST_0) BIF_RET(am_true); } -/* BIFs for handling the message area */ - -BIF_RETTYPE hipe_bifs_show_message_area_0(BIF_ALIST_0) -{ -#ifdef HYBRID -#ifdef DEBUG - print_message_area(); -#else - printf("Only available in debug compiled emulator\r\n"); -#endif - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - #if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) BIF_RETTYPE hipe_debug_bif_wrapper(BIF_ALIST_1); diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab index 51323ce7af..aac27e8bed 100644 --- a/erts/emulator/hipe/hipe_bif2.tab +++ b/erts/emulator/hipe/hipe_bif2.tab @@ -29,4 +29,3 @@ bif hipe_bifs:show_term/1 bif hipe_bifs:in_native/0 bif hipe_bifs:modeswitch_debug_on/0 bif hipe_bifs:modeswitch_debug_off/0 -bif hipe_bifs:show_message_area/0 diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c index e0575c35ff..07e4b8a4d6 100644 --- a/erts/emulator/hipe/hipe_gc.c +++ b/erts/emulator/hipe/hipe_gc.c @@ -237,329 +237,3 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) } abort(); } - -#ifdef HYBRID - -#ifdef INCREMENTAL -Eterm *ma_fullsweep_nstack(Process *p, Eterm *n_htop, Eterm *n_hend) -{ - /* known nstack walk state */ - Eterm *nsp; - Eterm *nsp_end; - const struct sdesc *sdesc; - unsigned int sdesc_size; - unsigned long ra; - unsigned int i; - unsigned int mask; - /* arch-specific nstack walk state */ - struct nstack_walk_state walk_state; - - if (!nstack_walk_init_check(p)) - return n_htop; - - nsp = nstack_walk_nsp_begin(p); - nsp_end = nstack_walk_nsp_end(p); - - sdesc = nstack_walk_init_sdesc(p, &walk_state); - - for (;;) { - if (nstack_walk_nsp_reached_end(nsp, nsp_end)) { - if (nsp == nsp_end) - return n_htop; - fprintf(stderr, "%s: passed end of stack\r\n", __FUNCTION__); - break; - } - sdesc_size = nstack_walk_frame_size(sdesc); - i = 0; - mask = sdesc->livebits[0]; - for (;;) { - if (mask & 1) { - Eterm *nsp_i = nstack_walk_frame_index(nsp, i); - Eterm val = *nsp_i; - Eterm *obj_ptr = ptr_val(val); - switch (primary_tag(val)) { - case TAG_PRIMARY_LIST: - COPYMARK_CONS(obj_ptr, n_htop, nsp_i, n_hend); - break; - case TAG_PRIMARY_BOXED: - COPYMARK_BOXED(obj_ptr, n_htop, nsp_i, n_hend); - break; - default: - break; - } - } - if (++i >= sdesc_size) - break; - if (i & 31) - mask >>= 1; - else - mask = sdesc->livebits[i >> 5]; - } - ra = nstack_walk_frame_ra(nsp, sdesc); - if (ra == (unsigned long)nbif_stack_trap_ra) - ra = (unsigned long)p->hipe.ngra; - sdesc = hipe_find_sdesc(ra); - nsp = nstack_walk_next_frame(nsp, sdesc_size); - } - abort(); -} - -void ma_gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) -{ - /* known nstack walk state */ - Eterm *nsp; - Eterm *nsp_end; - const struct sdesc *sdesc; - unsigned int sdesc_size; - unsigned long ra; - unsigned int i; - unsigned int mask; - /* arch-specific nstack walk state */ - struct nstack_walk_state walk_state; - - /* ma_gensweep-specific state */ - Eterm *low_water, *high_water, *surface; - Eterm *n_htop; - Eterm *old_htop; - - if (!nstack_walk_init_check(p)) - return; - - nsp = nstack_walk_nsp_begin(p); - nsp_end = nstack_walk_nsp_end(p); - - low_water = global_heap; - //high_water = global_high_water; - surface = global_htop; - - old_htop = *ptr_old_htop; - n_htop = *ptr_n_htop; - - sdesc = nstack_walk_init_sdesc(p, &walk_state); - - for (;;) { - if (nstack_walk_nsp_reached_end(nsp, nsp_end)) { - if (nsp == nsp_end) { - *ptr_old_htop = old_htop; - *ptr_n_htop = n_htop; - return; - } - fprintf(stderr, "%s: passed end of stack\r\n", __FUNCTION__); - break; - } - sdesc_size = nstack_walk_frame_size(sdesc); - i = 0; - mask = sdesc->livebits[0]; - for (;;) { - if (mask & 1) { - Eterm *nsp_i = nstack_walk_frame_index(nsp, i); - Eterm gval = *nsp_i; - if (is_boxed(gval)) { - Eterm *ptr = boxed_val(gval); - Eterm val = *ptr; - if (MY_IS_MOVED(val)) { - *nsp_i = val; - } else if (ptr_within(ptr, low_water, high_water)) { - MOVE_BOXED(ptr, val, old_htop, nsp_i); - } else if (ptr_within(ptr, high_water, surface)) { - MOVE_BOXED(ptr, val, n_htop, nsp_i); - } - } else if (is_list(gval)) { - Eterm *ptr = list_val(gval); - Eterm val = *ptr; - if (is_non_value(val)) { - *nsp_i = ptr[1]; - } else if (ptr_within(ptr, low_water, high_water)) { - MOVE_CONS(ptr, val, old_htop, nsp_i); - } else if (ptr_within(ptr, high_water, surface)) { - MOVE_CONS(ptr, val, n_htop, nsp_i); - } - } - } - if (++i >= sdesc_size) - break; - if (i & 31) - mask >>= 1; - else - mask = sdesc->livebits[i >> 5]; - } - ra = nstack_walk_frame_ra(nsp, sdesc); - if (ra == (unsigned long)nbif_stack_trap_ra) - ra = (unsigned long)p->hipe.ngra; - sdesc = hipe_find_sdesc(ra); - nsp = nstack_walk_next_frame(nsp, sdesc_size); - } - abort(); -} - -#else /* not INCREMENTAL */ - -Eterm *ma_fullsweep_nstack(Process *p, Eterm *n_htop) -{ - /* known nstack walk state */ - Eterm *nsp; - Eterm *nsp_end; - const struct sdesc *sdesc; - unsigned int sdesc_size; - unsigned long ra; - unsigned int i; - unsigned int mask; - /* arch-specific nstack walk state */ - struct nstack_walk_state walk_state; - - /* ma_fullsweep-specific state */ - Eterm *gheap = global_heap; - Eterm *ghtop = global_htop; - Eterm *goheap = global_old_heap; - Eterm *gohtop = global_old_htop; - - if (!nstack_walk_init_check(p)) - return n_htop; - - nsp = nstack_walk_nsp_begin(p); - nsp_end = nstack_walk_nsp_end(p); - - sdesc = nstack_walk_init_sdesc(p, &walk_state); - - for (;;) { - if (nstack_walk_nsp_reached_end(nsp, nsp_end)) { - if (nsp == nsp_end) - return n_htop; - fprintf(stderr, "%s: passed end of stack\r\n", __FUNCTION__); - break; - } - sdesc_size = nstack_walk_frame_size(sdesc); - i = 0; - mask = sdesc->livebits[0]; - for (;;) { - if (mask & 1) { - Eterm *nsp_i = nstack_walk_frame_index(nsp, i); - Eterm gval = *nsp_i; - if (is_boxed(gval)) { - Eterm *ptr = boxed_val(gval); - Eterm val = *ptr; - if (MY_IS_MOVED(val)) { - *nsp_i = val; - } else if (ptr_within(ptr, gheap, ghtop)) { - MOVE_BOXED(ptr, val, n_htop, nsp_i); - } else if (ptr_within(ptr, goheap, gohtop)) { - MOVE_BOXED(ptr, val, n_htop, nsp_i); - } - } else if (is_list(gval)) { - Eterm *ptr = list_val(gval); - Eterm val = *ptr; - if (is_non_value(val)) { - *nsp_i = ptr[1]; - } else if (ptr_within(ptr, gheap, ghtop)) { - MOVE_CONS(ptr, val, n_htop, nsp_i); - } else if (ptr_within(ptr, gheap, ghtop)) { - MOVE_CONS(ptr, val, n_htop, nsp_i); - } - } - } - if (++i >= sdesc_size) - break; - if (i & 31) - mask >>= 1; - else - mask = sdesc->livebits[i >> 5]; - } - ra = nstack_walk_frame_ra(nsp, sdesc); - if (ra == (unsigned long)nbif_stack_trap_ra) - ra = (unsigned long)p->hipe.ngra; - sdesc = hipe_find_sdesc(ra); - nsp = nstack_walk_next_frame(nsp, sdesc_size); - } - abort(); -} - -void ma_gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) -{ - /* known nstack walk state */ - Eterm *nsp; - Eterm *nsp_end; - const struct sdesc *sdesc; - unsigned int sdesc_size; - unsigned long ra; - unsigned int i; - unsigned int mask; - /* arch-specific nstack walk state */ - struct nstack_walk_state walk_state; - - /* ma_gensweep-specific state */ - Eterm *low_water, *high_water, *surface; - Eterm *n_htop; - Eterm *old_htop; - - if (!nstack_walk_init_check(p)) - return; - - nsp = nstack_walk_nsp_begin(p); - nsp_end = nstack_walk_nsp_end(p); - - low_water = global_heap; - high_water = global_high_water; - surface = global_htop; - - old_htop = *ptr_old_htop; - n_htop = *ptr_n_htop; - - sdesc = nstack_walk_init_sdesc(p, &walk_state); - - for (;;) { - if (nstack_walk_nsp_reached_end(nsp, nsp_end)) { - if (nsp == nsp_end) { - *ptr_old_htop = old_htop; - *ptr_n_htop = n_htop; - return; - } - fprintf(stderr, "%s: passed end of stack\r\n", __FUNCTION__); - break; - } - sdesc_size = nstack_walk_frame_size(sdesc); - i = 0; - mask = sdesc->livebits[0]; - for (;;) { - if (mask & 1) { - Eterm *nsp_i = nstack_walk_frame_index(nsp, i); - Eterm gval = *nsp_i; - if (is_boxed(gval)) { - Eterm *ptr = boxed_val(gval); - Eterm val = *ptr; - if (MY_IS_MOVED(val)) { - *nsp_i = val; - } else if (ptr_within(ptr, low_water, high_water)) { - MOVE_BOXED(ptr, val, old_htop, nsp_i); - } else if (ptr_within(ptr, high_water, surface)) { - MOVE_BOXED(ptr, val, n_htop, nsp_i); - } - } else if (is_list(gval)) { - Eterm *ptr = list_val(gval); - Eterm val = *ptr; - if (is_non_value(val)) { - *nsp_i = ptr[1]; - } else if (ptr_within(ptr, low_water, high_water)) { - MOVE_CONS(ptr, val, old_htop, nsp_i); - } else if (ptr_within(ptr, high_water, surface)) { - MOVE_CONS(ptr, val, n_htop, nsp_i); - } - } - } - if (++i >= sdesc_size) - break; - if (i & 31) - mask >>= 1; - else - mask = sdesc->livebits[i >> 5]; - } - ra = nstack_walk_frame_ra(nsp, sdesc); - if (ra == (unsigned long)nbif_stack_trap_ra) - ra = (unsigned long)p->hipe.ngra; - sdesc = hipe_find_sdesc(ra); - nsp = nstack_walk_next_frame(nsp, sdesc_size); - } - abort(); -} -#endif /* INCREMENTAL */ - -#endif /* HYBRID */ diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index d07d14028c..6e9041c84a 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -467,15 +467,11 @@ static const struct rts_param { int value; } rts_params[] = { { 1, "P_OFF_HEAP_FUNS", -#if !defined(HYBRID) 1, offsetof(struct process, off_heap.first) -#endif }, { 4, "EFT_NEXT", -#if !defined(HYBRID) 1, offsetof(struct erl_fun_thing, next) -#endif }, /* These are always defined, but their values depend on the diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4 index d09551d10d..7caf0c9a2d 100644 --- a/erts/emulator/hipe/hipe_ppc_bifs.m4 +++ b/erts/emulator/hipe/hipe_ppc_bifs.m4 @@ -32,12 +32,12 @@ include(`hipe/hipe_ppc_asm.m4') .text .p2align 2 -define(TEST_GOT_MBUF,`LOAD r4, P_MBUF(P) # `TEST_GOT_MBUF' +define(TEST_GOT_MBUF,`LOAD r4, P_MBUF(P) /* `TEST_GOT_MBUF' */ CMPI r4, 0 bne- 3f 2:') define(HANDLE_GOT_MBUF,` -3: bl CSYM(nbif_$1_gc_after_bif) # `HANDLE_GOT_MBUF' +3: bl CSYM(nbif_$1_gc_after_bif) /* `HANDLE_GOT_MBUF' */ b 2b') @@ -62,7 +62,7 @@ ASYM($1): /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_BIF - STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[] + STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */ addi r4, r3, P_ARG0 CALL_BIF($2) TEST_GOT_MBUF @@ -92,7 +92,7 @@ ASYM($1): /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_BIF - STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[] + STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */ STORE r5, P_ARG1(r3) addi r4, r3, P_ARG0 CALL_BIF($2) @@ -124,7 +124,7 @@ ASYM($1): /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_BIF - STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[] + STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */ STORE r5, P_ARG1(r3) STORE r6, P_ARG2(r3) addi r4, r3, P_ARG0 @@ -214,7 +214,7 @@ ASYM($1): /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_GC - STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[] + STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */ addi r4, r3, P_ARG0 CALL_BIF($2) TEST_GOT_MBUF @@ -244,7 +244,7 @@ ASYM($1): /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_GC - STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[] + STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */ STORE r5, P_ARG1(r3) addi r4, r3, P_ARG0 CALL_BIF($2) diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h index 4c14b4a519..4e3076caf5 100644 --- a/erts/emulator/hipe/hipe_stack.h +++ b/erts/emulator/hipe/hipe_stack.h @@ -116,13 +116,4 @@ extern int hipe_fill_stacktrace(Process*, int, Eterm**); extern Eterm *fullsweep_nstack(Process *p, Eterm *n_htop); extern void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop); -#ifdef HYBRID -#ifdef INCREMENTAL -extern Eterm *ma_fullsweep_nstack(Process *p, Eterm *n_htop, Eterm *n_hend); -#else -extern Eterm *ma_fullsweep_nstack(Process *p, Eterm *n_htop); -#endif -extern void ma_gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop); -#endif /* HYBRID */ - #endif /* HIPE_STACK_H */ diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index f94e0f2296..0e8395ea8a 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -551,7 +551,7 @@ erl_sys_init(void) size_t bindirsz = sizeof(bindir); Uint csp_path_sz; - res = erts_sys_getenv("BINDIR", bindir, &bindirsz); + res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz); if (res != 0) { if (res < 0) erl_exit(-1, @@ -708,7 +708,7 @@ prepare_crash_dump(void) } envsz = sizeof(env); - i = erts_sys_getenv("ERL_CRASH_DUMP_NICE", env, &envsz); + i = erts_sys_getenv_raw("ERL_CRASH_DUMP_NICE", env, &envsz); if (i >= 0) { int nice_val; nice_val = i != 0 ? 0 : atoi(env); @@ -719,7 +719,7 @@ prepare_crash_dump(void) } envsz = sizeof(env); - i = erts_sys_getenv("ERL_CRASH_DUMP_SECONDS", env, &envsz); + i = erts_sys_getenv_raw("ERL_CRASH_DUMP_SECONDS", env, &envsz); if (i >= 0) { unsigned sec; sec = (unsigned) i != 0 ? 0 : atoi(env); @@ -1163,6 +1163,8 @@ static int set_driver_data(int port_num, report_exit_list = report_exit; } + erts_port[port_num].os_pid = pid; + if (read_write & DO_READ) { driver_data[ifd].packet_bytes = packet_bytes; driver_data[ifd].port_num = port_num; @@ -1354,9 +1356,9 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op int no_vfork; size_t no_vfork_sz = sizeof(no_vfork); - no_vfork = (erts_sys_getenv("ERL_NO_VFORK", - (char *) &no_vfork, - &no_vfork_sz) >= 0); + no_vfork = (erts_sys_getenv_raw("ERL_NO_VFORK", + (char *) &no_vfork, + &no_vfork_sz) >= 0); #endif switch (opts->read_write) { @@ -2360,21 +2362,31 @@ void sys_get_pid(char *buffer){ } int -erts_sys_putenv(char *buffer, int sep_ix) +erts_sys_putenv_raw(char *key, char *value) { + return erts_sys_putenv(key, value); +} +int +erts_sys_putenv(char *key, char *value) { int res; char *env; + Uint need = strlen(key) + strlen(value) + 2; + #ifdef HAVE_COPYING_PUTENV - env = buffer; + env = erts_alloc(ERTS_ALC_T_TMP, need); #else - Uint sz = strlen(buffer)+1; - env = erts_alloc(ERTS_ALC_T_PUTENV_STR, sz); - erts_smp_atomic_add_nob(&sys_misc_mem_sz, sz); - strcpy(env,buffer); + env = erts_alloc(ERTS_ALC_T_PUTENV_STR, need); + erts_smp_atomic_add_nob(&sys_misc_mem_sz, need); #endif + strcpy(env,key); + strcat(env,"="); + strcat(env,value); erts_smp_rwmtx_rwlock(&environ_rwmtx); res = putenv(env); erts_smp_rwmtx_rwunlock(&environ_rwmtx); +#ifdef HAVE_COPYING_PUTENV + erts_free(ERTS_ALC_T_TMP, env); +#endif return res; } @@ -2401,6 +2413,11 @@ erts_sys_getenv__(char *key, char *value, size_t *size) } int +erts_sys_getenv_raw(char *key, char *value, size_t *size) { + return erts_sys_getenv(key, value, size); +} + +int erts_sys_getenv(char *key, char *value, size_t *size) { int res; @@ -2999,7 +3016,7 @@ erl_sys_args(int* argc, char** argv) if (erts_use_kernel_poll) { char no_kp[10]; size_t no_kp_sz = sizeof(no_kp); - int res = erts_sys_getenv("ERL_NO_KERNEL_POLL", no_kp, &no_kp_sz); + int res = erts_sys_getenv_raw("ERL_NO_KERNEL_POLL", no_kp, &no_kp_sz); if (res > 0 || (res == 0 && sys_strcmp("false", no_kp) != 0 diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 03298a6c54..1e7b555f42 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -171,8 +171,8 @@ extern clock_t sys_times(SysTimes *buffer); extern char *win_build_environment(char *); typedef struct { - char *environment_strings; - char *next_string; + WCHAR *environment_strings; + WCHAR *next_string; } GETENV_STATE; void erts_sys_env_init(void); diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index b106f0932d..29e2749800 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -68,9 +68,9 @@ static int async_write_file(struct async_io* aio, LPVOID buf, DWORD numToWrite); static int get_overlapped_result(struct async_io* aio, LPDWORD pBytesRead, BOOL wait); static BOOL create_child_process(char *, HANDLE, HANDLE, - HANDLE, LPHANDLE, BOOL, - LPVOID, LPTSTR, unsigned, - char **, int *); + HANDLE, LPHANDLE, LPDWORD, BOOL, + LPVOID, LPTSTR, unsigned, + char **, int *); static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL); static int application_type(const char* originalName, char fullPath[MAX_PATH], BOOL search_in_path, BOOL handle_quotes, @@ -1136,6 +1136,7 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) HANDLE hChildStdin = INVALID_HANDLE_VALUE; /* Child's stdin. */ HANDLE hChildStdout = INVALID_HANDLE_VALUE; /* Child's stout. */ HANDLE hChildStderr = INVALID_HANDLE_VALUE; /* Child's sterr. */ + DWORD pid; int close_child_stderr = 0; DriverData* dp; /* Pointer to driver data. */ ErlDrvData retval = ERL_DRV_ERROR_GENERAL; /* Return value. */ @@ -1203,14 +1204,13 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) */ DEBUGF(("Spawning \"%s\"\n", name)); - envir = win_build_environment(envir); /* Still an ansi environment, could be - converted to unicode for spawn_executable, but - that is not done (yet) */ + envir = win_build_environment(envir); /* Always a unicode environment */ ok = create_child_process(name, hChildStdin, hChildStdout, hChildStderr, &dp->port_pid, + &pid, opts->hide_window, (LPVOID) envir, (LPTSTR) opts->wd, @@ -1254,6 +1254,9 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) #endif retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write, opts->exit_status); + if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) + /* We assume that this cannot generate a negative number */ + erts_port[port_num].os_pid = (SWord) pid; } if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) @@ -1397,7 +1400,8 @@ create_child_process HANDLE hStdin, /* The standard input handle for child. */ HANDLE hStdout, /* The standard output handle for child. */ HANDLE hStderr, /* The standard error handle for child. */ - LPHANDLE phPid, /* Pointer to variable to received PID. */ + LPHANDLE phPid, /* Pointer to variable to received Process handle. */ + LPDWORD pdwID, /* Pointer to variable to received Process ID */ BOOL hide, /* Hide the window unconditionally. */ LPVOID env, /* Environment for the child */ LPTSTR wd, /* Working dir for the child */ @@ -1479,7 +1483,8 @@ create_child_process NULL, NULL, TRUE, - createFlags | staticCreateFlags, + createFlags | staticCreateFlags | + CREATE_UNICODE_ENVIRONMENT, env, wd, &siStartInfo, @@ -1607,7 +1612,8 @@ create_child_process NULL, NULL, TRUE, - createFlags | staticCreateFlags, + createFlags | staticCreateFlags | + CREATE_UNICODE_ENVIRONMENT, env, (WCHAR *) wd, &siStartInfo, @@ -1629,7 +1635,8 @@ create_child_process } CloseHandle(piProcInfo.hThread); /* Necessary to avoid resource leak. */ *phPid = piProcInfo.hProcess; - + *pdwID = piProcInfo.dwProcessId; + if (applType == APPL_DOS) { WaitForSingleObject(hProcess, 50); } diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c index 064745d418..658a77c824 100644 --- a/erts/emulator/sys/win32/sys_env.c +++ b/erts/emulator/sys/win32/sys_env.c @@ -1,271 +1,299 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2002-2011. 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% - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "sys.h" -#include "erl_sys_driver.h" -#include "erl_alloc.h" - -static char* merge_environment(char *current, char *add); -static char* arg_to_env(char **arg); -static char** env_to_arg(char *env); -static char** find_arg(char **arg, char *str); -static int compare(const void *a, const void *b); - -static erts_smp_rwmtx_t environ_rwmtx; - -void -erts_sys_env_init(void) -{ - erts_smp_rwmtx_init(&environ_rwmtx, "environ"); -} - -int -erts_sys_putenv(char *key_value, int sep_ix) -{ - int res; - char sep = key_value[sep_ix]; - ASSERT(sep == '='); - key_value[sep_ix] = '\0'; - erts_smp_rwmtx_rwlock(&environ_rwmtx); - res = (SetEnvironmentVariable((LPCTSTR) key_value, - (LPCTSTR) &key_value[sep_ix+1]) ? 0 : 1); - erts_smp_rwmtx_rwunlock(&environ_rwmtx); - key_value[sep_ix] = sep; - return res; -} - -int -erts_sys_getenv__(char *key, char *value, size_t *size) -{ - size_t req_size = 0; - int res = 0; - DWORD new_size; - - SetLastError(0); - new_size = GetEnvironmentVariable((LPCTSTR) key, - (LPTSTR) value, - (DWORD) *size); - res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0; - if (res < 0) - return res; - res = new_size > *size ? 1 : 0; - *size = new_size; - return res; -} - -int -erts_sys_getenv(char *key, char *value, size_t *size) -{ - int res; - erts_smp_rwmtx_rlock(&environ_rwmtx); - res = erts_sys_getenv__(key, value, size); - erts_smp_rwmtx_runlock(&environ_rwmtx); - return res; -} - -struct win32_getenv_state { - char *env; - char *next; -}; - - -void init_getenv_state(GETENV_STATE *state) -{ - erts_smp_rwmtx_rlock(&environ_rwmtx); - state->environment_strings = (char *) GetEnvironmentStrings(); - state->next_string = state->environment_strings; -} - -char *getenv_string(GETENV_STATE *state) -{ - ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx)); - if (state->next_string[0] == '\0') - return NULL; - else { - char *res = state->next_string; - state->next_string += sys_strlen(res) + 1; - return res; - } -} - -void fini_getenv_state(GETENV_STATE *state) -{ - FreeEnvironmentStrings(state->environment_strings); - state->environment_strings = state->next_string = NULL; - erts_smp_rwmtx_runlock(&environ_rwmtx); -} - -char* -win_build_environment(char* new_env) -{ - if (new_env == NULL) { - return NULL; - } else { - char *tmp, *merged; - - erts_smp_rwmtx_rlock(&environ_rwmtx); - tmp = GetEnvironmentStrings(); - merged = merge_environment(tmp, new_env); - - FreeEnvironmentStrings(tmp); - erts_smp_rwmtx_runlock(&environ_rwmtx); - return merged; - } -} - -static char* -merge_environment(char *old, char *add) -{ - char **a_arg = env_to_arg(add); - char **c_arg = env_to_arg(old); - char *ret; - int i, j; - - for(i = 0; c_arg[i] != NULL; ++i) - ; - - for(j = 0; a_arg[j] != NULL; ++j) - ; - - c_arg = erts_realloc(ERTS_ALC_T_TMP, - c_arg, (i+j+1) * sizeof(char *)); - - for(j = 0; a_arg[j] != NULL; ++j){ - char **tmp; - char *current = a_arg[j]; - char *eq_p = strchr(current,'='); - int unset = (eq_p!=NULL && eq_p[1]=='\0'); - - if ((tmp = find_arg(c_arg, current)) != NULL) { - if (!unset) { - *tmp = current; - } else { - *tmp = c_arg[--i]; - c_arg[i] = NULL; - } - } else if (!unset) { - c_arg[i++] = current; - c_arg[i] = NULL; - } - } - ret = arg_to_env(c_arg); - erts_free(ERTS_ALC_T_TMP, c_arg); - erts_free(ERTS_ALC_T_TMP, a_arg); - return ret; -} - -static char** -find_arg(char **arg, char *str) -{ - char *tmp; - int len; - - if ((tmp = strchr(str, '=')) != NULL) { - tmp++; - len = tmp - str; - while (*arg != NULL){ - if (_strnicmp(*arg, str, len) == 0){ - return arg; - } - ++arg; - } - } - return NULL; -} - -static int -compare(const void *a, const void *b) -{ - char *s1 = *((char **) a); - char *s2 = *((char **) b); - char *e1 = strchr(s1,'='); - char *e2 = strchr(s2,'='); - int ret; - int len; - - if(!e1) - e1 = s1 + strlen(s1); - if(!e2) - e2 = s2 + strlen(s2); - - if((e1 - s1) > (e2 - s2)) - len = (e2 - s2); - else - len = (e1 - s1); - - ret = _strnicmp(s1,s2,len); - if (ret == 0) - return ((e1 - s1) - (e2 - s2)); - else - return ret; -} - -static char** -env_to_arg(char *env) -{ - char **ret; - char *tmp; - int i; - int num_strings = 0; - - for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1) { - ++num_strings; - } - ret = erts_alloc(ERTS_ALC_T_TMP, sizeof(char *) * (num_strings + 1)); - i = 0; - for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1){ - ret[i++] = tmp; - } - ret[i] = NULL; - return ret; -} - -static char* -arg_to_env(char **arg) -{ - char *block; - char *ptr; - int i; - int totlen = 1; /* extra '\0' */ - - for(i = 0; arg[i] != NULL; ++i) { - totlen += strlen(arg[i])+1; - } - - /* sort the environment vector */ - qsort(arg, i, sizeof(char *), &compare); - - if (totlen == 1){ - block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, 2); - block[0] = block[1] = '\0'; - } else { - block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, totlen); - ptr = block; - for(i=0; arg[i] != NULL; ++i){ - strcpy(ptr, arg[i]); - ptr += strlen(ptr)+1; - } - *ptr = '\0'; - } - return block; -} +/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2011. 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%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_sys_driver.h"
+#include "erl_alloc.h"
+
+static WCHAR *merge_environment(WCHAR *current, WCHAR *add);
+static WCHAR *arg_to_env(WCHAR **arg);
+static WCHAR **env_to_arg(WCHAR *env);
+static WCHAR **find_arg(WCHAR **arg, WCHAR *str);
+static int compare(const void *a, const void *b);
+
+static erts_smp_rwmtx_t environ_rwmtx;
+
+void
+erts_sys_env_init(void)
+{
+ erts_smp_rwmtx_init(&environ_rwmtx, "environ");
+}
+
+int
+erts_sys_putenv_raw(char *key, char *value)
+{
+ int res;
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = (SetEnvironmentVariable((LPCTSTR) key,
+ (LPCTSTR) value) ? 0 : 1);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+ return res;
+}
+
+int
+erts_sys_putenv(char *key, char *value)
+{
+ int res;
+ WCHAR *wkey = (WCHAR *) key;
+ WCHAR *wvalue = (WCHAR *) value;
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = (SetEnvironmentVariableW(wkey,
+ wvalue) ? 0 : 1);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+ return res;
+}
+
+int
+erts_sys_getenv(char *key, char *value, size_t *size)
+{
+ size_t req_size = 0;
+ int res = 0;
+ DWORD new_size;
+ WCHAR *wkey = (WCHAR *) key;
+ WCHAR *wvalue = (WCHAR *) value;
+ DWORD wsize = *size / (sizeof(WCHAR) / sizeof(char));
+
+ SetLastError(0);
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ new_size = GetEnvironmentVariableW(wkey,
+ wvalue,
+ (DWORD) wsize);
+ res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ if (res < 0)
+ return res;
+ res = new_size > wsize ? 1 : 0;
+ *size = new_size * (sizeof(WCHAR) / sizeof(char));
+ return res;
+}
+int
+erts_sys_getenv__(char *key, char *value, size_t *size)
+{
+ size_t req_size = 0;
+ int res = 0;
+ DWORD new_size;
+
+ SetLastError(0);
+ new_size = GetEnvironmentVariable((LPCTSTR) key,
+ (LPTSTR) value,
+ (DWORD) *size);
+ res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
+ if (res < 0)
+ return res;
+ res = new_size > *size ? 1 : 0;
+ *size = new_size;
+ return res;
+}
+
+int
+erts_sys_getenv_raw(char *key, char *value, size_t *size)
+{
+ int res;
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ res = erts_sys_getenv__(key, value, size);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ return res;
+}
+
+void init_getenv_state(GETENV_STATE *state)
+{
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ state->environment_strings = GetEnvironmentStringsW();
+ state->next_string = state->environment_strings;
+}
+
+char *getenv_string(GETENV_STATE *state)
+{
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
+ if (state->next_string[0] == L'\0') {
+ return NULL;
+ } else {
+ WCHAR *res = state->next_string;
+ state->next_string += wcslen(res) + 1;
+ return (char *) res;
+ }
+}
+
+void fini_getenv_state(GETENV_STATE *state)
+{
+ FreeEnvironmentStringsW(state->environment_strings);
+ state->environment_strings = state->next_string = NULL;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+}
+
+char*
+win_build_environment(char* new_env)
+{
+ if (new_env == NULL) {
+ return NULL;
+ } else {
+ WCHAR *tmp, *merged, *tmp_new;
+
+ tmp_new = (WCHAR *) new_env;
+
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ tmp = GetEnvironmentStringsW();
+ merged = merge_environment(tmp, tmp_new);
+
+ FreeEnvironmentStringsW(tmp);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ return (char *) merged;
+ }
+}
+
+static WCHAR *
+merge_environment(WCHAR *old, WCHAR *add)
+{
+ WCHAR **a_arg = env_to_arg(add);
+ WCHAR **c_arg = env_to_arg(old);
+ WCHAR *ret;
+ int i, j;
+
+ for(i = 0; c_arg[i] != NULL; ++i)
+ ;
+
+ for(j = 0; a_arg[j] != NULL; ++j)
+ ;
+
+ c_arg = erts_realloc(ERTS_ALC_T_TMP,
+ c_arg, (i+j+1) * sizeof(WCHAR *));
+
+ for(j = 0; a_arg[j] != NULL; ++j){
+ WCHAR **tmp;
+ WCHAR *current = a_arg[j];
+ WCHAR *eq_p = wcschr(current,L'=');
+ int unset = (eq_p!=NULL && eq_p[1]==L'\0');
+
+ if ((tmp = find_arg(c_arg, current)) != NULL) {
+ if (!unset) {
+ *tmp = current;
+ } else {
+ *tmp = c_arg[--i];
+ c_arg[i] = NULL;
+ }
+ } else if (!unset) {
+ c_arg[i++] = current;
+ c_arg[i] = NULL;
+ }
+ }
+ ret = arg_to_env(c_arg);
+ erts_free(ERTS_ALC_T_TMP, c_arg);
+ erts_free(ERTS_ALC_T_TMP, a_arg);
+ return ret;
+}
+
+static WCHAR**
+find_arg(WCHAR **arg, WCHAR *str)
+{
+ WCHAR *tmp;
+ int len;
+
+ if ((tmp = wcschr(str, L'=')) != NULL) {
+ tmp++;
+ len = tmp - str;
+ while (*arg != NULL){
+ if (_wcsnicmp(*arg, str, len) == 0){
+ return arg;
+ }
+ ++arg;
+ }
+ }
+ return NULL;
+}
+
+static int
+compare(const void *a, const void *b)
+{
+ WCHAR *s1 = *((WCHAR **) a);
+ WCHAR *s2 = *((WCHAR **) b);
+ WCHAR *e1 = wcschr(s1,L'=');
+ WCHAR *e2 = wcschr(s2,L'=');
+ int ret;
+ int len;
+
+ if(!e1)
+ e1 = s1 + wcslen(s1);
+ if(!e2)
+ e2 = s2 + wcslen(s2);
+
+ if((e1 - s1) > (e2 - s2))
+ len = (e2 - s2);
+ else
+ len = (e1 - s1);
+
+ ret = _wcsnicmp(s1,s2,len);
+ if (ret == 0)
+ return ((e1 - s1) - (e2 - s2));
+ else
+ return ret;
+}
+
+static WCHAR**
+env_to_arg(WCHAR *env)
+{
+ WCHAR **ret;
+ WCHAR *tmp;
+ int i;
+ int num_strings = 0;
+
+ for(tmp = env; *tmp != '\0'; tmp += wcslen(tmp)+1) {
+ ++num_strings;
+ }
+ ret = erts_alloc(ERTS_ALC_T_TMP, sizeof(WCHAR *) * (num_strings + 1));
+ i = 0;
+ for(tmp = env; *tmp != '\0'; tmp += wcslen(tmp)+1){
+ ret[i++] = tmp;
+ }
+ ret[i] = NULL;
+ return ret;
+}
+
+static WCHAR *
+arg_to_env(WCHAR **arg)
+{
+ WCHAR *block;
+ WCHAR *ptr;
+ int i;
+ int totlen = 1; /* extra '\0' */
+
+ for(i = 0; arg[i] != NULL; ++i) {
+ totlen += wcslen(arg[i])+1;
+ }
+
+ /* sort the environment vector */
+ qsort(arg, i, sizeof(WCHAR *), &compare);
+
+ if (totlen == 1){
+ block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, 2 * sizeof(WCHAR));
+ block[0] = block[1] = '\0';
+ } else {
+ block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, totlen * sizeof(WCHAR));
+ ptr = block;
+ for(i=0; arg[i] != NULL; ++i){
+ wcscpy(ptr, arg[i]);
+ ptr += wcslen(ptr)+1;
+ }
+ *ptr = '\0';
+ }
+ return block;
+}
diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c index 347c31053b..a507a19480 100644 --- a/erts/emulator/sys/win32/sys_interrupt.c +++ b/erts/emulator/sys/win32/sys_interrupt.c @@ -75,11 +75,11 @@ BOOL WINAPI ctrl_handler_ignore_break(DWORD dwCtrlType) return TRUE; break; case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: if (nohup) return TRUE; /* else pour through... */ case CTRL_CLOSE_EVENT: - case CTRL_SHUTDOWN_EVENT: erl_exit(0, ""); break; } @@ -127,11 +127,11 @@ BOOL WINAPI ctrl_handler(DWORD dwCtrlType) SetEvent(erts_sys_break_event); break; case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: if (nohup) return TRUE; /* else pour through... */ case CTRL_CLOSE_EVENT: - case CTRL_SHUTDOWN_EVENT: erl_exit(0, ""); break; } diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index a3dcbc4cf3..03311423f0 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -196,13 +196,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(EMAKEFILE) $(TEST_SPEC_FILES) \ - $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(NO_OPT_ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(NATIVE_ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(NO_OPT_ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(NATIVE_ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index a21b055596..99ed8f1748 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -70,7 +70,7 @@ display(doc) -> display(Config) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), {ok, Node} = test_server:start_node(display_huge_term,peer, - [{args, "-pa "++Pa}]), + [{args, "-pa \""++Pa++"\""}]), true = rpc:call(Node,?MODULE,display_huge,[]), test_server:stop_node(Node), ok. diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index d9fc876482..c9f43bb00b 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1135,15 +1135,8 @@ sleeper() -> ?line receive after infinity -> ok end. -gc_test(doc) -> "Test that binaries are garbage collected properly."; -gc_test(suite) -> []; +%% Test that binaries are garbage collected properly. gc_test(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - private -> gc_test_1(); - hybrid -> {skip,"Hybrid heap"} - end. - -gc_test_1() -> %% Note: This test is only relevant for REFC binaries. %% Therefore, we take care that all binaries are REFC binaries. B = list_to_binary(lists:seq(0, ?heap_binary_size)), diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 7fdf36711b..dea02b1f4b 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -460,7 +460,6 @@ mem_leak(0, _) -> ok; mem_leak(N, B) -> ?line big_bin(B, <<23>>), ?line {'EXIT',{badarg,_}} = (catch big_bin(B, bad)), - maybe_gc(), mem_leak(N-1, B). big_bin(B1, B2) -> @@ -473,13 +472,6 @@ big_bin(B1, B2) -> make_bin(0, Acc) -> Acc; make_bin(N, Acc) -> make_bin(N-1, <<Acc/binary,Acc/binary>>). -maybe_gc() -> - case erlang:system_info(heap_type) of - shared -> erlang:garbage_collect(); - hybrid -> erlang:garbage_collect(); - private -> ok - end. - -define(COF(Int0), ?line (fun(Int) -> true = <<Int:32/float>> =:= <<(float(Int)):32/float>>, diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 25ce94096f..74ce5e397a 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -135,14 +135,7 @@ new_binary_types(Config) when is_list(Config) -> bit_sized_binary(Bin))), ok. -t_check_process_code(doc) -> "Test check_process_code/2."; t_check_process_code(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - private -> t_check_process_code_1(Config); - hybrid -> {skip,"Hybrid heap"} - end. - -t_check_process_code_1(Config) -> ?line Priv = ?config(priv_dir, Config), ?line Data = ?config(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), @@ -247,12 +240,10 @@ gc1() -> ok. t_check_process_code_ets(doc) -> "Test check_process_code/2 in combination with a fun obtained from an ets table."; t_check_process_code_ets(Config) when is_list(Config) -> - case {test_server:is_native(?MODULE),erlang:system_info(heap_type)} of - {true,_} -> - {skipped,"Native code"}; - {_,hybrid} -> - {skipped,"Hybrid heap"}; - {false,private} -> + case test_server:is_native(?MODULE) of + true -> + {skip,"Native code"}; + false -> do_check_process_code_ets(Config) end. @@ -397,9 +388,7 @@ module_md5_ok(Code) -> make_stub(Config) when is_list(Config) -> - %% No old code to purge if hybrid heap because of skipped test cases, - %% so we'll need a catch here. - ?line (catch erlang:purge_module(my_code_test)), + catch erlang:purge_module(my_code_test), ?line Data = ?config(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), @@ -433,9 +422,7 @@ make_stub(Config) when is_list(Config) -> ok. make_stub_many_funs(Config) when is_list(Config) -> - %% No old code to purge if hybrid heap because of skipped test cases, - %% so we'll need a catch here. - ?line (catch erlang:purge_module(many_funs)), + catch erlang:purge_module(many_funs), ?line Data = ?config(data_dir, Config), ?line File = filename:join(Data, "many_funs"), diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 08308629fe..2ddb4d76d9 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -1890,7 +1890,7 @@ start_node(Name, Args, Rel) when is_atom(Name), is_list(Rel) -> end, test_server:start_node(Name, slave, [{args, - Args++" -setcookie "++Cookie++" -pa "++Pa} + Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""} | RelArg]); start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) -> Name = list_to_atom((atom_to_list(?MODULE) diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index 8e6923ce9f..abe01bf5ec 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -25,7 +25,7 @@ init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, fpe/1,fp_drv/1,fp_drv_thread/1,denormalized/1,match/1, - bad_float_unpack/1,cmp_zero/1, cmp_integer/1, cmp_bignum/1]). + bad_float_unpack/1, write/1, cmp_zero/1, cmp_integer/1, cmp_bignum/1]). -export([otp_7178/1]). -export([hidden_inf/1]). @@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [fpe, fp_drv, fp_drv_thread, otp_7178, denormalized, - match, bad_float_unpack, {group, comparison} + match, bad_float_unpack, write, {group, comparison} ,hidden_inf ]. @@ -190,6 +190,11 @@ bad_float_unpack(Config) when is_list(Config) -> bad_float_unpack_match(<<F:64/float>>) -> F; bad_float_unpack_match(<<I:64/integer-signed>>) -> I. +%% Exposes endianness issues. + +write(Config) when is_list(Config) -> + "1.0" = io_lib:write(1.0). + cmp_zero(_Config) -> cmp(0.5e-323,0). diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 559e540016..839ad6a4f4 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -539,12 +539,6 @@ bad_md5(Bad) -> {'EXIT',{badarg,_}} = (catch erlang:md5(Bad)). refc(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - private -> refc_1(); - hybrid -> {skip,"Hybrid heap"} - end. - -refc_1() -> ?line F1 = fun_factory(2), ?line {refc,2} = erlang:fun_info(F1, refc), ?line F2 = fun_factory(42), @@ -570,12 +564,6 @@ fun_factory(Const) -> fun(X) -> X + Const end. refc_ets(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - private -> refc_ets_1(); - hybrid -> {skip,"Hybrid heap"} - end. - -refc_ets_1() -> ?line F = fun(X) -> X + 33 end, ?line {refc,2} = erlang:fun_info(F, refc), @@ -622,12 +610,6 @@ refc_ets_bag(F1, Options) -> ok. refc_dist(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - private -> refc_dist_1(); - hybrid -> {skip,"Hybrid heap"} - end. - -refc_dist_1() -> ?line {ok,Node} = start_node(fun_SUITE_refc_dist), ?line process_flag(trap_exit, true), ?line Pid = spawn_link(Node, diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl index 82a0aad189..68bc3434d4 100644 --- a/erts/emulator/test/hibernate_SUITE.erl +++ b/erts/emulator/test/hibernate_SUITE.erl @@ -67,10 +67,7 @@ end_per_testcase(_Func, Config) -> basic(Config) when is_list(Config) -> Ref = make_ref(), Info = {self(),Ref}, - ExpectedHeapSz = case erlang:system_info(heap_type) of - private -> erts_debug:size([Info]); - hybrid -> erts_debug:size([a|b]) - end, + ExpectedHeapSz = erts_debug:size([Info]), ?line Child = spawn_link(fun() -> basic_hibernator(Info) end), ?line hibernate_wake_up(100, ExpectedHeapSz, Child), ?line Child ! please_quit_now, @@ -166,10 +163,7 @@ whats_up_calc(A1, A2, A3, A4, A5, A6, A7, A8, A9, Acc) -> dynamic_call(Config) when is_list(Config) -> Ref = make_ref(), Info = {self(),Ref}, - ExpectedHeapSz = case erlang:system_info(heap_type) of - private -> erts_debug:size([Info]); - hybrid -> erts_debug:size([a|b]) - end, + ExpectedHeapSz = erts_debug:size([Info]), ?line Child = spawn_link(fun() -> ?MODULE:dynamic_call_hibernator(Info, hibernate) end), ?line hibernate_wake_up(100, ExpectedHeapSz, Child), ?line Child ! please_quit_now, diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index aa83459ef8..0bf2c03233 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -459,10 +459,6 @@ make_node_garbage(_, _, _, Ps) -> end, lists:foreach(fun (P) -> wait_until(fun () -> ProcIsCleanedUp(P) end) end, Ps), - ?line case erlang:system_info(heap_type) of - shared -> ?line garbage_collect(); - _ -> ?line ok - end, ?line ok. @@ -605,17 +601,7 @@ node_controller_refc(Config) when is_list(Config) -> % Get rid of all references to Node ?line exec(P, fun () -> exit(normal) end), ?line wait_until(fun () -> not is_process_alive(P) end), - ?line case erlang:system_info(heap_type) of - shared -> - ?line garbage_collect(); - hybrid -> - ?line lists:foreach(fun (Proc) -> garbage_collect(Proc) end, - processes()), - ?line erlang:garbage_collect_message_area(); - _ -> - ?line lists:foreach(fun (Proc) -> garbage_collect(Proc) end, - processes()) - end, + lists:foreach(fun (Proc) -> garbage_collect(Proc) end, processes()), ?line false = get_node_references({Node,Creation}), ?line false = get_dist_references(Node), ?line false = lists:member(Node, nodes(known)), diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 0a1ef5a78f..0b99b3438a 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1244,8 +1244,8 @@ otp_3906_forker(N, Parent, Ref, Sup, Prog) -> otp_4389(suite) -> []; otp_4389(doc) -> []; otp_4389(Config) when is_list(Config) -> - case {os:type(),erlang:system_info(heap_type)} of - {{unix, _},private} -> + case os:type() of + {unix, _} -> ?line Dog = test_server:timetrap(test_server:seconds(240)), ?line TCR = self(), case get_true_cmd() of @@ -1293,7 +1293,7 @@ otp_4389(Config) when is_list(Config) -> ?line {skipped, "\"true\" command not found"} end; _ -> - {skip,"Only run on Unix and private heaps"} + {skip,"Only run on Unix"} end. get_true_cmd() -> diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index d9c82aba0e..8feea87d7e 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -24,6 +24,7 @@ init_per_group/2,end_per_group/2, command/1, command_e_1/1, command_e_2/1, command_e_3/1, command_e_4/1, port_info1/1, port_info2/1, + port_info_os_pid/1, connect/1, control/1, echo_to_busy/1]). -export([do_command_e_1/1, do_command_e_2/1, do_command_e_4/1]). @@ -41,7 +42,7 @@ all() -> groups() -> [{command_e, [], [command_e_1, command_e_2, command_e_3, command_e_4]}, - {port_info, [], [port_info1, port_info2]}]. + {port_info, [], [port_info1, port_info2, port_info_os_pid]}]. init_per_suite(Config) -> Config. @@ -65,15 +66,15 @@ end_per_testcase(_Func, Config) when is_list(Config) -> test_server:timetrap_cancel(Dog). command(Config) when is_list(Config) -> - ?line load_control_drv(Config), - - ?line P = open_port({spawn, control_drv}, []), - ?line do_command(P, "hello"), - ?line do_command(P, <<"hello">>), - ?line do_command(P, sub_bin(<<"1234kalle">>)), - ?line do_command(P, unaligned_sub_bin(<<"blurf">>)), - ?line do_command(P, ["bl"|unaligned_sub_bin(<<"urf">>)]), - ?line true = erlang:port_close(P), + load_control_drv(Config), + + P = open_port({spawn, control_drv}, []), + do_command(P, "hello"), + do_command(P, <<"hello">>), + do_command(P, sub_bin(<<"1234kalle">>)), + do_command(P, unaligned_sub_bin(<<"blurf">>)), + do_command(P, ["bl"|unaligned_sub_bin(<<"urf">>)]), + true = erlang:port_close(P), ok. do_command(P, Data) -> @@ -94,139 +95,163 @@ do_command(P, Data) -> %% port_command/2: badarg 1st arg command_e_1(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line _ = spawn_link(?MODULE, do_command_e_1, [Program]), - ?line receive - {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> - ok; - Other -> - ?line test_server:fail(Other) - after 10000 -> - ?line test_server:fail(timeout) - end, + _ = spawn_link(?MODULE, do_command_e_1, [Program]), + receive + {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. do_command_e_1(Program) -> - ?line _ = open_port({spawn, Program}, []), - ?line erlang:port_command(apple, "plock"), + _ = open_port({spawn, Program}, []), + erlang:port_command(apple, "plock"), exit(survived). %% port_command/2: badarg 2nd arg command_e_2(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line _ = spawn_link(?MODULE, do_command_e_2, [Program]), - ?line receive - {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> - ok; - Other -> - ?line test_server:fail(Other) - after 10000 -> - ?line test_server:fail(timeout) - end, + _ = spawn_link(?MODULE, do_command_e_2, [Program]), + receive + {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. do_command_e_2(Program) -> - ?line P = open_port({spawn, Program}, []), - ?line erlang:port_command(P, 1), + P = open_port({spawn, Program}, []), + erlang:port_command(P, 1), exit(survived). %% port_command/2: Posix signals trapped command_e_3(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line P = open_port({spawn, Program}, [{packet, 1}]), - ?line Data = lists:duplicate(257, $a), - ?line erlang:port_command(P, Data), - ?line receive - {'EXIT', Port, einval} when is_port(Port) -> - ok; - Other -> - test_server:fail(Other) - after 10000 -> - test_server:fail(timeout) - end, + P = open_port({spawn, Program}, [{packet, 1}]), + Data = lists:duplicate(257, $a), + erlang:port_command(P, Data), + receive + {'EXIT', Port, einval} when is_port(Port) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. %% port_command/2: Posix exit signals not trapped command_e_4(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line _ = spawn_link(?MODULE, do_command_e_4, [Program]), - ?line receive - {'EXIT', Pid, {einval, _}} when is_pid(Pid) -> - ok; - Other -> - ?line test_server:fail(Other) - after 10000 -> - ?line test_server:fail(timeout) - end, + _ = spawn_link(?MODULE, do_command_e_4, [Program]), + receive + {'EXIT', Pid, {einval, _}} when is_pid(Pid) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. do_command_e_4(Program) -> - ?line P = open_port({spawn, Program}, [{packet, 1}]), - ?line Data = lists:duplicate(257, $a), - ?line erlang:port_command(P, Data), + P = open_port({spawn, Program}, [{packet, 1}]), + Data = lists:duplicate(257, $a), + erlang:port_command(P, Data), exit(survived). %% Tests the port_info/1 BIF port_info1(Config) when is_list(Config) -> - ?line load_control_drv(Config), + load_control_drv(Config), Me=self(), - ?line P = open_port({spawn, control_drv}, []), - ?line A1 = erlang:port_info(P), - ?line false = lists:keysearch(registered_name, 1, A1), - ?line register(myport, P), - ?line A = erlang:port_info(P), - ?line {value,{registered_name,myport}}= - lists:keysearch(registered_name, 1, A), - ?line {value,{name,"control_drv"}}=lists:keysearch(name, 1, A), - ?line {value,{links,[Me]}}=lists:keysearch(links, 1, A), - ?line {value,{id,_IdNum}}=lists:keysearch(id, 1, A), - ?line {value,{connected,_}}=lists:keysearch(connected, 1, A), - ?line {value,{input,0}}=lists:keysearch(input, 1, A), - ?line {value,{output,0}}=lists:keysearch(output, 1, A), - ?line true=erlang:port_close(P), + P = open_port({spawn, control_drv}, []), + A1 = erlang:port_info(P), + false = lists:keysearch(registered_name, 1, A1), + register(myport, P), + A = erlang:port_info(P), + {value,{registered_name,myport}}= lists:keysearch(registered_name, 1, A), + {value,{name,"control_drv"}}=lists:keysearch(name, 1, A), + {value,{links,[Me]}}=lists:keysearch(links, 1, A), + {value,{id,_IdNum}}=lists:keysearch(id, 1, A), + {value,{connected,_}}=lists:keysearch(connected, 1, A), + {value,{input,0}}=lists:keysearch(input, 1, A), + {value,{output,0}}=lists:keysearch(output, 1, A), + {value,{os_pid,undefined}}=lists:keysearch(os_pid, 1, A), % linked-in driver doesn't have a OS pid + true=erlang:port_close(P), ok. %% Tests erlang:port_info/2" port_info2(Config) when is_list(Config) -> - ?line load_control_drv(Config), + load_control_drv(Config), - ?line P = open_port({spawn,control_drv}, [binary]), - ?line [] = erlang:port_info(P, registered_name), - ?line register(myport, P), - ?line {registered_name, myport} = erlang:port_info(P, registered_name), + P = open_port({spawn,control_drv}, [binary]), + [] = erlang:port_info(P, registered_name), + register(myport, P), + {registered_name, myport} = erlang:port_info(P, registered_name), - ?line {name, "control_drv"}=erlang:port_info(P, name), - ?line {id, _IdNum} = erlang:port_info(P, id), + {name, "control_drv"}=erlang:port_info(P, name), + {id, _IdNum} = erlang:port_info(P, id), Me=self(), - ?line {links, [Me]} = erlang:port_info(P, links), - ?line {connected, Me} = erlang:port_info(P, connected), - ?line {input, 0}=erlang:port_info(P, input), - ?line {output,0}=erlang:port_info(P, output), - - ?line erlang:port_control(P, $i, "abc"), - ?line receive - {P,{data,<<"abc">>}} -> ok - end, - ?line {input,3} = erlang:port_info(P, input), - ?line {output,0} = erlang:port_info(P, output), - - ?line Bin = list_to_binary(lists:duplicate(2047, 42)), - ?line output_test(P, Bin, 3, 0), + {links, [Me]} = erlang:port_info(P, links), + {connected, Me} = erlang:port_info(P, connected), + {input, 0}=erlang:port_info(P, input), + {output,0}=erlang:port_info(P, output), + {os_pid, undefined}=erlang:port_info(P, os_pid), % linked-in driver doesn't have a OS pid + + erlang:port_control(P, $i, "abc"), + receive + {P,{data,<<"abc">>}} -> ok + end, + {input,3} = erlang:port_info(P, input), + {output,0} = erlang:port_info(P, output), + + Bin = list_to_binary(lists:duplicate(2047, 42)), + output_test(P, Bin, 3, 0), - ?line true = erlang:port_close(P), + true = erlang:port_close(P), + ok. + +%% Tests the port_info/1,2 os_pid option BIF +port_info_os_pid(Config) when is_list(Config) -> + case os:type() of + {unix,_} -> + do_port_info_os_pid(); + _ -> + {skip,"Only on Unix."} + end. + +do_port_info_os_pid() -> + P = open_port({spawn, "echo $$"}, [eof]), + A = erlang:port_info(P), + {os_pid, InfoOSPid} = erlang:port_info(P, os_pid), + EchoPidStr = receive + {P, {data, EchoPidStr0}} -> EchoPidStr0 + after 10000 -> test_server:fail(timeout) + end, + {ok, [EchoPid], []} = io_lib:fread("~u\n", EchoPidStr), + {value,{os_pid, InfoOSPid}}=lists:keysearch(os_pid, 1, A), + EchoPid = InfoOSPid, + true = erlang:port_close(P), ok. output_test(_, _, Input, Output) when Output > 16#1fffffff -> @@ -237,7 +262,7 @@ output_test(P, Bin, Input0, Output0) -> {P,{data,Bin}} -> ok; Other -> io:format("~p", [Other]), - ?line ?t:fail() + ?t:fail() end, Input = Input0 + size(Bin), Output = Output0 + size(Bin), @@ -254,109 +279,106 @@ output_test(P, Bin, Input0, Output0) -> %% Tests the port_connect/2 BIF. connect(Config) when is_list(Config) -> - ?line load_control_drv(Config), + load_control_drv(Config), - ?line P = open_port({spawn, control_drv}, []), + P = open_port({spawn, control_drv}, []), register(myport, P), - ?line true = erlang:port_connect(myport, self()), + true = erlang:port_connect(myport, self()), %% Connect the port to another process. Data = "hello, world", Parent = self(), - ?line Rec = - fun(Me) -> receive - {P,{data,Data}} -> - Parent ! connect_ok, - Me(Me) - end - end, - ?line RecPid = spawn_link(fun() -> Rec(Rec) end), - ?line true = erlang:port_connect(P, RecPid), - ?line unlink(P), + Rec = fun(Me) -> + receive + {P,{data,Data}} -> + Parent ! connect_ok, + Me(Me) + end + end, + RecPid = spawn_link(fun() -> Rec(Rec) end), + true = erlang:port_connect(P, RecPid), + unlink(P), %% Send a command to the port and make sure that the %% other process receives the echo. - ?line erlang:port_command(P, Data), - ?line receive - connect_ok -> ok - end, + erlang:port_command(P, Data), + receive + connect_ok -> ok + end, %% Tests some errors. - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), self())), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), P)), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, P)), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, xxxx)), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, [])), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), self())), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), P)), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, P)), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, xxxx)), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, [])), - ?line process_flag(trap_exit, true), - ?line exit(P, you_should_die), - ?line receive - {'EXIT',RecPid,you_should_die} -> ok; - Other -> ?line ?t:fail({bad_message,Other}) - end, + process_flag(trap_exit, true), + exit(P, you_should_die), + receive + {'EXIT',RecPid,you_should_die} -> ok; + Other -> ?line ?t:fail({bad_message,Other}) + end, %% Done. ok. %% Tests port_control/3 control(Config) when is_list(Config) -> - ?line load_control_drv(Config), - ?line P = open_port({spawn, control_drv}, []), + load_control_drv(Config), + P = open_port({spawn, control_drv}, []), %% Test invalid (out-of-range) arguments. - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(self(), 1, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(self(), 1, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -1, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -34887348739733833, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 16#100000000, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, a, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 'e', dum)), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, dum)), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, fun(X) -> X end)), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, - [fun(X) -> X end])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, - [1|fun(X) -> X end])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -1, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -34887348739733833, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 16#100000000, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, a, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 'e', dum)), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, dum)), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, fun(X) -> X end)), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, [fun(X) -> X end])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, [1|fun(X) -> X end])), %% Test errors detected by the driver. - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 177, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 155, - random_packet(1024))), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 177, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 155, random_packet(1024))), %% Test big op codes. register(myport, P), - ?line test_op(myport, 256), - ?line test_op(P, 256), - ?line test_op(P, 16#0033A837), - ?line test_op(P, 16#0ab37938), - ?line test_op(P, 16#eab37938), - ?line test_op(P, 16#ffffFFFF), + test_op(myport, 256), + test_op(P, 256), + test_op(P, 16#0033A837), + test_op(P, 16#0ab37938), + test_op(P, 16#eab37938), + test_op(P, 16#ffffFFFF), %% Test the echo function of the driver. - ?line echo(P, 0), - ?line echo(P, 1), - ?line echo(P, 10), - ?line echo(P, 13), - ?line echo(P, 63), - ?line echo(P, 64), - ?line echo(P, 65), - ?line echo(P, 127), - ?line echo(P, 1023), - ?line echo(P, 1024), - ?line echo(P, 11243), - ?line echo(P, 70000), + echo(P, 0), + echo(P, 1), + echo(P, 10), + echo(P, 13), + echo(P, 63), + echo(P, 64), + echo(P, 65), + echo(P, 127), + echo(P, 1023), + echo(P, 1024), + echo(P, 11243), + echo(P, 70000), %% Done. - ?line true=erlang:port_close(myport), + true = erlang:port_close(myport), ok. test_op(P, Op) -> @@ -364,23 +386,23 @@ test_op(P, Op) -> <<Op:32>> = list_to_binary(R). echo_to_busy(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line load_control_drv(Config), - ?line P = open_port({spawn, control_drv}, []), - ?line erlang:port_control(P, $b, [1]), % Set to busy. + Dog = test_server:timetrap(test_server:seconds(10)), + load_control_drv(Config), + P = open_port({spawn, control_drv}, []), + erlang:port_control(P, $b, [1]), % Set to busy. Self = self(), - ?line Echoer = spawn(fun() -> echoer(P, Self) end), - ?line receive after 500 -> ok end, - ?line erlang:port_control(P, $b, [0]), % Set to not busy. - ?line receive - {Echoer, done} -> - ok; - {Echoer, Other} -> - test_server:fail(Other); - Other -> - test_server:fail({unexpected_message, Other}) - end, - ?line test_server:timetrap_cancel(Dog), + Echoer = spawn(fun() -> echoer(P, Self) end), + receive after 500 -> ok end, + erlang:port_control(P, $b, [0]), % Set to not busy. + receive + {Echoer, done} -> + ok; + {Echoer, Other} -> + test_server:fail(Other); + Other -> + test_server:fail({unexpected_message, Other}) + end, + test_server:timetrap_cancel(Dog), ok. echoer(P, ReplyTo) -> @@ -405,9 +427,9 @@ echo(P, Size) -> Packet = erlang:port_control(P, $e, [unaligned_sub_bin(Bin)]). load_control_drv(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line erl_ddll:start(), - ?line ok = load_driver(DataDir, "control_drv"). + DataDir = ?config(data_dir, Config), + erl_ddll:start(), + ok = load_driver(DataDir, "control_drv"). load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of @@ -459,4 +481,3 @@ sub_bin(Bin) when is_binary(Bin) -> B. id(I) -> I. - diff --git a/erts/emulator/utils/make_preload b/erts/emulator/utils/make_preload index 13019d4062..4e329b24f8 100755 --- a/erts/emulator/utils/make_preload +++ b/erts/emulator/utils/make_preload @@ -87,6 +87,7 @@ foreach $file (@ARGV) { my $comment = ''; open(FILE, $file) or error("failed to read $file: $!"); + binmode(FILE); $_ = <FILE>; $_ = beam_strip($_); close(FILE); diff --git a/erts/emulator/valgrind/suppress.patched.3.6.0 b/erts/emulator/valgrind/suppress.patched.3.6.0 index 8cf4cba2c8..62ba032520 100644 --- a/erts/emulator/valgrind/suppress.patched.3.6.0 +++ b/erts/emulator/valgrind/suppress.patched.3.6.0 @@ -57,18 +57,16 @@ fun:putenv fun:erts_sys_putenv fun:os_putenv_2 - fun:process_main + ... } { -Leak in libc putenv -Memcheck:Leak -fun:malloc -fun:erts_sys_alloc -... -fun:erts_alloc -fun:erts_sys_putenv -fun:os_putenv_2 -fun:process_main + Leak in libc putenv + Memcheck:Leak + ... + fun:erts_alloc + fun:erts_sys_putenv + fun:os_putenv_2 + ... } { erronous warning @@ -348,3 +346,14 @@ fun:erl_start fun:main } +{ +Harmless leak of ErtsThrPrgrData from async threads in exiting emulator +Memcheck:Leak +... +fun:erts_alloc +fun:erts_thr_progress_register_unmanaged_thread +fun:async_thread_init +fun:async_main +... +} + diff --git a/erts/emulator/valgrind/suppress.standard b/erts/emulator/valgrind/suppress.standard index 26e34e3757..5a129bfd10 100644 --- a/erts/emulator/valgrind/suppress.standard +++ b/erts/emulator/valgrind/suppress.standard @@ -54,18 +54,16 @@ fun:putenv fun:erts_sys_putenv fun:os_putenv_2 - fun:process_main + ... } { -Leak in libc putenv -Memcheck:Leak -fun:malloc -fun:erts_sys_alloc -... -fun:erts_alloc -fun:erts_sys_putenv -fun:os_putenv_2 -fun:process_main + Leak in libc putenv + Memcheck:Leak + ... + fun:erts_alloc + fun:erts_sys_putenv + fun:os_putenv_2 + ... } { erronous warning @@ -306,3 +304,14 @@ fun:erl_start fun:main } +{ +Harmless leak of ErtsThrPrgrData from async threads in exiting emulator +Memcheck:Leak +... +fun:erts_alloc +fun:erts_thr_progress_register_unmanaged_thread +fun:async_thread_init +fun:async_main +... +} + diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in index 5767edc346..577fc77c13 100644 --- a/erts/epmd/src/Makefile.in +++ b/erts/epmd/src/Makefile.in @@ -139,8 +139,8 @@ $(ERTS_LIB): include $(ERL_TOP)/make/otp_release_targets.mk release_spec: all - $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/bin - $(INSTALL_PROGRAM) $(INSTALL_PROGS) $(RELEASE_PATH)/erts-$(VSN)/bin + $(INSTALL_DIR) "$(RELEASE_PATH)/erts-$(VSN)/bin" + $(INSTALL_PROGRAM) $(INSTALL_PROGS) "$(RELEASE_PATH)/erts-$(VSN)/bin" release_docs_spec: diff --git a/erts/epmd/test/Makefile b/erts/epmd/test/Makefile index 54688fd90b..868b1c023b 100644 --- a/erts/epmd/test/Makefile +++ b/erts/epmd/test/Makefile @@ -71,10 +71,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: opt - $(INSTALL_DIR) $(RELEPMDDIR) + $(INSTALL_DIR) "$(RELEPMDDIR)" $(INSTALL_DATA) epmd.spec epmd.spec.vxworks $(ERL_FILES) \ - $(EMAKEFILE) $(RELEPMDDIR) - chmod -R u+w $(RELEPMDDIR) + $(EMAKEFILE) "$(RELEPMDDIR)" + chmod -R u+w "$(RELEPMDDIR)" release_docs_spec: diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 6889ec0b34..3f31cd979c 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -931,7 +931,7 @@ epmdrun(Epmd,Args0) -> O -> " "++O end, - osrun(Epmd ++ Args ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT)). + osrun("\"" ++ Epmd ++ "\"" ++ Args ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT)). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 28c5e5ccad..83fe97df8e 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -64,6 +64,9 @@ LD = @LD@ LIBS = @LIBS@ LDFLAGS = @LDFLAGS@ +# For clock_gettime in heart +RTLIBS = @LIBRT@ + ifeq ($(TARGET),win32) ifeq ($(TYPE),debug) CFLAGS = $(subst -O2,-g,@CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSDIR) \ @@ -416,7 +419,7 @@ else $(BINDIR)/heart@EXEEXT@: $(OBJDIR)/heart.o $(ENTRY_OBJ) $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/heart.o \ - $(ENTRY_OBJ) $(WINDSOCK) + $(RTLIBS) $(ENTRY_OBJ) $(WINDSOCK) $(OBJDIR)/heart.o: heart.c $(RC_GENERATED) $(CC) $(CFLAGS) -o $@ -c heart.c @@ -545,48 +548,48 @@ include $(ERL_TOP)/make/otp_release_targets.mk .PHONY: release_spec release_spec: etc ifneq ($(INSTALL_OBJS),) - $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/obj - $(INSTALL_DATA) $(INSTALL_OBJS) $(RELEASE_PATH)/erts-$(VSN)/obj + $(INSTALL_DIR) "$(RELEASE_PATH)/erts-$(VSN)/obj" + $(INSTALL_DATA) $(INSTALL_OBJS) "$(RELEASE_PATH)/erts-$(VSN)/obj" endif - $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/bin + $(INSTALL_DIR) "$(RELEASE_PATH)/erts-$(VSN)/bin" ifneq ($(TARGET), win32) ifneq ($(findstring vxworks,$(TARGET)), vxworks) ifneq ($(findstring ose,$(TARGET)), ose) - $(INSTALL_SCRIPT) erl.src $(RELEASE_PATH)/erts-$(VSN)/bin + $(INSTALL_SCRIPT) erl.src "$(RELEASE_PATH)/erts-$(VSN)/bin" endif endif endif ifneq ($(INSTALL_PROGS),) - $(INSTALL_PROGRAM) $(INSTALL_PROGS) $(RELEASE_PATH)/erts-$(VSN)/bin + $(INSTALL_PROGRAM) $(INSTALL_PROGS) "$(RELEASE_PATH)/erts-$(VSN)/bin" endif ifneq ($(INSTALL_TOP),) - $(INSTALL_SCRIPT) $(INSTALL_TOP) $(RELEASE_PATH) + $(INSTALL_SCRIPT) $(INSTALL_TOP) "$(RELEASE_PATH)" endif ifneq ($(INSTALL_TOP_BIN),) - $(INSTALL_PROGRAM) $(INSTALL_TOP_BIN) $(RELEASE_PATH) + $(INSTALL_PROGRAM) $(INSTALL_TOP_BIN) "$(RELEASE_PATH)" endif ifneq ($(INSTALL_MISC),) - $(INSTALL_DIR) $(RELEASE_PATH)/misc - $(INSTALL_SCRIPT) $(INSTALL_MISC) $(RELEASE_PATH)/misc + $(INSTALL_DIR) "$(RELEASE_PATH)/misc" + $(INSTALL_SCRIPT) $(INSTALL_MISC) "$(RELEASE_PATH)/misc" endif ifneq ($(INSTALL_ERL_OSE),) - $(INSTALL_DIR) $(RELEASE_PATH)/build_erl_ose + $(INSTALL_DIR) "$(RELEASE_PATH)/build_erl_ose" cd $(OSEETC) && $(TAR) erl_ose_$(SYSTEM_VSN).tar $(INSTALL_ERL_OSE) - cd $(OSEETC) && $(INSTALL_SCRIPT) erl_ose_$(SYSTEM_VSN).tar $(RELEASE_PATH)/build_erl_ose + cd $(OSEETC) && $(INSTALL_SCRIPT) erl_ose_$(SYSTEM_VSN).tar "$(RELEASE_PATH)/build_erl_ose" endif ifneq ($(INSTALL_SRC),) - $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/src - $(INSTALL_DATA) $(INSTALL_SRC) $(RELEASE_PATH)/erts-$(VSN)/src + $(INSTALL_DIR) "$(RELEASE_PATH)/erts-$(VSN)/src" + $(INSTALL_DATA) $(INSTALL_SRC) "$(RELEASE_PATH)/erts-$(VSN)/src" endif ifneq ($(INSTALL_EMBEDDED_DATA),) - $(INSTALL_DATA) $(INSTALL_EMBEDDED_DATA) $(RELEASE_PATH)/erts-$(VSN)/bin + $(INSTALL_DATA) $(INSTALL_EMBEDDED_DATA) "$(RELEASE_PATH)/erts-$(VSN)/bin" endif ifneq ($(INSTALL_LIBS),) - $(INSTALL_DATA) $(INSTALL_LIBS) $(RELEASE_PATH)/erts-$(VSN)/bin + $(INSTALL_DATA) $(INSTALL_LIBS) "$(RELEASE_PATH)/erts-$(VSN)/bin" endif ifneq ($(INSTALL_INCLUDES),) - $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/include - $(INSTALL_DATA) $(INSTALL_INCLUDES) $(RELEASE_PATH)/erts-$(VSN)/include + $(INSTALL_DIR) "$(RELEASE_PATH)/erts-$(VSN)/include" + $(INSTALL_DATA) $(INSTALL_INCLUDES) "$(RELEASE_PATH)/erts-$(VSN)/include" endif .PHONY: release_docs_spec diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c index 04e9199ef3..70b11b1b36 100644 --- a/erts/etc/common/dialyzer.c +++ b/erts/etc/common/dialyzer.c @@ -189,6 +189,18 @@ main(int argc, char** argv) argc--, argv++; } + if (argc > 2 && strcmp(argv[1], "+P") == 0) { + PUSH2("+P", argv[2]); + argc--, argv++; + argc--, argv++; + } else PUSH2("+P", "1000000"); + + if (argc > 2 && strcmp(argv[1], "+sbt") == 0) { + PUSH2("+sbt", argv[2]); + argc--, argv++; + argc--, argv++; + } + PUSH("+B"); PUSH2("-boot", "start_clean"); PUSH3("-run", "dialyzer", "plain_cl"); diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c index 23f009ff4d..9f05213c95 100644 --- a/erts/etc/common/erlc.c +++ b/erts/etc/common/erlc.c @@ -252,13 +252,6 @@ main(int argc, char** argv) } } break; - case 'h': - if (strcmp(argv[1], "-hybrid") == 0) { - UNSHIFT(argv[1]); - } else { - usage(); - } - break; case 'I': PUSH2("@i", process_opt(&argc, &argv, 0)); break; @@ -342,7 +335,7 @@ main(int argc, char** argv) /* Push the following options: * o makedep_phony */ - buf = strsave("makedep_add_missing"); + buf = strsave("makedep_phony"); PUSH2("@option", buf); break; default: @@ -649,7 +642,6 @@ usage(void) {"-d", "turn on debugging of erlc itself"}, {"-Dname", "define name"}, {"-Dname=value", "define name to have value"}, - {"-hybrid", "compile using hybrid-heap emulator"}, {"-help", "shows this help text"}, {"-I path", "where to search for include files"}, {"-M", "generate a rule for make(1) describing the dependencies"}, diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 19b3bb82ef..cba7429fab 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -121,9 +121,11 @@ static char *plusM_other_switches[] = { /* +s arguments with values */ static char *pluss_val_switches[] = { "bt", + "bwt", "cl", "ct", "wt", + "ws", "ss", NULL }; @@ -157,20 +159,13 @@ static char *plusz_val_switches[] = { #endif #define SMP_SUFFIX ".smp" -#define HYBRID_SUFFIX ".hybrid" - -#ifdef __WIN32__ #define DEBUG_SUFFIX ".debug" -#define EMU_TYPE_SUFFIX_LENGTH (strlen(HYBRID_SUFFIX)+(strlen(DEBUG_SUFFIX))) -#else -/* The length of the longest memory architecture suffix. */ -#define EMU_TYPE_SUFFIX_LENGTH strlen(HYBRID_SUFFIX) -#endif +#define EMU_TYPE_SUFFIX_LENGTH strlen(DEBUG_SUFFIX) + /* * Define flags for different memory architectures. */ #define EMU_TYPE_SMP 0x0001 -#define EMU_TYPE_HYBRID 0x0002 #ifdef __WIN32__ #define EMU_TYPE_DEBUG 0x0004 @@ -184,7 +179,7 @@ void error(char* format, ...); * Local functions. */ -#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU) +#if !defined(ERTS_HAVE_SMP_EMU) static void usage_notsup(const char *switchname); #endif static void usage_msg(const char *msg); @@ -250,7 +245,9 @@ static char* config_script = NULL; /* used by option -start_erl and -config */ static HANDLE this_module_handle; static int run_werl; - +static WCHAR *utf8_to_utf16(unsigned char *bytes); +static char *utf16_to_utf8(WCHAR *wstr); +static WCHAR *latin1_to_utf16(char *str); #endif /* @@ -268,8 +265,12 @@ static void set_env(char *key, char *value) { #ifdef __WIN32__ - if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value)) + WCHAR *wkey = latin1_to_utf16(key); + WCHAR *wvalue = utf8_to_utf16(value); + if (!SetEnvironmentVariableW(wkey, wvalue)) error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value); + efree(wkey); + efree(wvalue); #else size_t size = strlen(key) + 1 + strlen(value) + 1; char *str = emalloc(size); @@ -282,25 +283,33 @@ set_env(char *key, char *value) #endif } + static char * get_env(char *key) { #ifdef __WIN32__ DWORD size = 32; - char *value = NULL; + WCHAR *value = NULL; + WCHAR *wkey = latin1_to_utf16(key); + char *res; while (1) { DWORD nsz; if (value) efree(value); - value = emalloc(size); + value = emalloc(size*sizeof(WCHAR)); SetLastError(0); - nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + nsz = GetEnvironmentVariableW(wkey, value, size); if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { efree(value); + efree(wkey); return NULL; } - if (nsz <= size) - return value; + if (nsz <= size) { + efree(wkey); + res = utf16_to_utf8(value); + efree(value); + return res; + } size = nsz; } #else @@ -367,9 +376,6 @@ add_extra_suffixes(char *prog, int type) if (type == EMU_TYPE_SMP) { p = write_str(p, SMP_SUFFIX); } - else if (type == EMU_TYPE_HYBRID) { - p = write_str(p, HYBRID_SUFFIX); - } #ifdef __WIN32__ if (dll) { p = write_str(p, DLL_EXT); @@ -535,13 +541,6 @@ int main(int argc, char **argv) emu_type_passed |= EMU_TYPE_DEBUG; emu_type |= EMU_TYPE_DEBUG; #endif - } else if (strcmp(argv[i], "-hybrid") == 0) { - emu_type_passed |= EMU_TYPE_HYBRID; -#ifdef ERTS_HAVE_HYBRID_EMU - emu_type |= EMU_TYPE_HYBRID; -#else - usage_notsup("-hybrid"); -#endif } else if (strcmp(argv[i], "-extra") == 0) { break; } @@ -552,19 +551,6 @@ int main(int argc, char **argv) erts_cpu_info_destroy(cpuinfo); cpuinfo = NULL; - if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) { - /* - * We have a conflict. Only using explicitly passed arguments - * may solve it... - */ - emu_type &= emu_type_passed; - if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) { - usage_msg("Hybrid heap emulator with SMP support selected. The " - "combination hybrid heap and SMP support is currently " - "not supported."); - } - } - if (malloc_lib) { if (strcmp(malloc_lib, "libc") != 0) usage("+MYm"); @@ -1115,9 +1101,6 @@ usage_aux(void) "]" #endif "] " -#ifdef ERTS_HAVE_HYBRID_EMU - "[-hybrid] " -#endif "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] " "[-args_file FILENAME] [+A THREADS] [+a SIZE] [+B[c|d|i]] [+c] " "[+h HEAP_SIZE_OPTION] [+K BOOLEAN] " @@ -1135,7 +1118,7 @@ usage(const char *switchname) usage_aux(); } -#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU) +#if !defined(ERTS_HAVE_SMP_EMU) static void usage_notsup(const char *switchname) { @@ -1176,7 +1159,7 @@ start_epmd(char *epmd) erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "%s" DIRSEP "epmd", bindir); arg1 = "-daemon"; #else - erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "%s" DIRSEP "epmd -daemon", bindir); + erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "\"%s" DIRSEP "epmd\" -daemon", bindir); #endif } #ifdef __WIN32__ @@ -2111,4 +2094,147 @@ possibly_quote(char* arg) return narg; } +/* + * Unicode helpers to handle environment and command line parameters on + * Windows. We internally handle all environment variables in UTF8, + * but put and get the environment using the WCHAR (limited UTF16) interface + * + * These are simplified to only handle Unicode characters that can fit in + * Windows simplified UTF16, i.e. characters that fit in 16 bits. + */ + +static int utf8_len(unsigned char first) +{ + if ((first & ((unsigned char) 0x80)) == 0) { + return 1; + } else if ((first & ((unsigned char) 0xE0)) == 0xC0) { + return 2; + } else if ((first & ((unsigned char) 0xF0)) == 0xE0) { + return 3; + } else if ((first & ((unsigned char) 0xF8)) == 0xF0) { + return 4; + } + return 1; /* will be a '?' */ +} + +static WCHAR *utf8_to_utf16(unsigned char *bytes) +{ + unsigned int unipoint; + unsigned char *tmp = bytes; + WCHAR *target, *res; + int num = 0; + + while (*tmp) { + num++; + tmp += utf8_len(*tmp); + } + res = target = emalloc((num + 1) * sizeof(WCHAR)); + while (*bytes) { + if (((*bytes) & ((unsigned char) 0x80)) == 0) { + unipoint = (Uint) *bytes; + ++bytes; + } else if (((*bytes) & ((unsigned char) 0xE0)) == 0xC0) { + unipoint = + (((Uint) ((*bytes) & ((unsigned char) 0x1F))) << 6) | + ((Uint) (bytes[1] & ((unsigned char) 0x3F))); + bytes += 2; + } else if (((*bytes) & ((unsigned char) 0xF0)) == 0xE0) { + unipoint = + (((Uint) ((*bytes) & ((unsigned char) 0xF))) << 12) | + (((Uint) (bytes[1] & ((unsigned char) 0x3F))) << 6) | + ((Uint) (bytes[2] & ((unsigned char) 0x3F))); + if (unipoint > 0xFFFF) { + unipoint = (unsigned int) '?'; + } + bytes +=3; + } else if (((*bytes) & ((unsigned char) 0xF8)) == 0xF0) { + unipoint = (unsigned int) '?'; /* Cannot put in a wchar */ + bytes += 4; + } else { + unipoint = (unsigned int) '?'; + } + *target++ = (WCHAR) unipoint; + } + *target = L'\0'; + return res; +} + +static int put_utf8(WCHAR ch, unsigned char *target, int sz, int *pos) +{ + Uint x = (Uint) ch; + if (x < 0x80) { + if (*pos >= sz) { + return -1; + } + target[(*pos)++] = (unsigned char) x; + } + else if (x < 0x800) { + if (((*pos) + 1) >= sz) { + return -1; + } + target[(*pos)++] = (((unsigned char) (x >> 6)) | + ((unsigned char) 0xC0)); + target[(*pos)++] = (((unsigned char) (x & 0x3F)) | + ((unsigned char) 0x80)); + } else { + if ((x >= 0xD800 && x <= 0xDFFF) || + (x == 0xFFFE) || + (x == 0xFFFF)) { /* Invalid unicode range */ + return -1; + } + if (((*pos) + 2) >= sz) { + return -1; + } + + target[(*pos)++] = (((unsigned char) (x >> 12)) | + ((unsigned char) 0xE0)); + target[(*pos)++] = ((((unsigned char) (x >> 6)) & 0x3F) | + ((unsigned char) 0x80)); + target[(*pos)++] = (((unsigned char) (x & 0x3F)) | + ((unsigned char) 0x80)); + } + return 0; +} + +static int need_bytes_for_utf8(WCHAR x) +{ + if (x < 0x80) + return 1; + else if (x < 0x800) + return 2; + else + return 3; +} + +static WCHAR *latin1_to_utf16(char *str) +{ + int len = strlen(str); + int i; + WCHAR *wstr = emalloc((len+1) * sizeof(WCHAR)); + for(i=0;i<len;++i) + wstr[i] = (WCHAR) str[i]; + wstr[len] = L'\0'; + return wstr; +} + +static char *utf16_to_utf8(WCHAR *wstr) +{ + int len = wcslen(wstr); + char *result; + int i,pos; + int reslen = 0; + for(i=0;i<len;++i) { + reslen += need_bytes_for_utf8(wstr[i]); + } + result = emalloc(reslen+1); + pos = 0; + for(i=0;i<len;++i) { + if (put_utf8((int) wstr[i], result, reslen, &pos) < 0) { + break; + } + } + result[pos] = '\0'; + return result; +} + #endif diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index 755e308219..70c2b3bb23 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -102,9 +102,6 @@ * that can simulate gethrtime with clock_gettime, no use implementing * a phony gethrtime in this file as the time questions are so infrequent. */ -#if defined(CORRET_USING_TIMES) || defined(GETHRTIME_WITH_CLOCK_GETTIME) -# define HEART_CORRECT_USING_TIMES 1 -#endif #include <stdio.h> #include <stddef.h> @@ -134,7 +131,7 @@ # include <sys/time.h> # include <unistd.h> # include <signal.h> -# if defined(HEART_CORRECT_USING_TIMES) +# if defined(CORRECT_USING_TIMES) # include <sys/times.h> # include <limits.h> # endif @@ -447,7 +444,8 @@ message_loop(erlin_fd, erlout_fd) */ timestamp(&now); if (now > last_received + heart_beat_timeout) { - print_error("heart-beat time-out."); + print_error("heart-beat time-out, no activity for %lu seconds", + (unsigned long) (now - last_received)); return R_TIMEOUT; } /* @@ -1079,7 +1077,31 @@ time_t timestamp(time_t *res) return r; } -#elif defined(HAVE_GETHRTIME) +#elif defined(HAVE_GETHRTIME) || defined(GETHRTIME_WITH_CLOCK_GETTIME) + +#if defined(GETHRTIME_WITH_CLOCK_GETTIME) +typedef long long SysHrTime; + +SysHrTime sys_gethrtime(void); + +SysHrTime sys_gethrtime(void) +{ + struct timespec ts; + long long result; + if (clock_gettime(CLOCK_MONOTONIC,&ts) != 0) { + print_error("Fatal, could not get clock_monotonic value, terminating! " + "errno = %d\n", errno); + exit(1); + } + result = ((long long) ts.tv_sec) * 1000000000LL + + ((long long) ts.tv_nsec); + return (SysHrTime) result; +} +#else +typedef hrtime_t SysHrTime; +#define sys_gethrtime() gethrtime() +#endif + void init_timestamp(void) { @@ -1087,14 +1109,14 @@ void init_timestamp(void) time_t timestamp(time_t *res) { - hrtime_t ht = gethrtime(); + SysHrTime ht = sys_gethrtime(); time_t r = (time_t) (ht / 1000000000); if (res != NULL) *res = r; return r; } -#elif defined(HEART_CORRECT_USING_TIMES) +#elif defined(CORRECT_USING_TIMES) # ifdef NO_SYSCONF # include <sys/param.h> diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src index 8f40c43874..58f7b38ed0 100644 --- a/erts/etc/unix/Install.src +++ b/erts/etc/unix/Install.src @@ -66,12 +66,12 @@ then exit 1 fi -if [ ! -d $ERL_ROOT/bin ] +if [ ! -d "$ERL_ROOT/bin" ] then - mkdir $ERL_ROOT/bin + mkdir "$ERL_ROOT/bin" fi -cd $ERL_ROOT/erts-%I_VSN%/bin +cd "$ERL_ROOT/erts-%I_VSN%/bin" sed -e "s;%FINAL_ROOTDIR%;$TARGET_ERL_ROOT;" erl.src > erl chmod 755 erl @@ -79,18 +79,18 @@ chmod 755 erl # # Create start file for embedded system use, # -(cd $ERL_ROOT/erts-%I_VSN%/bin; +(cd "$ERL_ROOT/erts-%I_VSN%/bin"; sed -e "s;%FINAL_ROOTDIR%;$TARGET_ERL_ROOT;" start.src > start; chmod 755 start) -cd $ERL_ROOT/bin +cd "$ERL_ROOT/bin" -cp -p $ERL_ROOT/erts-%I_VSN%/bin/erl . -cp -p $ERL_ROOT/erts-%I_VSN%/bin/erlc . -cp -p $ERL_ROOT/erts-%I_VSN%/bin/dialyzer . -cp -p $ERL_ROOT/erts-%I_VSN%/bin/typer . -cp -p $ERL_ROOT/erts-%I_VSN%/bin/ct_run . -cp -p $ERL_ROOT/erts-%I_VSN%/bin/escript . +cp -p "$ERL_ROOT/erts-%I_VSN%/bin/erl" . +cp -p "$ERL_ROOT/erts-%I_VSN%/bin/erlc" . +cp -p "$ERL_ROOT/erts-%I_VSN%/bin/dialyzer" . +cp -p "$ERL_ROOT/erts-%I_VSN%/bin/typer" . +cp -p "$ERL_ROOT/erts-%I_VSN%/bin/ct_run" . +cp -p "$ERL_ROOT/erts-%I_VSN%/bin/escript" . # Remove in R16B ln -s ct_run run_test @@ -107,15 +107,15 @@ fi ln -s ../erts-%I_VSN%/bin/epmd epmd -cp -p $ERL_ROOT/erts-%I_VSN%/bin/run_erl . -cp -p $ERL_ROOT/erts-%I_VSN%/bin/to_erl . -cp -p $ERL_ROOT/erts-%I_VSN%/bin/start . -sed -e "s;%EMU%;%EMULATOR%%EMULATOR_NUMBER%;" $ERL_ROOT/erts-%I_VSN%/bin/start_erl.src > start_erl +cp -p "$ERL_ROOT/erts-%I_VSN%/bin/run_erl" . +cp -p "$ERL_ROOT/erts-%I_VSN%/bin/to_erl" . +cp -p "$ERL_ROOT/erts-%I_VSN%/bin/start" . +sed -e "s;%EMU%;%EMULATOR%%EMULATOR_NUMBER%;" "$ERL_ROOT/erts-%I_VSN%/bin/start_erl.src" > start_erl chmod 755 start_erl echo "" -echo %I_VSN% %I_SYSTEM_VSN% > $ERL_ROOT/releases/start_erl.data -sed -e "s;%ERL_ROOT%;$TARGET_ERL_ROOT;" $ERL_ROOT/releases/RELEASES.src > $ERL_ROOT/releases/RELEASES +echo %I_VSN% %I_SYSTEM_VSN% > "$ERL_ROOT/releases/start_erl.data" +sed -e "s;%ERL_ROOT%;$TARGET_ERL_ROOT;" "$ERL_ROOT/releases/RELEASES.src" > "$ERL_ROOT/releases/RELEASES" if [ "$start_option" = "query" ] then @@ -147,10 +147,10 @@ cp -p ../releases/%I_SYSTEM_VSN%/$Name.script start.script # Fixing the man pages # -if [ -d $ERL_ROOT/man ] +if [ -d "$ERL_ROOT/man" ] then - cd $ERL_ROOT - ./misc/format_man_pages $ERL_ROOT + cd "$ERL_ROOT" + ./misc/format_man_pages "$ERL_ROOT" fi exit 0 diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 0b2d6512ea..ffd48d5811 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -227,7 +227,7 @@ done PATH=$BINDIR:$ROOTDIR/bin:$PATH EXEC=$BINDIR/erlexec -PROGNAME="$PROGNAME $cargs" +PROGNAME="$PROGNAME$cargs" EMU="$EMU$TYPE" EMU_NAME=`$EXEC -emu_name_exit $eeargs` @@ -302,7 +302,7 @@ else # Set annotation level for gdb in emacs 22 and higher. emacs_major=`$EMACS --version | head -1 | sed 's,^[^0-9]*\([0-9]*\).*,\1,g'` if [ '!' -z "$emacs_major" -a $emacs_major -gt 21 ]; then - GDBARGS="--annotate=3 " + GDBARGS="--annotate=1 " fi gdbcmd="$gdbcmd $GDBBP \ (insert-string \"source $ROOTDIR/erts/etc/unix/etp-commands\") \ diff --git a/erts/etc/unix/erl.src.src b/erts/etc/unix/erl.src.src index 50603f12f4..fa187c5509 100644 --- a/erts/etc/unix/erl.src.src +++ b/erts/etc/unix/erl.src.src @@ -17,7 +17,7 @@ # # %CopyrightEnd% # -ROOTDIR=%FINAL_ROOTDIR% +ROOTDIR="%FINAL_ROOTDIR%" BINDIR=$ROOTDIR/erts-%VSN%/bin EMU=%EMULATOR%%EMULATOR_NUMBER% PROGNAME=`echo $0 | sed 's/.*\///'` @@ -25,4 +25,4 @@ export EMU export ROOTDIR export BINDIR export PROGNAME -exec $BINDIR/erlexec ${1+"$@"} +exec "$BINDIR/erlexec" ${1+"$@"} diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index 8db8e09bee..6b350e8bd5 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -126,7 +126,7 @@ /* prototypes */ static void usage(char *); static int create_fifo(char *name, int perm); -static int open_pty_master(char **name); +static int open_pty_master(char **name, int *sfd); static int open_pty_slave(char *name); static void pass_on(pid_t); static void exec_shell(char **); @@ -150,6 +150,10 @@ static int write_all(int fd, const char* buf, int len); static int extract_ctrl_seq(char* buf, int len); static void set_window_size(unsigned col, unsigned row); +static ssize_t sf_write(int fd, const void *buffer, size_t len); +static ssize_t sf_read(int fd, void *buffer, size_t len); +static int sf_open(const char *path, int flags, mode_t mode); +static int sf_close(int fd); #ifdef DEBUG static void show_terminal_settings(struct termios *t); @@ -216,7 +220,7 @@ static char* outbuf_in; int main(int argc, char **argv) { int childpid; - int sfd; + int sfd = -1; int fd; char *p, *ptyslave=NULL; int i = 1; @@ -338,9 +342,9 @@ int main(int argc, char **argv) strn_cat(fifo2, sizeof(fifo2), ".w"); /* Check that nobody is running run_erl already */ - if ((fd = open (fifo2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { + if ((fd = sf_open(fifo2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { /* Open as client succeeded -- run_erl is already running! */ - close(fd); + sf_close(fd); if (calculated_pipename) { ++highest_pipe_num; strn_catf(pipename, sizeof(pipename), "%s.%d", @@ -361,7 +365,7 @@ int main(int argc, char **argv) * Open master pseudo-terminal */ - if ((mfd = open_pty_master(&ptyslave)) < 0) { + if ((mfd = open_pty_master(&ptyslave, &sfd)) < 0) { ERRNO_ERR0(LOG_ERR,"Could not open pty master"); exit(1); } @@ -376,7 +380,7 @@ int main(int argc, char **argv) } if (childpid == 0) { /* Child */ - close(mfd); + sf_close(mfd); /* disassociate from control terminal */ #ifdef USE_SETPGRP_NOARGS /* SysV */ setpgrp(); @@ -386,15 +390,30 @@ int main(int argc, char **argv) setsid(); #endif /* Open the slave pty */ - if ((sfd = open_pty_slave(ptyslave)) < 0) { - ERRNO_ERR1(LOG_ERR,"Could not open pty slave '%s'", ptyslave); - exit(1); + if (sfd < 0) { + /* not allocated by open_pty_master */ + if ((sfd = open_pty_slave(ptyslave)) < 0) { + ERRNO_ERR1(LOG_ERR,"Could not open pty slave '%s'", ptyslave); + exit(1); + } + /* But sfd may be one of the stdio fd's now, and we should be unmodern and not use dup2... */ + /* easiest to dup it up... */ + while (sfd < 3) { + sfd = dup(sfd); + } } - /* But sfd may be one of the stdio fd's now, and we should be unmodern and not use dup2... */ - /* easiest to dup it up... */ - while (sfd < 3) { - sfd = dup(sfd); +#if defined(HAVE_OPENPTY) && defined(TIOCSCTTY) + else { + /* sfd is from open_pty_master + * openpty -> fork -> login_tty (forkpty) + * + * It would be preferable to implement a portable + * forkpty instead of open_pty_master / open_pty_slave + */ + /* login_tty(sfd); <- FAIL */ + ioctl(sfd, TIOCSCTTY, (char *)NULL); } +#endif #ifndef NO_SYSLOG /* Before fiddling with file descriptors we make sure syslog is turned off @@ -407,14 +426,14 @@ int main(int argc, char **argv) #endif /* Close stdio */ - close(0); - close(1); - close(2); + sf_close(0); + sf_close(1); + sf_close(2); if (dup(sfd) != 0 || dup(sfd) != 1 || dup(sfd) != 2) { status("Cannot dup\n"); } - close(sfd); + sf_close(sfd); exec_shell(argv+off_argv); /* exec_shell expects argv[2] to be */ /* the command name, so we have to */ /* adjust. */ @@ -466,7 +485,7 @@ static void pass_on(pid_t childpid) * We can't open the writing side because nobody is reading and * we'd either hang or get an error. */ - if ((rfd = open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { + if ((rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2); exit(1); } @@ -559,7 +578,7 @@ static void pass_on(pid_t childpid) char* buf = outbuf_first(); len = outbuf_size(); - written = write(wfd, buf, len); + written = sf_write(wfd, buf, len); if (written < 0 && errno == EAGAIN) { /* * Nothing was written - this is really strange because @@ -570,7 +589,7 @@ static void pass_on(pid_t childpid) * A write error. Assume that to_erl has terminated. */ clear_outbuf(); - close(wfd); + sf_close(wfd); wfd = 0; } else { /* Delete the written part (or all) from the buffer. */ @@ -585,10 +604,10 @@ static void pass_on(pid_t childpid) #ifdef DEBUG status("Pty master read; "); #endif - if ((len = read(mfd, buf, BUFSIZ)) <= 0) { - close(rfd); - if(wfd) close(wfd); - close(mfd); + if ((len = sf_read(mfd, buf, BUFSIZ)) <= 0) { + sf_close(rfd); + if(wfd) sf_close(wfd); + sf_close(mfd); unlink(fifo1); unlink(fifo2); if (len < 0) { @@ -619,10 +638,10 @@ static void pass_on(pid_t childpid) #ifdef DEBUG status("FIFO read; "); #endif - if ((len = read(rfd, buf, BUFSIZ)) < 0) { - close(rfd); - if(wfd) close(wfd); - close(mfd); + if ((len = sf_read(rfd, buf, BUFSIZ)) < 0) { + sf_close(rfd); + if(wfd) sf_close(wfd); + sf_close(mfd); unlink(fifo1); unlink(fifo2); ERRNO_ERR0(LOG_ERR,"Error in reading from FIFO."); @@ -631,8 +650,8 @@ static void pass_on(pid_t childpid) if(!len) { /* to_erl closed its end of the pipe */ - close(rfd); - rfd = open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0); + sf_close(rfd); + rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0); if (rfd < 0) { ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2); exit(1); @@ -645,11 +664,11 @@ static void pass_on(pid_t childpid) * from to_erl, to_erl should already be reading this pipe - open * should succeed. But in case of error, we just ignore it. */ - if ((wfd = open(fifo1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { + if ((wfd = sf_open(fifo1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { status("Client expected on FIFO %s, but can't open (len=%d)\n", fifo1, len); - close(rfd); - rfd = open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0); + sf_close(rfd); + rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0); if (rfd < 0) { ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2); exit(1); @@ -683,9 +702,9 @@ static void pass_on(pid_t childpid) } else if (len>0 && write_all(mfd, buf, len) != len) { ERRNO_ERR0(LOG_ERR,"Error in writing to terminal."); - close(rfd); - if(wfd) close(wfd); - close(mfd); + sf_close(rfd); + if(wfd) sf_close(wfd); + sf_close(mfd); exit(1); } } @@ -797,7 +816,7 @@ static int open_log(int log_num, int flags) /* Create or continue on the current log file */ sn_printf(buf, sizeof(buf), "%s/%s%d", log_dir, LOG_STUBNAME, log_num); - if((lfd = open(buf, flags, LOG_PERM))<0){ + if((lfd = sf_open(buf, flags, LOG_PERM))<0){ ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf); exit(1); } @@ -841,7 +860,7 @@ static void write_to_log(int* lfd, int* log_num, char* buf, int len) size = lseek(*lfd,0,SEEK_END); if(size+len > log_maxsize) { - close(*lfd); + sf_close(*lfd); *log_num = next_log(*log_num); *lfd = open_log(*log_num, O_RDWR|O_CREAT|O_TRUNC|O_SYNC); } @@ -872,7 +891,7 @@ static int create_fifo(char *name, int perm) * Find a master device, open and return fd and slave device name. */ -static int open_pty_master(char **ptyslave) +static int open_pty_master(char **ptyslave, int *sfdp) { int mfd; @@ -882,7 +901,9 @@ static int open_pty_master(char **ptyslave) # ifdef HAVE_WORKING_POSIX_OPENPT if ((mfd = posix_openpt(O_RDWR)) >= 0) { # elif defined(__sun) && defined(__SVR4) - if ((mfd = open("/dev/ptmx", O_RDWR)) >= 0) { + mfd = sf_open("/dev/ptmx", O_RDWR, 0); + + if (mfd >= 0) { # endif if ((*ptyslave = ptsname(mfd)) != NULL && grantpt(mfd) == 0 && @@ -890,12 +911,12 @@ static int open_pty_master(char **ptyslave) return mfd; } - close(mfd); + sf_close(mfd); } /* fallback to openpty if it exist */ #endif -#ifdef HAVE_OPENPTY +#if defined(HAVE_OPENPTY) # ifdef PATH_MAX # define SLAVE_SIZE PATH_MAX # else @@ -903,11 +924,8 @@ static int open_pty_master(char **ptyslave) # endif { static char slave[SLAVE_SIZE]; - int sfd; # undef SLAVE_SIZE - - if (openpty(&mfd, &sfd, slave, NULL, NULL) == 0) { - close(sfd); + if (openpty(&mfd, sfdp, slave, NULL, NULL) == 0) { *ptyslave = slave; return mfd; } @@ -939,7 +957,8 @@ static int open_pty_master(char **ptyslave) for (minor = minorchars; *minor; minor++) { ptyname[10] = *minor; - if ((mfd = open(ptyname, O_RDWR, 0)) >= 0) { + + if ((mfd = sf_open(ptyname, O_RDWR, 0)) >= 0) { ptyname[9] = 's'; *ptyslave = ptyname; return mfd; @@ -957,7 +976,7 @@ static int open_pty_master(char **ptyslave) ptyname[13] = *major; for (minor = minorchars; *minor; minor++) { ptyname[14] = *minor; - if ((mfd = open(ptyname, O_RDWR, 0)) >= 0) { + if ((mfd = sf_open(ptyname, O_RDWR, 0)) >= 0) { ttyname[12] = *major; ttyname[13] = *minor; *ptyslave = ttyname; @@ -976,7 +995,7 @@ static int open_pty_master(char **ptyslave) ptyname[8] = *major; for (minor = minorchars; *minor; minor++) { ptyname[9] = *minor; - if ((mfd = open(ptyname, O_RDWR, 0)) >= 0) { + if ((mfd = sf_open(ptyname, O_RDWR, 0)) >= 0) { ptyname[5] = 't'; *ptyslave = ptyname; return mfd; @@ -993,7 +1012,7 @@ static int open_pty_slave(char *name) int sfd; struct termios tty_rmode; - if ((sfd = open(name, O_RDWR, 0)) < 0) { + if ((sfd = sf_open(name, O_RDWR, 0)) < 0) { return -1; } @@ -1120,7 +1139,7 @@ static void daemon_init(void) would be backward incompatible */ for (i = 0; i < maxfd; ++i ) { - close(i); + sf_close(i); } OPEN_SYSLOG(); @@ -1246,7 +1265,7 @@ static int write_all(int fd, const char* buf, int len) int left = len; int written; for (;;) { - written = write(fd,buf,left); + written = sf_write(fd,buf,left); if (written == left) { return len; } @@ -1258,6 +1277,36 @@ static int write_all(int fd, const char* buf, int len) } } +static ssize_t sf_read(int fd, void *buffer, size_t len) { + ssize_t n = 0; + + do { n = read(fd, buffer, len); } while (n < 0 && errno == EINTR); + + return n; +} + +static ssize_t sf_write(int fd, const void *buffer, size_t len) { + ssize_t n = 0; + + do { n = write(fd, buffer, len); } while (n < 0 && errno == EINTR); + + return n; +} + +static int sf_open(const char *path, int type, mode_t mode) { + int fd = 0; + + do { fd = open(path, type, mode); } while(fd < 0 && errno == EINTR); + + return fd; +} +static int sf_close(int fd) { + int res = 0; + + do { res = close(fd); } while(fd < 0 && errno == EINTR); + + return res; +} /* Extract any control sequences that are ment only for run_erl * and should not be forwarded to the pty. */ diff --git a/erts/etc/win32/Makefile b/erts/etc/win32/Makefile index 400e5c5bba..4218be1eff 100644 --- a/erts/etc/win32/Makefile +++ b/erts/etc/win32/Makefile @@ -59,14 +59,14 @@ opt debug all clean depend: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/bin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(ROOTSYSDIR)/usr/include - $(INSTALL_DIR) $(ROOTSYSDIR)/usr/lib - $(INSTALL_DIR) $(ROOTSYSDIR)/usr/lib/icons - $(INSTALL_PROGRAM) $(INSTALL_PROGS) $(RELSYSDIR)/bin - $(INSTALL_DATA) $(INSTALL_SRC) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INSTALL_ICONS) $(ROOTSYSDIR)/usr/lib/icons + $(INSTALL_DIR) "$(RELSYSDIR)/bin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(ROOTSYSDIR)/usr/include" + $(INSTALL_DIR) "$(ROOTSYSDIR)/usr/lib" + $(INSTALL_DIR) "$(ROOTSYSDIR)/usr/lib/icons" + $(INSTALL_PROGRAM) $(INSTALL_PROGS) "$(RELSYSDIR)/bin" + $(INSTALL_DATA) $(INSTALL_SRC) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INSTALL_ICONS) "$(ROOTSYSDIR)/usr/lib/icons" release_docs release_docs_spec docs: diff --git a/erts/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c index 242e2905a9..8b734b0c05 100644 --- a/erts/etc/win32/erlsrv/erlsrv_service.c +++ b/erts/etc/win32/erlsrv/erlsrv_service.c @@ -104,11 +104,10 @@ static VOID WINAPI handler(DWORD control){ log_debug(buffer); switch(control){ case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: set_stop_pending(30000,1); SetEvent(eventStop); return; - case SERVICE_CONTROL_SHUTDOWN: - return; default: reset_current(); break; diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile index 6a93c5153d..4ed4ef2700 100644 --- a/erts/etc/win32/nsis/Makefile +++ b/erts/etc/win32/nsis/Makefile @@ -44,14 +44,14 @@ TARGET_DIR = $(RELEASE_PATH) ifeq ($(MSYSTEM),MINGW32) MAKENSISFLAGS = //V2 - WTESTROOT=$(shell (msys2win_path.sh $(RELEASE_PATH))) - WTARGET_DIR=$(shell (msys2win_path.sh $(TARGET_DIR))) + WTESTROOT=$(shell (msys2win_path.sh "$(RELEASE_PATH)")) + WTARGET_DIR=$(shell (msys2win_path.sh "$(TARGET_DIR)")) else MAKENSISFLAGS = /V2 - WTESTROOT=$(shell (cygpath -d $(RELEASE_PATH) 2>/dev/null || cygpath -w $(RELEASE_PATH))) - WTARGET_DIR=$(shell (cygpath -d $(TARGET_DIR) 2>/dev/null || cygpath -d $(TARGET_DIR))) + WTESTROOT=$(shell (cygpath -d "$(RELEASE_PATH)" 2>/dev/null || cygpath -w "$(RELEASE_PATH)")) + WTARGET_DIR=$(shell (cygpath -d "$(TARGET_DIR)" 2>/dev/null || cygpath -d "$(TARGET_DIR)")) endif @@ -74,7 +74,7 @@ release_spec: echo '!define ERTS_VERSION "$(VSN)"' >> $(VERSION_HEADER);\ echo '!define TESTROOT "$(WTESTROOT)"' >> $(VERSION_HEADER);\ echo '!define OUTFILEDIR "$(WTARGET_DIR)"' >> $(VERSION_HEADER);\ - if [ -f $(RELEASE_PATH)/docs/doc/index.html ];\ + if [ -f "$(RELEASE_PATH)/docs/doc/index.html" ];\ then\ echo '!define HAVE_DOCS 1' >> $(VERSION_HEADER); \ fi;\ @@ -91,13 +91,13 @@ release_spec: fi;\ if [ '!' -z "$(REDIST_FILE)" -a '!' -z "$(REDIST_DLL_VERSION)" ];\ then \ - cp $(REDIST_FILE) $(RELEASE_PATH)/$(REDIST_TARGET);\ + cp $(REDIST_FILE) "$(RELEASE_PATH)/$(REDIST_TARGET);"\ echo '!define HAVE_REDIST_FILE 1' >> $(VERSION_HEADER); \ echo '!define REDIST_DLL_VERSION "$(REDIST_DLL_VERSION)"' >> $(VERSION_HEADER);\ echo '!define REDIST_DLL_NAME "$(REDIST_DLL_NAME)"' >> $(VERSION_HEADER);\ echo '!define REDIST_EXECUTABLE "$(REDIST_TARGET)"' >> $(VERSION_HEADER);\ fi;\ - if [ -f $(RELEASE_PATH)/docs/doc/index.html ];\ + if [ -f "$(RELEASE_PATH)/docs/doc/index.html" ];\ then \ echo '!define HAVE_DOCS 1' >> $(VERSION_HEADER); \ fi;\ diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi index fb0eff3867..c5ada9e3b3 100644 --- a/erts/etc/win32/nsis/erlang20.nsi +++ b/erts/etc/win32/nsis/erlang20.nsi @@ -4,6 +4,25 @@ ; Original example written by Joost Verburg
; Modified for Erlang by Patrik
+;
+; %CopyrightBegin%
+;
+; Copyright Ericsson AB 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
+; 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%
+;
+
; Verbosity does not come naturally with MUI, have to set it back now and then.
!verbose 1
!define MUI_MANUALVERBOSE 1
@@ -109,8 +128,11 @@ Section "Microsoft redistributable libraries." SecMSRedist ; Set back verbosity...
!verbose 1
-; Run the setup program
- ExecWait '"$INSTDIR\${REDIST_EXECUTABLE}"'
+; Run the setup program
+ IfSilent +3
+ ExecWait '"$INSTDIR\${REDIST_EXECUTABLE}"'
+ Goto +2
+ ExecWait '"$INSTDIR\${REDIST_EXECUTABLE}" /q'
!verbose 1
SectionEnd ; MSRedist
@@ -317,24 +339,32 @@ Function DllVersionGoodEnough FunctionEnd
Function .onInit
- SectionGetFlags 0 $MYTEMP
- ;MessageBox MB_YESNO "Found $SYSDIR\${REDIST_DLL_NAME}" IDYES FoundLbl
- IfFileExists $SYSDIR\${REDIST_DLL_NAME} MaybeFoundInSystemLbl
- SearchSxsLbl:
- FindFirst $0 $1 $WINDIR\WinSxS\x86*
+ Var /GLOBAL archprefix
+ Var /GLOBAL sysnativedir
+ SectionGetFlags 0 $MYTEMP
+ StrCmpS ${WINTYPE} "win64" +1 +4
+ StrCpy $archprefix "amd64"
+ StrCpy $sysnativedir "$WINDIR\sysnative"
+ Goto +3
+ StrCpy $archprefix "x86"
+ StrCpy $sysnativedir $SYSDIR
+ ;MessageBox MB_YESNO "Found $sysnativedir\${REDIST_DLL_NAME}" IDYES FoundLbl
+ IfFileExists $sysnativedir\${REDIST_DLL_NAME} MaybeFoundInSystemLbl
+ SearchSxSLbl:
+ FindFirst $0 $1 $WINDIR\WinSxS\$archprefix*
LoopLbl:
StrCmp $1 "" NotFoundLbl
- IfFileExists $WINDIR\WinSxS\$1\${REDIST_DLL_NAME} MaybeFoundInSxsLbl
+ IfFileExists $WINDIR\WinSxS\$1\${REDIST_DLL_NAME} MaybeFoundInSxSLbl
FindNext $0 $1
Goto LoopLbl
- MaybeFoundInSxsLbl:
+ MaybeFoundInSxSLbl:
GetDllVersion $WINDIR\WinSxS\$1\${REDIST_DLL_NAME} $R0 $R1
Call DllVersionGoodEnough
FindNext $0 $1
IntCmp 2 $R0 LoopLbl
Goto FoundLbl
MaybeFoundInSystemLbl:
- GetDllVersion $SYSDIR\${REDIST_DLL_NAME} $R0 $R1
+ GetDllVersion $sysnativedir\${REDIST_DLL_NAME} $R0 $R1
Call DllVersionGoodEnough
IntCmp 2 $R0 SearchSxSLbl
FoundLbl:
diff --git a/erts/etc/win32/port_entry.c b/erts/etc/win32/port_entry.c index 49b5ad2f34..9c3e750cbc 100644 --- a/erts/etc/win32/port_entry.c +++ b/erts/etc/win32/port_entry.c @@ -45,6 +45,8 @@ extern void mainCRTStartup(void); BOOL WINAPI erl_port_default_handler(DWORD ctrl){ if(ctrl == CTRL_LOGOFF_EVENT) return TRUE; + if(ctrl == CTRL_SHUTDOWN_EVENT) + return TRUE; return FALSE; } diff --git a/erts/etc/win32/start_erl.c b/erts/etc/win32/start_erl.c index 28c8e55bd3..41b221d5bc 100644 --- a/erts/etc/win32/start_erl.c +++ b/erts/etc/win32/start_erl.c @@ -585,6 +585,9 @@ BOOL WINAPI LogoffHandlerRoutine( DWORD dwCtrlType ) if(dwCtrlType == CTRL_LOGOFF_EVENT) { return TRUE; } + if(dwCtrlType == CTRL_SHUTDOWN_EVENT) { + return TRUE; + } return FALSE; } diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in index ea80e74100..aed889eaef 100644 --- a/erts/lib_src/Makefile.in +++ b/erts/lib_src/Makefile.in @@ -473,31 +473,31 @@ INTERNAL_RELEASE_LIBS= \ .PHONY: release_spec release_spec: all ifneq ($(strip $(RELEASE_INCLUDES)),) - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELEASE_PATH)/usr/include - $(INSTALL_DATA) $(RELEASE_INCLUDES) $(RELSYSDIR)/include - $(INSTALL_DATA) $(RELEASE_INCLUDES) $(RELEASE_PATH)/usr/include + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELEASE_PATH)/usr/include" + $(INSTALL_DATA) $(RELEASE_INCLUDES) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(RELEASE_INCLUDES) "$(RELEASE_PATH)/usr/include" endif ifneq ($(strip $(INTERNAL_RELEASE_INCLUDES)),) - $(INSTALL_DIR) $(RELSYSDIR)/include/internal - $(INSTALL_DATA) $(INTERNAL_RELEASE_INCLUDES) $(RELSYSDIR)/include/internal + $(INSTALL_DIR) "$(RELSYSDIR)/include/internal" + $(INSTALL_DATA) $(INTERNAL_RELEASE_INCLUDES) "$(RELSYSDIR)/include/internal" endif ifneq ($(strip $(INTERNAL_X_RELEASE_INCLUDE_DIRS)),) for xdir in $(INTERNAL_X_RELEASE_INCLUDE_DIRS); do \ - $(INSTALL_DIR) $(RELSYSDIR)/include/internal/$$xdir; \ + $(INSTALL_DIR) "$(RELSYSDIR)/include/internal/$$xdir"; \ $(INSTALL_DATA) $(ERTS_INCL_INT)/$$xdir/*.h \ - $(RELSYSDIR)/include/internal/$$xdir; \ + "$(RELSYSDIR)/include/internal/$$xdir"; \ done endif ifneq ($(strip $(RELEASE_LIBS)),) - $(INSTALL_DIR) $(RELSYSDIR)/lib - $(INSTALL_DIR) $(RELEASE_PATH)/usr/lib - $(INSTALL_DATA) $(RELEASE_LIBS) $(RELSYSDIR)/lib - $(INSTALL_DATA) $(RELEASE_LIBS) $(RELEASE_PATH)/usr/lib + $(INSTALL_DIR) "$(RELSYSDIR)/lib" + $(INSTALL_DIR) "$(RELEASE_PATH)/usr/lib" + $(INSTALL_DATA) $(RELEASE_LIBS) "$(RELSYSDIR)/lib" + $(INSTALL_DATA) $(RELEASE_LIBS) "$(RELEASE_PATH)/usr/lib" endif ifneq ($(strip $(INTERNAL_RELEASE_LIBS)),) - $(INSTALL_DIR) $(RELSYSDIR)/lib/internal - $(INSTALL_DATA) $(INTERNAL_RELEASE_LIBS) $(RELSYSDIR)/lib/internal + $(INSTALL_DIR) "$(RELSYSDIR)/lib/internal" + $(INSTALL_DATA) $(INTERNAL_RELEASE_LIBS) "$(RELSYSDIR)/lib/internal" endif .PHONY: docs diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c index 473791dce4..71d2aa35d0 100644 --- a/erts/lib_src/common/erl_printf_format.c +++ b/erts/lib_src/common/erl_printf_format.c @@ -457,6 +457,15 @@ static int fmt_double(fmtfn_t fn,void*arg,double val, return res; } +/* strnlen doesn't exist everywhere */ +static size_t my_strnlen(const char *s, size_t maxlen) +{ + size_t i = 0; + while (i < maxlen && s[i] != '\0') + i++; + return i; +} + int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) { char* ptr0 = fmt; @@ -771,9 +780,7 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) case FMTC_s: { char* str = va_arg(ap,char*); - int len = strlen(str); - if (precision >= 0 && precision < len) - len = precision; + int len = (precision >= 0) ? my_strnlen(str,precision) : strlen(str); if (width > 0 && !(fmt & FMTF_adj)) { if (width > len) BLANKS(fn, arg, width - len, count); diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index 521640317e..89149b716b 100644 --- a/erts/lib_src/common/ethr_aux.c +++ b/erts/lib_src/common/ethr_aux.c @@ -40,7 +40,7 @@ #include <unistd.h> #endif -#define ERTS_TS_EV_ALLOC_DEFAULT_POOL_SIZE 100 +#define ERTS_TS_EV_ALLOC_DEFAULT_POOL_SIZE 4000 #define ERTS_TS_EV_ALLOC_POOL_SIZE 25 erts_cpu_info_t *ethr_cpu_info__; diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam Binary files differindex df1831f340..80fc79f396 100644 --- a/erts/preloaded/ebin/erl_prim_loader.beam +++ b/erts/preloaded/ebin/erl_prim_loader.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex cc170b86b2..b588b450c9 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex b2f3ab6c5b..ad49f5e892 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 145638802f..d5f5ba3c37 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -68,10 +68,10 @@ copy: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(STATIC_TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(STATIC_TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 14a7a2bf20..5ee3d03fef 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1,7 +1,7 @@ %% %% %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 @@ -49,7 +49,7 @@ prim_read_file_info/2, prim_get_cwd/2]). %% Used by escript and code --export([set_primary_archive/3, release_archives/0]). +-export([set_primary_archive/4, release_archives/0]). -include_lib("kernel/include/file.hrl"). @@ -222,15 +222,16 @@ get_cwd(Drive) -> check_file_result(get_cwd, Drive, request({get_cwd,[Drive]})). -spec set_primary_archive(File :: string() | 'undefined', - ArchiveBin :: binary() | 'undefined', - FileInfo :: #file_info{} | 'undefined') + ArchiveBin :: binary() | 'undefined', + FileInfo :: #file_info{} | 'undefined', + ParserFun :: fun()) -> {ok, [string()]} | {error,_}. -set_primary_archive(undefined, undefined, undefined) -> - request({set_primary_archive, undefined, undefined, undefined}); -set_primary_archive(File, ArchiveBin, FileInfo) +set_primary_archive(undefined, undefined, undefined, ParserFun) -> + request({set_primary_archive, undefined, undefined, undefined, ParserFun}); +set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) when is_list(File), is_binary(ArchiveBin), is_record(FileInfo, file_info) -> - request({set_primary_archive, File, ArchiveBin, FileInfo}). + request({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}). -spec release_archives() -> 'ok' | {'error', _}. @@ -318,8 +319,11 @@ loop(State, Parent, Paths) -> {get_cwd,[_]=Args} -> {Res,State1} = handle_get_cwd(State, Args), {Res,State1,Paths}; - {set_primary_archive,File,Bin,FileInfo} -> - {Res,State1} = handle_set_primary_archive(State, File, Bin, FileInfo), + {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} -> + {Res,State1} = + handle_set_primary_archive(State, File, + ArchiveBin, FileInfo, + ParserFun), {Res,State1,Paths}; release_archives -> {Res,State1} = handle_release_archives(State), @@ -359,8 +363,8 @@ handle_get_file(State = #state{loader = efile}, Paths, File) -> handle_get_file(State = #state{loader = inet}, Paths, File) -> ?SAFE2(inet_get_file_from_port(State, File, Paths), State). -handle_set_primary_archive(State= #state{loader = efile}, File, Bin, FileInfo) -> - ?SAFE2(efile_set_primary_archive(State, File, Bin, FileInfo), State). +handle_set_primary_archive(State= #state{loader = efile}, File, ArchiveBin, FileInfo, ParserFun) -> + ?SAFE2(efile_set_primary_archive(State, File, ArchiveBin, FileInfo, ParserFun), State). handle_release_archives(State= #state{loader = efile}) -> ?SAFE2(efile_release_archives(State), State). @@ -484,8 +488,10 @@ efile_get_file_from_port3(State, File, [P | Paths]) -> efile_get_file_from_port3(State, _File, []) -> {{error,enoent},State}. -efile_set_primary_archive(#state{prim_state = PS} = State, File, Bin, FileInfo) -> - {Res, PS2} = prim_set_primary_archive(PS, File, Bin, FileInfo), +efile_set_primary_archive(#state{prim_state = PS} = State, File, + ArchiveBin, FileInfo, ParserFun) -> + {Res, PS2} = prim_set_primary_archive(PS, File, ArchiveBin, + FileInfo, ParserFun), {Res,State#state{prim_state = PS2}}. efile_release_archives(#state{prim_state = PS} = State) -> @@ -791,7 +797,7 @@ prim_release_archives(PS) -> prim_do_release_archives(PS, [{ArchiveFile, DictVal} | KeyVals], Acc) -> Res = case DictVal of - {primary, _PrimZip, _FI} -> + {primary, _PrimZip, _FI, _ParserFun} -> ok; % Keep primary archive {Cache, _FI} -> debug(PS, {release, cache, ArchiveFile}), @@ -809,7 +815,7 @@ prim_do_release_archives(PS, [], []) -> prim_do_release_archives(PS, [], Errors) -> {{error, Errors}, PS#prim_state{primary_archive = undefined}}. -prim_set_primary_archive(PS, undefined, undefined, undefined) -> +prim_set_primary_archive(PS, undefined, undefined, undefined, _ParserFun) -> debug(PS, {set_primary_archive, clean}), case PS#prim_state.primary_archive of undefined -> @@ -817,48 +823,40 @@ prim_set_primary_archive(PS, undefined, undefined, undefined) -> debug(PS, {return, Res}), {Res, PS}; ArchiveFile -> - {primary, PrimZip, _FI} = erase(ArchiveFile), + {primary, PrimZip, _FI, _ParserFun2} = erase(ArchiveFile), ok = prim_zip:close(PrimZip), PS2 = PS#prim_state{primary_archive = undefined}, Res = {ok, []}, debug(PS2, {return, Res}), {Res, PS2} end; -prim_set_primary_archive(PS, ArchiveFile, ArchiveBin, #file_info{} = FileInfo) - when is_list(ArchiveFile), is_binary(ArchiveBin) -> + +prim_set_primary_archive(PS, ArchiveFile0, ArchiveBin, + #file_info{} = FileInfo, ParserFun) + when is_list(ArchiveFile0), is_binary(ArchiveBin) -> %% Try the archive file - debug(PS, {set_primary_archive, ArchiveFile, byte_size(ArchiveBin)}), + debug(PS, {set_primary_archive, ArchiveFile0, byte_size(ArchiveBin)}), + ArchiveFile = real_path(absname(ArchiveFile0)), {Res3, PS3} = case PS#prim_state.primary_archive of undefined -> - Fun = - fun({Funny, _GI, _GB}, A) -> - case Funny of - ["", "nibe", RevApp] -> % Reverse ebin - %% Collect ebin directories in archive - Ebin = reverse(RevApp) ++ "/ebin", - {true, [Ebin | A]}; - _ -> - {true, A} - end - end, - Ebins0 = [ArchiveFile], - case open_archive({ArchiveFile, ArchiveBin}, FileInfo, Ebins0, Fun) of - {ok, PrimZip, {RevEbins, FI, _}} -> - Ebins = reverse(RevEbins), + case load_prim_archive(ArchiveFile, ArchiveBin, FileInfo) of + {ok, PrimZip, FI, Ebins} -> debug(PS, {set_primary_archive, Ebins}), - put(ArchiveFile, {primary, PrimZip, FI}), - {{ok, Ebins}, PS#prim_state{primary_archive = ArchiveFile}}; + put(ArchiveFile, {primary, PrimZip, FI, ParserFun}), + {{ok, Ebins}, + PS#prim_state{primary_archive = ArchiveFile}}; Error -> debug(PS, {set_primary_archive, Error}), {Error, PS} end; OldArchiveFile -> debug(PS, {set_primary_archive, clean}), - {primary, PrimZip, _FI} = erase(OldArchiveFile), + {primary, PrimZip, _FI, _ParserFun} = erase(OldArchiveFile), ok = prim_zip:close(PrimZip), PS2 = PS#prim_state{primary_archive = undefined}, - prim_set_primary_archive(PS2, ArchiveFile, ArchiveBin, FileInfo) + prim_set_primary_archive(PS2, ArchiveFile, ArchiveBin, + FileInfo, ParserFun) end, debug(PS3, {return, Res3}), {Res3, PS3}. @@ -873,11 +871,11 @@ prim_get_file(PS, File) -> {Res, PS}; {archive, ArchiveFile, FileInArchive} -> debug(PS, {archive_get_file, ArchiveFile, FileInArchive}), - FunnyFile = funny_split(FileInArchive, $/), + FileComponents = path_split(FileInArchive), Fun = - fun({Funny, _GetInfo, GetBin}, Acc) -> + fun({Components, _GetInfo, GetBin}, Acc) -> if - Funny =:= FunnyFile -> + Components =:= FileComponents -> {false, {ok, GetBin()}}; true -> {true, Acc} @@ -900,11 +898,11 @@ prim_list_dir(PS, Dir) -> {Res, PS}; {archive, ArchiveFile, FileInArchive} -> debug(PS, {archive_list_dir, ArchiveFile, FileInArchive}), - FunnyDir = funny_split(FileInArchive, $/), + DirComponents = path_split(FileInArchive), Fun = - fun({Funny, _GetInfo, _GetBin}, {Status, Names} = Acc) -> - case Funny of - [RevName | FD] when FD =:= FunnyDir -> + fun({Components, _GetInfo, _GetBin}, {Status, Names} = Acc) -> + case Components of + [RevName | DC] when DC =:= DirComponents -> case RevName of "" -> %% The listed directory @@ -914,16 +912,16 @@ prim_list_dir(PS, Dir) -> Name = reverse(RevName), {true, {Status, [Name | Names]}} end; - ["", RevName | FD] when FD =:= FunnyDir -> + ["", RevName | DC] when DC =:= DirComponents -> %% Directory Name = reverse(RevName), {true, {Status, [Name | Names]}}; - [RevName] when FunnyDir =:= [""] -> - %% Top file + [RevName] when DirComponents =:= [""] -> + %% File in top directory Name = reverse(RevName), {true, {ok, [Name | Names]}}; - ["", RevName] when FunnyDir =:= [""] -> - %% Top file + ["", RevName] when DirComponents =:= [""] -> + %% Directory in top directory Name = reverse(RevName), {true, {ok, [Name | Names]}}; _ -> @@ -962,15 +960,14 @@ prim_read_file_info(PS, File) -> end; {archive, ArchiveFile, FileInArchive} -> debug(PS, {archive_read_file_info, File}), - FunnyFile = funny_split(FileInArchive, $/), + FileComponents = path_split(FileInArchive), Fun = - fun({Funny, GetInfo, _GetBin}, Acc) -> - case Funny of - [H | T] when H =:= "", - T =:= FunnyFile -> + fun({Components, GetInfo, _GetBin}, Acc) -> + case Components of + ["" | F] when F =:= FileComponents -> %% Directory {false, {ok, GetInfo()}}; - F when F =:= FunnyFile -> + F when F =:= FileComponents -> %% Plain file {false, {ok, GetInfo()}}; _ -> @@ -1011,7 +1008,7 @@ apply_archive(PS, Fun, Acc, Archive) -> %% put(Archive, {Error, FI}), {Error, PS} end; - {primary, PrimZip, FI} -> + {primary, PrimZip, FI, ParserFun} -> case prim_file:read_file_info(Archive) of {ok, FI2} when FI#file_info.mtime =:= FI2#file_info.mtime -> @@ -1022,6 +1019,16 @@ apply_archive(PS, Fun, Acc, Archive) -> debug(PS, {primary, Error}), {Error, PS} end; + {ok, FI2} -> + ok = clear_cache(Archive, {ok, PrimZip}), + case load_prim_archive(Archive, FI2, ParserFun) of + {ok, PrimZip2, FI3, _Ebins} -> + debug(PS, {cache, {update, Archive}}), + put(Archive, {primary, PrimZip2, FI3, ParserFun}); + Error2 -> + debug(PS, {cache, {clear, Error2}}) + end, + apply_archive(PS, Fun, Acc, Archive); Error -> debug(PS, {cache, {clear, Error}}), clear_cache(Archive, {ok, PrimZip}), @@ -1063,50 +1070,69 @@ open_archive(Archive, Acc, Fun) -> {error, Reason} end. +%% Open the given archive and iterate through all files with an own +%% wrapper fun in order to identify each file as a component list as +%% returned from path_split/1. +%% +%% In the archive (zip) file, directory elements might or might not be +%% present. To ensure consistency, a directory element is added if it +%% does not already exist (ensure_virtual_dirs/6). NOTE that there will +%% be no such directory element for the top directory of the archive. open_archive(Archive, FileInfo, Acc, Fun) -> FakeFI = FileInfo#file_info{type = directory}, Wrapper = - fun({N, GI, GB}, {A, I, FunnyDirs}) -> % Full iteration at open - Funny = funny_split(N, $/), - FunnyDirs2 = - case Funny of - ["" | FunnyDir] -> - [FunnyDir | FunnyDirs]; + fun({N, GI, GB}, {A, I, Dirs}) -> + Components = path_split(N), + Dirs2 = + case Components of + ["" | Dir] -> + %% This is a directory + [Dir | Dirs]; _ -> - FunnyDirs + %% This is a regular file + Dirs end, - {Includes, FunnyDirs3, A2} = - ensure_virtual_dirs(Funny, Fun, FakeFI, [{true, Funny}], FunnyDirs2, A), - {_Continue, A3} = Fun({Funny, GI, GB}, A2), - {true, Includes, {A3, I, FunnyDirs3}} + {Includes, Dirs3, A2} = + ensure_virtual_dirs(Components, Fun, FakeFI, + [{true, Components}], Dirs2, A), + {_Continue, A3} = Fun({Components, GI, GB}, A2), + {true, Includes, {A3, I, Dirs3}} end, prim_zip:open(Wrapper, {Acc, FakeFI, []}, Archive). -ensure_virtual_dirs(Funny, Fun, FakeFI, Includes, FunnyDirs, Acc) -> - case Funny of - [_ | FunnyDir] -> - case lists:member(FunnyDir, FunnyDirs) of % BIF +ensure_virtual_dirs(Components, Fun, FakeFI, Includes, Dirs, Acc) -> + case Components of + [_] -> + %% Don't add virtual dir for top directory + {Includes, Dirs, Acc}; + [_ | Dir] -> + case lists:member(Dir, Dirs) of % BIF false -> + %% The directory does not yet exist - add it GetInfo = fun() -> FakeFI end, GetBin = fun() -> <<>> end, - VirtualDir = ["" | FunnyDir], + VirtualDir = ["" | Dir], Includes2 = [{true, VirtualDir, GetInfo, GetBin} | Includes], - FunnyDirs2 = [FunnyDir | FunnyDirs], - {I, F, Acc2} = ensure_virtual_dirs(FunnyDir, Fun, FakeFI, Includes2, FunnyDirs2, Acc), + Dirs2 = [Dir | Dirs], + + %% Recursively ensure dir elements on all levels + {I, F, Acc2} = ensure_virtual_dirs(Dir, Fun, FakeFI, + Includes2, Dirs2, Acc), + {_Continue, Acc3} = Fun({VirtualDir, GetInfo, GetBin}, Acc2), {I, F, Acc3}; true -> - {reverse(Includes), FunnyDirs, Acc} - end; - [] -> - {reverse(Includes), FunnyDirs, Acc} + %% The directory element does already exist + %% Recursivly ensure dir elements on all levels + ensure_virtual_dirs(Dir,Fun,FakeFI,Includes,Dirs,Acc) + end end. foldl_archive(PrimZip, Acc, Fun) -> Wrapper = - fun({Funny, GI, GB}, A) -> + fun({Components, GI, GB}, A) -> %% Allow partial iteration at foldl - {Continue, A2} = Fun({Funny, GI, GB}, A), + {Continue, A2} = Fun({Components, GI, GB}, A), {Continue, true, A2} end, prim_zip:foldl(Wrapper, Acc, PrimZip). @@ -1202,17 +1228,32 @@ reverse([A, B]) -> reverse([A, B | L]) -> lists:reverse(L, [B, A]). % BIF -%% Returns all lists in reverse order -funny_split(List, Sep) -> - funny_split(List, Sep, [], []). - -funny_split([Sep | Tail], Sep, Path, Paths) -> - funny_split(Tail, Sep, [], [Path | Paths]); -funny_split([Head | Tail], Sep, Path, Paths) -> - funny_split(Tail, Sep, [Head | Path], Paths); -funny_split([], _Sep, Path, Paths) -> +%% Returns a reversed list of path components, each component itself a +%% reversed list (string), e.g. +%% /path/to/file -> ["elif","ot","htap",""] +%% /path/to/dir/ -> ["","rid","ot","htap",""] +%% Note the "" marking leading and trailing / (slash). +path_split(List) -> + path_split(List, [], []). + +path_split([$/ | Tail], Path, Paths) -> + path_split(Tail, [], [Path | Paths]); +path_split([Head | Tail], Path, Paths) -> + path_split(Tail, [Head | Path], Paths); +path_split([], Path, Paths) -> [Path | Paths]. +%% The opposite of path_split/1 +path_join(Paths) -> + path_join(Paths,[]). + +path_join([""],Acc) -> + Acc; +path_join([Path],Acc) -> + reverse(Path) ++ Acc; +path_join([Path|Paths],Acc) -> + path_join(Paths,"/" ++ reverse(Path) ++ Acc). + name_split(ArchiveFile, File0) -> File = absname(File0), do_name_split(ArchiveFile, File). @@ -1235,26 +1276,22 @@ do_name_split(undefined, File) -> %% False match. Assume plain file {file, File} end; -do_name_split(ArchiveFile0, File) -> +do_name_split(ArchiveFile, File) -> %% Look first in primary archive - ArchiveFile = absname(ArchiveFile0), - case string_match(File, ArchiveFile, []) of + case string_match(real_path(File), ArchiveFile, []) of no_match -> %% Archive or plain file do_name_split(undefined, File); {match, _RevPrimArchiveFile, FileInArchive} -> %% Primary archive - case FileInArchive of - [$/ | FileInArchive2] -> - {archive, ArchiveFile, FileInArchive2}; - _ -> - {archive, ArchiveFile, FileInArchive} - end + {archive, ArchiveFile, FileInArchive} end. string_match([Char | File], [Char | Archive], RevTop) -> string_match(File, Archive, [Char | RevTop]); -string_match(File, [], RevTop) -> +string_match([] = File, [], RevTop) -> + {match, RevTop, File}; +string_match([$/ | File], [], RevTop) -> {match, RevTop, File}; string_match(_File, _Archive, _RevTop) -> no_match. @@ -1314,14 +1351,14 @@ absname(Name) -> case prim_file:get_cwd() of {ok, Cwd} -> Cwd ++ "/" ++ Name2; - {error, _} -> + {error, _} -> Name2 end; volumerelative -> case prim_file:get_cwd() of {ok, Cwd} -> absname_vr(Name2, Cwd); - {error, _} -> + {error, _} -> Name2 end end. @@ -1380,22 +1417,12 @@ win32_pathtype(Name) -> win32_pathtype([Char | List++Rest]); [$/, $/|_] -> absolute; - [$\\, $/|_] -> - absolute; - [$/, $\\|_] -> - absolute; - [$\\, $\\|_] -> - absolute; [$/|_] -> volumerelative; - [$\\|_] -> - volumerelative; [C1, C2, List | Rest] when is_list(List) -> - pathtype([C1, C2|List ++ Rest]); + win32_pathtype([C1, C2|List ++ Rest]); [_Letter, $:, $/|_] -> absolute; - [_Letter, $:, $\\|_] -> - absolute; [_Letter, $:|_] -> volumerelative; _ -> @@ -1408,8 +1435,6 @@ vxworks_first(Name) -> {not_device, [], []}; [$/ | T] -> vxworks_first2(device, T, [$/]); - [$\\ | T] -> - vxworks_first2(device, T, [$/]); [H | T] when is_list(H) -> vxworks_first(H ++ T); [H | T] -> @@ -1422,8 +1447,6 @@ vxworks_first2(Devicep, Name, FirstComp) -> {Devicep, [], FirstComp}; [$/ |T ] -> {Devicep, [$/ | T], FirstComp}; - [$\\ | T] -> - {Devicep, [$/ | T], FirstComp}; [$: | T]-> {device, T, [$: | FirstComp]}; [H | T] when is_list(H) -> @@ -1445,3 +1468,70 @@ normalize(Name, Acc) -> [] -> reverse(Acc) end. + +%% Remove .. and . from the path, e.g. +%% /path/./to/this/../file -> /path/to/file +%% This includes resolving symlinks. +%% +%% This is done to ensure that paths are totally normalized before +%% comparing to find out if a file is inside the primary archive or +%% not. +real_path(Name) -> + real_path(Name,reverse(path_split(Name)),[],[]). + +real_path(_Name,[],Acc,_Links) -> + path_join(Acc); +real_path(Name,["."|Paths],Acc,Links) -> + real_path(Name,Paths,Acc,Links); +real_path(Name,[".."|Paths],[""]=Acc,Links) -> + %% /.. -> / (can't get higher than root) + real_path(Name,Paths,Acc,Links); +real_path(Name,[".."|Paths],[Prev|Acc],Links) when Prev=/=".." -> + real_path(Name,Paths,Acc,Links); +real_path(Name,[Path|Paths],Acc,Links) -> + This = [Path|Acc], + ThisFile = path_join(This), + case lists:member(ThisFile,Links) of + true -> % circular!! + Name; + false -> + case prim_file:read_link(ThisFile) of + {ok,Link} -> + case reverse(path_split(Link)) of + [""|_] = LinkPaths -> + real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]); + LinkPaths -> + real_path(Name,LinkPaths++Paths,Acc,[ThisFile|Links]) + end; + _ -> + real_path(Name,Paths,This,Links) + end + end. + +load_prim_archive(ArchiveFile, ArchiveBin, #file_info{}=FileInfo) -> + Fun = fun({Components, _GI, _GB}, A) -> + case Components of + ["", "nibe", RevApp] -> % Reverse ebin + %% Collect ebin directories in archive + Ebin = lists:reverse(RevApp, "/ebin"), + {true, [Ebin | A]}; + _ -> + {true, A} + end + end, + Ebins0 = [ArchiveFile], + case open_archive({ArchiveFile, ArchiveBin}, FileInfo, + Ebins0, Fun) of + {ok, PrimZip, {RevEbins, FI, _}} -> + Ebins = reverse(RevEbins), + {ok, PrimZip, FI, Ebins}; + Error -> + Error + end; +load_prim_archive(ArchiveFile, FileInfo, ParserFun) -> + case ParserFun(ArchiveFile) of + {ok, ArchiveBin} -> + load_prim_archive(ArchiveFile, ArchiveBin, FileInfo); + Error -> + Error + end. diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index c9c434dea0..185b62a899 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -635,8 +635,16 @@ do_unload([M|Mods]) -> catch erlang:purge_module(M), do_unload(Mods); do_unload([]) -> + purge_all_hipe_refs(), ok. +purge_all_hipe_refs() -> + case erlang:system_info(hipe_architecture) of + undefined -> ok; + _ -> hipe_bifs:remove_refs_from(all) + end. + + sub([H|T],L) -> sub(T,del(H,L)); sub([],L) -> L. @@ -1252,7 +1260,11 @@ get_arguments([]) -> []. to_strings([H|T]) when is_atom(H) -> [atom_to_list(H)|to_strings(T)]; -to_strings([H|T]) when is_binary(H) -> [binary_to_list(H)|to_strings(T)]; +to_strings([H|T]) when is_binary(H) -> [try + unicode:characters_to_list(H,file:native_name_encoding()) + catch + _:_ -> binary_to_list(H) + end|to_strings(T)]; to_strings([]) -> []. get_argument(Arg,Flags) -> diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 0fc56c8c8e..ec158ba970 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -453,7 +453,7 @@ position(#file_descriptor{module = ?MODULE, data = {Port, _}}, At) -> {error, Reason} end. -%% Returns {error, Reaseon} | ok. +%% Returns {error, Reason} | ok. truncate(#file_descriptor{module = ?MODULE, data = {Port, _}}) -> drv_command(Port, <<?FILE_TRUNCATE>>). diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 846ae97ed2..91fcd3ac82 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -184,7 +184,7 @@ close_pend_loop(S, N) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% bind(S,IP,Port) when is_port(S), is_integer(Port), Port >= 0, Port =< 65535 -> - case ctl_cmd(S,?INET_REQ_BIND,[?int16(Port),ip_to_bytes(IP)]) of + case ctl_cmd(S,?INET_REQ_BIND,enc_value(set, addr, {IP,Port})) of {ok, [P1,P0]} -> {ok, ?u16(P1, P0)}; {error,_}=Error -> Error end; @@ -206,10 +206,10 @@ bindx(S, AddFlag, Addrs) -> case getprotocol(S) of sctp -> %% Really multi-homed "bindx". Stringified args: - %% [AddFlag, (Port, IP)+]: + %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]: Args = [?int8(AddFlag)| - [[?int16(Port)|ip_to_bytes(IP)] || + [enc_value(set, addr, {IP,Port}) || {IP, Port} <- Addrs]], case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of {ok,_} -> {ok, S}; diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile index d8e39062e3..55584bb057 100644 --- a/erts/start_scripts/Makefile +++ b/erts/start_scripts/Makefile @@ -163,14 +163,14 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: script - $(INSTALL_DIR) $(RELEASE_PATH)/releases/$(SYSTEM_VSN) + $(INSTALL_DIR) "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)" ifneq ($(findstring win32,$(TARGET)),win32) - $(INSTALL_DATA) RELEASES.src $(RELEASE_PATH)/releases + $(INSTALL_DATA) RELEASES.src "$(RELEASE_PATH)/releases" endif $(INSTALL_DATA) $(INSTALL_SCRIPTS) $(REL_SCRIPTS) \ - $(RELEASE_PATH)/releases/$(SYSTEM_VSN) - $(INSTALL_DATA) start_clean.script $(RELEASE_PATH)/releases/$(SYSTEM_VSN)/start.script - $(INSTALL_DATA) start_clean.boot $(RELEASE_PATH)/releases/$(SYSTEM_VSN)/start.boot + "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)" + $(INSTALL_DATA) start_clean.script "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)/start.script" + $(INSTALL_DATA) start_clean.boot "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)/start.boot" release_docs_spec: diff --git a/erts/test/Makefile b/erts/test/Makefile index 68be3f2178..79be5497b0 100644 --- a/erts/test/Makefile +++ b/erts/test/Makefile @@ -86,10 +86,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) system.spec system.dynspec system.spec.vxworks \ - $(ERL_FILES) $(TARGET_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - tar cf - *_SUITE_data utils | (cd $(RELSYSDIR); tar xf -) + $(ERL_FILES) $(TARGET_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + tar cf - *_SUITE_data utils | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/erts/test/erl_print_SUITE_data/Makefile.src b/erts/test/erl_print_SUITE_data/Makefile.src index dec5650416..96d71c7a98 100644 --- a/erts/test/erl_print_SUITE_data/Makefile.src +++ b/erts/test/erl_print_SUITE_data/Makefile.src @@ -20,23 +20,35 @@ include @erts_lib_include_internal_generated@@[email protected] CC = @CC@ -CFLAGS = @ERTS_CFLAGS@ -LIBS = @ERTS_LIBS@ +CFLAGST = @ERTS_CFLAGS@ +LIBST = @ERTS_LIBS@ +CFLAGSF = @CFLAGS@ +LIBSF = @LIBS@ +CP=cp +CHMOD=chmod -EPTF_CFLAGS = -Wall $(CFLAGS) @DEFS@ -I@erts_lib_include_internal@ -I@erts_lib_include_internal_generated@ -EPTF_LIBS = $(LIBS) -L@erts_lib_internal_path@ -lerts_internal@type_marker@ +COMMON_CFLAGS = -Wall @DEFS@ -I@erts_lib_include_internal@ -I@erts_lib_include_internal_generated@ -EPTT_CFLAGS = -DTHREAD_SAFE $(ETHR_DEFS) $(EPTF_CFLAGS) -EPTT_LIBS = -L@erts_lib_internal_path@ -lerts_internal_r@type_marker@ $(ETHR_LIBS) $(LIBS) +EPTF_CFLAGS = $(CFLAGSF) $(COMMON_CFLAGS) +EPTF_LIBS = -L@erts_lib_internal_path@ -lerts_internal@type_marker@ $(LIBSF) + +EPTT_CFLAGS = -DTHREAD_SAFE $(ETHR_DEFS) $(CFLAGST) $(COMMON_CFLAGS) +EPTT_LIBS = -L@erts_lib_internal_path@ -lerts_internal_r@type_marker@ $(ETHR_LIBS) $(LIBST) GCC = .@DS@gccifier -CC"$(CC)" -PROGS = erl_print_tests.@emu_threads@@exe@ +PROGS = erl_print_tests.true@exe@ erl_print_tests.false@exe@ all: $(PROGS) +@IFEQ@ (@cross@, yes) +gccifier@exe@: + $(CP) ..@DS@utils@[email protected] gccifier@exe@ + $(CHMOD) a+x gccifier@exe@ +@ELSE@ gccifier@exe@: ..@DS@utils@[email protected] - $(CC) $(CFLAGS) -o gccifier@exe@ ..@DS@utils@[email protected] $(LIBS) + $(CC) $(CFLAGST) -o gccifier@exe@ ..@DS@utils@[email protected] $(LIBST) +@ENDIF@ erl_print_tests.false@exe@: gccifier@exe@ erl_print_tests.c $(GCC) $(EPTF_CFLAGS) -o erl_print_tests.false@exe@ erl_print_tests.c $(EPTF_LIBS) diff --git a/erts/test/erl_print_SUITE_data/erl_print_tests.c b/erts/test/erl_print_SUITE_data/erl_print_tests.c index 28ce78f4e1..82b0c21132 100644 --- a/erts/test/erl_print_SUITE_data/erl_print_tests.c +++ b/erts/test/erl_print_SUITE_data/erl_print_tests.c @@ -44,8 +44,8 @@ #endif #ifdef __WIN32__ -#define signed_long_long LONGLONG -#define unsigned_long_long ULONGLONG +#define signed_long_long __int64 +#define unsigned_long_long unsigned __int64 #else #define signed_long_long signed long long #define unsigned_long_long unsigned long long diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index 6c0d662126..ada09db975 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -253,7 +253,7 @@ erlc() -> false -> test_server:fail("Can't find erlc"); Erlc -> - Erlc + "\"" ++ Erlc ++ "\"" end. %% Runs a command. diff --git a/erts/test/erlexec_SUITE_data/Makefile.src b/erts/test/erlexec_SUITE_data/Makefile.src index b751547b8f..970a905c32 100644 --- a/erts/test/erlexec_SUITE_data/Makefile.src +++ b/erts/test/erlexec_SUITE_data/Makefile.src @@ -20,6 +20,8 @@ CC = @CC@ CFLAGS = @ERTS_CFLAGS@ LIBS = @ERTS_LIBS@ +CP=cp +CHMOD=chmod ERLX_T_CFLAGS = -Wall $(ERLX_DEFS) $(CFLAGS) @DEFS@ @@ -29,8 +31,14 @@ PROGS = erlexec_tests@exe@ all: $(PROGS) +@IFEQ@ (@cross@, yes) +gccifier@exe@: + $(CP) ..@DS@utils@[email protected] gccifier@exe@ + $(CHMOD) a+x gccifier@exe@ +@ELSE@ gccifier@exe@: ..@DS@utils@[email protected] $(CC) $(CFLAGS) -o gccifier@exe@ ..@DS@utils@[email protected] $(LIBS) +@ENDIF@ erlexec_tests@exe@: gccifier@exe@ erlexec_tests.c $(GCC) $(ERLX_T_CFLAGS) -o erlexec_tests@exe@ erlexec_tests.c diff --git a/erts/test/ethread_SUITE_data/Makefile.src b/erts/test/ethread_SUITE_data/Makefile.src index 132b23344c..f93a31178b 100644 --- a/erts/test/ethread_SUITE_data/Makefile.src +++ b/erts/test/ethread_SUITE_data/Makefile.src @@ -23,6 +23,8 @@ include @erts_lib_include_internal_generated@@DS@erts_internal.mk CC = @CC@ CFLAGS = @ERTS_CFLAGS@ LIBS = @ERTS_LIBS@ +CP=cp +CHMOD=chmod ETHR_T_CFLAGS = -Wall $(ETHR_DEFS) $(CFLAGS) @DEFS@ -I@erts_lib_include_internal@ -I@erts_lib_include_internal_generated@ ETHR_T_LIBS = $(LIBS) -L@erts_lib_internal_path@ $(ETHR_LIBS) $(ERTS_INTERNAL_X_LIBS) @@ -33,8 +35,14 @@ PROGS = ethread_tests@exe@ all: $(PROGS) +@IFEQ@ (@cross@, yes) +gccifier@exe@: + $(CP) ..@DS@utils@[email protected] gccifier@exe@ + $(CHMOD) a+x gccifier@exe@ +@ELSE@ gccifier@exe@: ..@DS@utils@[email protected] $(CC) $(CFLAGS) -o gccifier@exe@ ..@DS@utils@[email protected] $(LIBS) +@ENDIF@ ethread_tests@exe@: gccifier@exe@ ethread_tests.c $(GCC) $(ETHR_T_CFLAGS) -o ethread_tests@exe@ ethread_tests.c -lerts_internal_r $(ETHR_T_LIBS) diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl index 214031a6fe..559d95007c 100644 --- a/erts/test/install_SUITE.erl +++ b/erts/test/install_SUITE.erl @@ -452,7 +452,7 @@ bin_dirname_fail(Config) when is_list(Config) -> ?line Be = Bs, ?line EBs = "/opt/lib/erlang/otp/bin", ?line EBe = EBs, - ?line CMDPRFX = "PATH="++?config(data_dir,Config)++":"++os:getenv("PATH"), + ?line CMDPRFX = "PATH=\""++?config(data_dir,Config)++":"++os:getenv("PATH")++"\"", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, @@ -485,7 +485,7 @@ bin_no_use_dirname_fail(Config) when is_list(Config) -> ?line EBs = "/opt/lib/erlang/otp/bin", ?line EBe = EBs, ?line RP = "../lib/erlang/otp/bin", - ?line CMDPRFX = "PATH="++?config(data_dir,Config)++":"++os:getenv("PATH"), + ?line CMDPRFX = "PATH=\""++?config(data_dir,Config)++":"++os:getenv("PATH")++"\"", ChkRes = fun (Res, #inst{test_prefix = TP, destdir = D, extra_prefix = EP, diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl index f146bf1d69..332733e075 100644 --- a/erts/test/otp_SUITE.erl +++ b/erts/test/otp_SUITE.erl @@ -88,8 +88,9 @@ undefined_functions(Config) when is_list(Config) -> ?line Undef1 = hipe_filter(Undef0), ?line Undef2 = ssl_crypto_filter(Undef1), ?line Undef3 = edoc_filter(Undef2), - ?line Undef = eunit_filter(Undef3), - ?line Undef = megaco_filter(Undef), + Undef4 = eunit_filter(Undef3), + Undef5 = dialyzer_filter(Undef4), + Undef = wx_filter(Undef5), case Undef of [] -> ok; @@ -97,9 +98,11 @@ undefined_functions(Config) when is_list(Config) -> Fd = open_log(Config, "undefined_functions"), foreach(fun ({MFA1,MFA2}) -> io:format("~s calls undefined ~s", - [format_mfa(MFA1),format_mfa(MFA2)]), + [format_mfa(Server, MFA1), + format_mfa(MFA2)]), io:format(Fd, "~s ~s\n", - [format_mfa(MFA1),format_mfa(MFA2)]) + [format_mfa(Server, MFA1), + format_mfa(MFA2)]) end, Undef), close_log(Fd), ?line ?t:fail({length(Undef),undefined_functions_in_otp}) @@ -171,34 +174,33 @@ eunit_filter(Undef) -> (_) -> true end, Undef). -megaco_filter(Undef) -> - %% Intentional calls to undefined functions. - filter(fun({{megaco_compact_text_encoder,encode_action_reply,3}, - {megaco_compact_text_encoder_v3,encode_action_reply,2}}) -> false; - ({{megaco_compact_text_encoder,encode_action_request,3}, - {megaco_compact_text_encoder_v3,encode_action_request,2}}) -> false; - ({{megaco_compact_text_encoder,encode_action_requests,3}, - {megaco_compact_text_encoder_v3,encode_action_requests,2}}) -> false; - ({{megaco_compact_text_encoder,encode_command_request,3}, - {megaco_compact_text_encoder_v3,encode_command_request,2}}) -> false; - ({{megaco_compact_text_encoder,encode_message,3}, - {megaco_compact_text_encoder_v3,encode_message,2}}) -> false; - ({{megaco_compact_text_encoder,encode_transaction,3}, - {megaco_compact_text_encoder_v3,encode_transaction,2}}) -> false; - ({{megaco_pretty_text_encoder,encode_action_reply,3}, - {megaco_pretty_text_encoder_v3,encode_action_reply,2}}) -> false; - ({{megaco_pretty_text_encoder,encode_action_request,3}, - {megaco_pretty_text_encoder_v3,encode_action_request,2}}) -> false; - ({{megaco_pretty_text_encoder,encode_action_requests,3}, - {megaco_pretty_text_encoder_v3,encode_action_requests,2}}) -> false; - ({{megaco_pretty_text_encoder,encode_command_request,3}, - {megaco_pretty_text_encoder_v3,encode_command_request,2}}) -> false; - ({{megaco_pretty_text_encoder,encode_message,3}, - {megaco_pretty_text_encoder_v3,encode_message,2}}) -> false; - ({{megaco_pretty_text_encoder,encode_transaction,3}, - {megaco_pretty_text_encoder_v3,encode_transaction,2}}) -> false; - (_) -> true - end, Undef). +dialyzer_filter(Undef) -> + case code:lib_dir(dialyzer) of + {error,bad_name} -> + filter(fun({_,{dialyzer_callgraph,_,_}}) -> false; + ({_,{dialyzer_codeserver,_,_}}) -> false; + ({_,{dialyzer_contracts,_,_}}) -> false; + ({_,{dialyzer_cl_parse,_,_}}) -> false; + ({_,{dialyzer_plt,_,_}}) -> false; + ({_,{dialyzer_succ_typings,_,_}}) -> false; + ({_,{dialyzer_utils,_,_}}) -> false; + (_) -> true + end, Undef); + _ -> Undef + end. + +wx_filter(Undef) -> + case code:lib_dir(wx) of + {error,bad_name} -> + filter(fun({_,{MaybeWxModule,_,_}}) -> + case atom_to_list(MaybeWxModule) of + "wx"++_ -> false; + _ -> true + end + end, Undef); + _ -> Undef + end. + deprecated_not_in_obsolete(Config) when is_list(Config) -> ?line Server = ?config(xref_server, Config), @@ -215,9 +217,9 @@ deprecated_not_in_obsolete(Config) when is_list(Config) -> _ -> io:put_chars("The following functions have -deprecated() attributes,\n" "but are not listed in otp_internal:obsolete/3.\n"), - ?line print_mfas(group_leader(), L), + print_mfas(group_leader(), Server, L), Fd = open_log(Config, "deprecated_not_obsolete"), - print_mfas(Fd, L), + print_mfas(Fd, Server, L), close_log(Fd), ?line ?t:fail({length(L),deprecated_but_not_obsolete}) end. @@ -239,9 +241,9 @@ obsolete_but_not_deprecated(Config) when is_list(Config) -> io:put_chars("The following functions are listed " "in otp_internal:obsolete/3,\n" "but don't have -deprecated() attributes.\n"), - ?line print_mfas(group_leader(), L), + print_mfas(group_leader(), Server, L), Fd = open_log(Config, "obsolete_not_deprecated"), - print_mfas(Fd, L), + print_mfas(Fd, Server, L), close_log(Fd), ?line ?t:fail({length(L),obsolete_but_not_deprecated}) end. @@ -310,15 +312,22 @@ strong_components(Config) when is_list(Config) -> %%% Common help functions. %%% - -print_mfas(Fd, [MFA|T]) -> - io:format(Fd, "~s\n", [format_mfa(MFA)]), - print_mfas(Fd, T); -print_mfas(_, []) -> ok. +print_mfas(Fd, Server, MFAs) -> + [io:format(Fd, "~s\n", [format_mfa(Server, MFA)]) || MFA <- MFAs], + ok. format_mfa({M,F,A}) -> lists:flatten(io_lib:format("~s:~s/~p", [M,F,A])). +format_mfa(Server, MFA) -> + MFAString = format_mfa(MFA), + AQ = "(App)" ++ MFAString, + AppPrefix = case xref:q(Server, AQ) of + {ok,[App]} -> "[" ++ atom_to_list(App) ++ "]"; + _ -> "" + end, + AppPrefix ++ MFAString. + open_log(Config, Name) -> PrivDir = ?config(priv_dir, Config), RunDir = filename:dirname(filename:dirname(PrivDir)), diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl index 6350dc47dd..6838c8924d 100644 --- a/erts/test/run_erl_SUITE.erl +++ b/erts/test/run_erl_SUITE.erl @@ -250,7 +250,7 @@ defunct_2(Config, Perl) -> ?line ok = file:make_dir(LogDir), ?line Pipe = LogDir ++ "/", ?line RunErl = os:find_executable(run_erl), - ?line Cmd = Perl ++ " " ++ RunErlTest ++ " " ++ RunErl ++ " " ++ + ?line Cmd = Perl ++ " " ++ RunErlTest ++ " \"" ++ RunErl ++ "\" " ++ Defuncter ++ " " ++ Pipe ++ " " ++ LogDir, ?line io:format("~p", [Cmd]), ?line Res = os:cmd(Cmd), diff --git a/erts/test/utils/gccifier.c b/erts/test/utils/gccifier.c index 64de764260..a1019f9a72 100644 --- a/erts/test/utils/gccifier.c +++ b/erts/test/utils/gccifier.c @@ -73,17 +73,23 @@ save_arg(args_t *args, char *arg1, ...) args->vec = (char **) (args->no ? realloc((void *) args->vec, (sizeof(char *) - *(args->no + ARGS_INCR + 1))) + *(args->no + ARGS_INCR + 2))) : malloc((sizeof(char *) - *(args->no + ARGS_INCR + 1)))); + *(args->no + ARGS_INCR + 2)))); if (!args->vec) enomem(); args->no += ARGS_INCR; } + if (carg == arg1) { + args->vec[args->ix++] = "\""; + args->chars++; + } args->vec[args->ix++] = carg; args->chars += strlen(carg); carg = va_arg(argp, char *); } + args->vec[args->ix++] = "\""; + args->chars++; args->vec[args->ix++] = " "; args->chars++; va_end(argp); @@ -231,6 +237,9 @@ main(int argc, char *argv[]) CHECK_FIRST_LINK_ARG; save_arg(&link_args, "-libpath:", arg, NULL); } + else if (strcmp("-link",arg) == 0) { + CHECK_FIRST_LINK_ARG; + } #endif /* #ifdef __WIN32__ */ else if (is_prefix("-l", &arg)) { CHECK_FIRST_LINK_ARG; diff --git a/erts/test/utils/gccifier.sh b/erts/test/utils/gccifier.sh new file mode 100755 index 0000000000..4c6ba35db2 --- /dev/null +++ b/erts/test/utils/gccifier.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2005-2009. 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% +# + +CC=`echo "$1" | sed -e "s/-CC//"` +shift +echo "->" +echo "$CC $*" +$CC $* +echo "" diff --git a/erts/vsn.mk b/erts/vsn.mk index bbf77b1a68..da0f03b24f 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,8 +17,8 @@ # %CopyrightEnd% # -VSN = 5.9.1 -SYSTEM_VSN = R15B01 +VSN = 5.9.2 +SYSTEM_VSN = R15B02 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/.gitignore b/lib/.gitignore index 56b1ed2b84..4125111ebd 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -7,6 +7,7 @@ /common_test/doc/src/ct_rpc.xml /common_test/doc/src/ct_snmp.xml /common_test/doc/src/ct_ssh.xml +/common_test/doc/src/ct_netconfc.xml /common_test/doc/src/ct_telnet.xml /common_test/doc/src/unix_telnet.xml diff --git a/lib/appmon/doc/src/Makefile b/lib/appmon/doc/src/Makefile index 743f123c06..2bf560ee6b 100644 --- a/lib/appmon/doc/src/Makefile +++ b/lib/appmon/doc/src/Makefile @@ -107,14 +107,14 @@ debug opt: 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/appmon/priv/Makefile b/lib/appmon/priv/Makefile index 9af4fbd228..8920923c98 100644 --- a/lib/appmon/priv/Makefile +++ b/lib/appmon/priv/Makefile @@ -57,8 +57,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(HELP_FILES) $(TOOLBAR_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(HELP_FILES) $(TOOLBAR_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/appmon/src/Makefile b/lib/appmon/src/Makefile index 43f4f085b8..9ee7c92a4d 100644 --- a/lib/appmon/src/Makefile +++ b/lib/appmon/src/Makefile @@ -92,10 +92,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) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/asn1/c_src/Makefile b/lib/asn1/c_src/Makefile index 8c06be56f8..903cf32838 100644 --- a/lib/asn1/c_src/Makefile +++ b/lib/asn1/c_src/Makefile @@ -110,10 +110,10 @@ $(NIF_SHARED_OBJ_FILE): $(NIF_OBJ_FILES) include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_PROGRAM) $(NIF_SHARED_OBJ_FILE) $(RELSYSDIR)/priv/lib - $(INSTALL_DIR) $(RELSYSDIR)/c_src - $(INSTALL_DATA) *.c $(RELSYSDIR)/c_src + $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" + $(INSTALL_PROGRAM) $(NIF_SHARED_OBJ_FILE) "$(RELSYSDIR)/priv/lib" + $(INSTALL_DIR) "$(RELSYSDIR)/c_src" + $(INSTALL_DATA) *.c "$(RELSYSDIR)/c_src" release_docs_spec: diff --git a/lib/asn1/doc/src/Makefile b/lib/asn1/doc/src/Makefile index 20bd00a3b8..9ef5750353 100644 --- a/lib/asn1/doc/src/Makefile +++ b/lib/asn1/doc/src/Makefile @@ -119,13 +119,13 @@ debug opt: 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/asn1/src/Makefile b/lib/asn1/src/Makefile index 5614cbea91..4bd49aa93b 100644 --- a/lib/asn1/src/Makefile +++ b/lib/asn1/src/Makefile @@ -153,16 +153,16 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(APP_SRC) $(APPUP_SRC) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(EXAMPLES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(APP_SRC) $(APPUP_SRC) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLES) "$(RELSYSDIR)/examples" # there are no include files to be used by the user -#$(INSTALL_DIR) $(RELSYSDIR)/include -#$(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include +#$(INSTALL_DIR) "$(RELSYSDIR)/include" +#$(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 187339fb53..59e82b7a57 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -4177,7 +4177,7 @@ check_constraint(S,{'SizeConstraint',Lb}) -> check_constraint(S,{'SingleValue', L}) when is_list(L) -> F = fun(A) -> resolv_value(S,A) end, - {'SingleValue',lists:map(F,L)}; + {'SingleValue',lists:sort(lists:map(F,L))}; check_constraint(S,{'SingleValue', V}) when is_integer(V) -> Val = resolv_value(S,V), @@ -5991,17 +5991,25 @@ generate_automatic_tags1([H|T],[TagNo|TagNos]) when is_record(H,'ComponentType') type={default,'IMPLICIT'}, form= 0 }]}, % PRIMITIVE [H#'ComponentType'{typespec=NewTs}|generate_automatic_tags1(T,[TagNo+1|TagNos])]; -generate_automatic_tags1([ExtMark|T],[_TagNo|TagNos]) -> % EXTENSIONMARK +generate_automatic_tags1([ExtMark = #'EXTENSIONMARK'{}|T],[_TagNo|TagNos]) -> [ExtMark | generate_automatic_tags1(T,TagNos)]; +generate_automatic_tags1([H|T],TagList) -> % ExtensionAdditionGroup etc are just ignored + [H | generate_automatic_tags1(T,TagList)]; generate_automatic_tags1([],_) -> []. -any_manual_tag([#'ComponentType'{typespec=#type{tag=[]}}|Rest]) -> - any_manual_tag(Rest); -any_manual_tag([#'EXTENSIONMARK'{}|Rest]) -> - any_manual_tag(Rest); -any_manual_tag([_|_Rest]) -> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Returns true if there is at least one ComponentType with a manually +%% specified tag. No manual tag is indicated by typespec=#type{tag=[]} +%% so we check if we find a tag =/= [] and return true in that case +%% all other things in the componentlist like (EXTENSIONMARK, +%% ExtensionAdditionGroup,...) except ComponentType is simply +%% ignored/skipped +any_manual_tag([#'ComponentType'{typespec=#type{tag=Tag}}|_Rest]) + when Tag =/= []-> true; +any_manual_tag([_|Rest]) -> + any_manual_tag(Rest); any_manual_tag([]) -> false. diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index e07680f10b..5f5138ef23 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -100,18 +100,26 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) -> case Ext of {ext,_,NumExt} when NumExt > 0 -> case extgroup_pos_and_length(CompList) of - {extgrouppos,ExtGroupPos,ExtGroupLen} -> - Elements = make_elements(ExtGroupPos+1, - "Val1",lists:seq(1,ExtGroupLen)), - emit([ - {next,val}," = case [X || X <- [",Elements, - "],X =/= asn1_NOVALUE] of",nl, - "[] -> ",{curr,val},";",nl, - "_ -> setelement(",{asis,ExtGroupPos+1},",", - {curr,val},",", - "{extaddgroup,", Elements,"})",nl, - "end,",nl]), - asn1ct_name:new(val); + {extgrouppos,[]} -> % no extenstionAdditionGroup + ok; + {extgrouppos,ExtGroupPosLenList} -> + ExtGroupFun = + fun({ExtActualGroupPos,ExtGroupVirtualPos,ExtGroupLen}) -> + Elements = + make_elements(ExtGroupVirtualPos+1, + "Val1", + lists:seq(1,ExtGroupLen)), + emit([ + {next,val}," = case [X || X <- [",Elements, + "],X =/= asn1_NOVALUE] of",nl, + "[] -> ",{curr,val},";",nl, + "_ -> setelement(",{asis,ExtActualGroupPos+1},",", + {curr,val},",", + "{extaddgroup,", Elements,"})",nl, + "end,",nl]), + asn1ct_name:new(val) + end, + lists:foreach(ExtGroupFun,ExtGroupPosLenList); _ -> % no extensionAdditionGroup ok end, @@ -279,9 +287,9 @@ gen_decode_constructed(Erules,Typename,D) when is_record(D,type) -> {false,false,false} end end, - NewCompList = wrap_compList(CompList), +%% NewCompList = wrap_compList(CompList), {AccTerm,AccBytes} = - gen_dec_components_call(Erules,Typename,NewCompList,MaybeComma2,DecObjInf,Ext,length(Optionals)), + gen_dec_components_call(Erules,Typename,CompList,MaybeComma2,DecObjInf,Ext,length(Optionals)), case asn1ct_name:all(term) of [] -> emit(MaybeComma2); % no components at all _ -> emit({com,nl}) @@ -689,24 +697,28 @@ ext_length([],_,Acc) -> Acc. extgroup_pos_and_length(CompList) when is_list(CompList) -> - noextgroup; + {extgrouppos,[]}; extgroup_pos_and_length({RootList,ExtList}) -> - extgrouppos(ExtList,length(RootList)+1); -extgroup_pos_and_length({Rl1,Ext,_Rl2}) -> - extgrouppos(Ext,length(Rl1)+1). - -extgrouppos([{'ExtensionAdditionGroup',_Num}|T],Pos) -> - extgrouppos(T,Pos,0); -extgrouppos([_|T],Pos) -> - extgrouppos(T,Pos+1); -extgrouppos([],_) -> - noextgroup. - -extgrouppos(['ExtensionAdditionGroupEnd'|_T],Pos,Len) -> - {extgrouppos,Pos,Len}; -extgrouppos([_|T],Pos,Len) -> - extgrouppos(T,Pos,Len+1). - + ActualPos = length(RootList) +1, + %% position to get and deliver data in the record to the user + VirtualPos = ActualPos, + %% position to encode/decode the extaddgroup as an opentype sequence + extgrouppos(ExtList,ActualPos,VirtualPos,[]); +extgroup_pos_and_length({RootList,ExtList,_Rl2}) -> + extgroup_pos_and_length({RootList,ExtList}). + +extgrouppos([{'ExtensionAdditionGroup',_Num}|T],ActualPos,VirtualPos,Acc) -> + extgrouppos(T,ActualPos,VirtualPos,0,Acc); +extgrouppos([_|T],ActualPos,VirtualPos,Acc) -> + extgrouppos(T,ActualPos+1,VirtualPos+1,Acc); +extgrouppos([],_,_,Acc) -> + {extgrouppos,lists:reverse(Acc)}. + +extgrouppos(['ExtensionAdditionGroupEnd'|T],ActualPos,VirtualPos,Len,Acc) -> + extgrouppos(T,ActualPos+1,VirtualPos+Len,[{ActualPos,VirtualPos,Len}|Acc]); +extgrouppos([_|T],ActualPos,VirtualPos,Len,Acc) -> + extgrouppos(T,ActualPos,VirtualPos,Len+1,Acc). + gen_dec_extension_value(_) -> @@ -817,19 +829,21 @@ add_textual_order1(Cs,NumIn) -> end, NumIn,Cs). -gen_enc_components_call(Erule,TopType,{Root1,ExtList,Root2},MaybeComma,DynamicEnc,Ext) -> - gen_enc_components_call(Erule,TopType,{Root1++Root2,ExtList},MaybeComma,DynamicEnc,Ext); -gen_enc_components_call(Erule,TopType,{CompList,ExtList},MaybeComma,DynamicEnc,Ext) -> +gen_enc_components_call(Erule,TopType,{Root,ExtList},MaybeComma,DynamicEnc,Ext) -> + gen_enc_components_call(Erule,TopType,{Root,ExtList,[]},MaybeComma,DynamicEnc,Ext); +gen_enc_components_call(Erule,TopType,CL={Root,ExtList,Root2},MaybeComma,DynamicEnc,Ext) -> %% The type has extensionmarker - Rpos = gen_enc_components_call1(Erule,TopType,CompList,1,MaybeComma,DynamicEnc,noext), + Rpos = gen_enc_components_call1(Erule,TopType,Root++Root2,1,MaybeComma,DynamicEnc,noext), case Ext of {ext,_,ExtNum} when ExtNum > 0 -> emit([nl, ",Extensions",nl]); + _ -> true end, %handle extensions - NewExtList = wrap_extensionAdditionGroups(ExtList), + {extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL), + NewExtList = wrap_extensionAdditionGroups(ExtList,ExtGroupPosLen), gen_enc_components_call1(Erule,TopType,NewExtList,Rpos,MaybeComma,DynamicEnc,Ext); gen_enc_components_call(Erule,TopType, CompList, MaybeComma, DynamicEnc, Ext) -> %% The type has no extensionmarker @@ -938,7 +952,7 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> Atype = case Type of #type{def=#'ObjectClassFieldType'{type=InnerType}} -> - InnerType; + InnerType; _ -> asn1ct_gen:get_inner(Type#type.def) end, @@ -948,6 +962,7 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> emit(["?RT_PER:encode_open_type(dummy,?RT_PER:complete("]); _ -> true end, + case Atype of {typefield,_} -> case DynamicEnc of @@ -1023,20 +1038,22 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> emit("))"); _ -> true end. -gen_dec_components_call(Erule,TopType,{Root1,ExtList,Root2},MaybeComma,DecInfObj,Ext,NumberOfOptionals) -> - gen_dec_components_call(Erule,TopType,{Root1++Root2,ExtList},MaybeComma,DecInfObj,Ext,NumberOfOptionals); -gen_dec_components_call(Erule,TopType,{CompList,ExtList},MaybeComma, +gen_dec_components_call(Erule,TopType,{Root,ExtList},MaybeComma, DecInfObj,Ext,NumberOfOptionals) -> + gen_dec_components_call(Erule,TopType,{Root,ExtList,[]},MaybeComma,DecInfObj,Ext,NumberOfOptionals); +gen_dec_components_call(Erule,TopType,CL={Root1,ExtList,Root2},MaybeComma,DecInfObj,Ext,NumberOfOptionals) -> %% The type has extensionmarker - OptTable = create_optionality_table(CompList), + + OptTable = create_optionality_table(Root1++Root2), {Rpos,AccTerm,AccBytes} = - gen_dec_components_call1(Erule,TopType, CompList, 1, OptTable, + gen_dec_components_call1(Erule,TopType, Root1++Root2, 1, OptTable, MaybeComma,DecInfObj,noext,[],[], NumberOfOptionals), emit([",",nl,"{Extensions,",{next,bytes},"} = "]), emit(["?RT_PER:getextension(Ext,",{curr,bytes},"),",nl]), asn1ct_name:new(bytes), - NewExtList = wrap_extensionAdditionGroups(ExtList), + {extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL), + NewExtList = wrap_extensionAdditionGroups(ExtList,ExtGroupPosLen), {_Epos,AccTermE,AccBytesE} = gen_dec_components_call1(Erule,TopType,NewExtList,Rpos, OptTable, "",DecInfObj,Ext,[],[],NumberOfOptionals), @@ -1233,8 +1250,7 @@ gen_dec_line(Erule,TopType,Cname,Type,Pos,DecInfObj,Ext,Prop) -> "} = ?RT_PER:decode_open_type(",{curr,bytes}, ", []),",nl]), emit([indent(2),"case (catch ObjFun(", - {asis,Name}, - ",",{curr,tmpterm},",telltype,", + {asis,Name},",",{curr,tmpterm},",telltype,", {asis,RestFieldNames},")) of", nl]), emit([indent(4),"{'EXIT',",{curr,reason},"} ->",nl]), emit([indent(6),"exit({'Type not ", @@ -1596,42 +1612,44 @@ flat_complist({Rl1,El,Rl2}) -> Rl1 ++ El ++ Rl2; flat_complist({Rl,El}) -> Rl ++ El; flat_complist(CompList) -> CompList. -wrap_compList({Root1,Ext,Root2}) -> - {Root1,wrap_extensionAdditionGroups(Ext),Root2}; -wrap_compList({Root1,Ext}) -> - {Root1,wrap_extensionAdditionGroups(Ext)}; -wrap_compList(CompList) -> - CompList. +%%wrap_compList({Root1,Ext,Root2}) -> +%% {Root1,wrap_extensionAdditionGroups(Ext),Root2}; +%%wrap_compList({Root1,Ext}) -> +%% {Root1,wrap_extensionAdditionGroups(Ext)}; +%%wrap_compList(CompList) -> +%% CompList. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Will convert all componentTypes following 'ExtensionAdditionGroup' %% up to the matching 'ExtensionAdditionGroupEnd' into one componentType %% of type SEQUENCE with the componentTypes as components %% -wrap_extensionAdditionGroups(ExtCompList) -> - wrap_extensionAdditionGroups(ExtCompList,[],0). +wrap_extensionAdditionGroups(ExtCompList,ExtGroupPosLen) -> + wrap_extensionAdditionGroups(ExtCompList,ExtGroupPosLen,[],0,0). -wrap_extensionAdditionGroups([{'ExtensionAdditionGroup',_Number}|Rest],Acc,0) -> - {ExtGroupCompList= - [#'ComponentType'{textual_order=TextPos}|_], - ['ExtensionAdditionGroupEnd'|Rest2]} = +wrap_extensionAdditionGroups([{'ExtensionAdditionGroup',_Number}|Rest], + [{ActualPos,_,_}|ExtGroupPosLenRest],Acc,_ExtAddGroupDiff,ExtGroupNum) -> + {ExtGroupCompList,['ExtensionAdditionGroupEnd'|Rest2]} = lists:splitwith(fun(#'ComponentType'{}) -> true; (_) -> false end, Rest), - wrap_extensionAdditionGroups(Rest2, + wrap_extensionAdditionGroups(Rest2,ExtGroupPosLenRest, [#'ComponentType'{ - name='ExtAddGroup', % FIXME: handles ony one ExtAddGroup - typespec=#type{def=#'SEQUENCE'{ - extaddgroup=1,% FIXME: handles only one + name=list_to_atom("ExtAddGroup"++ + integer_to_list(ExtGroupNum+1)), + typespec=#type{def=#'SEQUENCE'{ + extaddgroup=ExtGroupNum+1, components=ExtGroupCompList}}, - textual_order = TextPos, - prop='OPTIONAL'}|Acc],length(ExtGroupCompList)-1); -wrap_extensionAdditionGroups([H=#'ComponentType'{textual_order=Tord}|T],Acc,ExtAddGroupDiff) when is_integer(Tord) -> - wrap_extensionAdditionGroups(T,[H#'ComponentType'{ - textual_order=Tord - ExtAddGroupDiff}|Acc],ExtAddGroupDiff); -wrap_extensionAdditionGroups([H|T],Acc,ExtAddGroupDiff) -> - wrap_extensionAdditionGroups(T,[H|Acc],ExtAddGroupDiff); -wrap_extensionAdditionGroups([],Acc,_) -> + textual_order = ActualPos, + prop='OPTIONAL'}|Acc],length(ExtGroupCompList)-1, + ExtGroupNum+1); +wrap_extensionAdditionGroups([H=#'ComponentType'{textual_order=Tord}|T], + ExtAddGrpLenPos,Acc,ExtAddGroupDiff,ExtGroupNum) when is_integer(Tord) -> + wrap_extensionAdditionGroups(T,ExtAddGrpLenPos,[H#'ComponentType'{ + textual_order=Tord - ExtAddGroupDiff}|Acc],ExtAddGroupDiff,ExtGroupNum); +wrap_extensionAdditionGroups([H|T],ExtAddGrpLenPos,Acc,ExtAddGroupDiff,ExtGroupNum) -> + wrap_extensionAdditionGroups(T,ExtAddGrpLenPos,[H|Acc],ExtAddGroupDiff,ExtGroupNum); +wrap_extensionAdditionGroups([],_,Acc,_,_) -> lists:reverse(Acc). diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index fda4e1c6d9..64a3555f62 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -129,28 +129,39 @@ pgen_types(Rtmod,Erules,N2nConvEnums,Module,[H|T]) -> end, pgen_types(Rtmod,Erules,N2nConvEnums,Module,T). +%% Enumerated type with extension marker pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',{NN1,NN2}}}}) -> NN = NN1 ++ NN2, - pgen_name2numfunc(TypeName,NN), - pgen_num2namefunc(TypeName,NN); + pgen_name2numfunc(TypeName,NN, extension_marker), + pgen_num2namefunc(TypeName,NN, extension_marker); +%% Without extension marker +pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',NN}}}) -> + pgen_name2numfunc(TypeName,NN, no_extension_marker), + pgen_num2namefunc(TypeName,NN, no_extension_marker); pgen_n2nconversion(_Erules,_) -> true. -pgen_name2numfunc(_TypeName,[]) -> +pgen_name2numfunc(_TypeName,[], _) -> true; -pgen_name2numfunc(TypeName,[{Atom,Number}]) -> +pgen_name2numfunc(TypeName,[{Atom,Number}], extension_marker) -> + emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]), + emit(["name2num_",TypeName,"({asn1_enum, Num}) -> Num.",nl,nl]); +pgen_name2numfunc(TypeName,[{Atom,Number}], _) -> emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,".",nl,nl]); -pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest]) -> +pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest], EM) -> emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]), - pgen_name2numfunc(TypeName,NNRest). + pgen_name2numfunc(TypeName,NNRest, EM). -pgen_num2namefunc(_TypeName,[]) -> +pgen_num2namefunc(_TypeName,[], _) -> true; -pgen_num2namefunc(TypeName,[{Atom,Number}]) -> +pgen_num2namefunc(TypeName,[{Atom,Number}], extension_marker) -> + emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]), + emit(["num2name_",TypeName,"(ExtensionNum) -> {asn1_enum, ExtensionNum}.",nl,nl]); +pgen_num2namefunc(TypeName,[{Atom,Number}], _) -> emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},".",nl,nl]); -pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest]) -> +pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest], EM) -> emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]), - pgen_num2namefunc(TypeName,NNRest). + pgen_num2namefunc(TypeName,NNRest, EM). pgen_objects(_,_,_,[]) -> true; diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 597fb0030b..3ccfca3784 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -419,7 +419,7 @@ gen_decode_selected(Erules,Type,FuncName) -> " {Tlv,_} = ?RT_BER:decode(Bin2",asn1ct_gen:nif_parameter(),"),",nl]), emit("{ok,"), gen_decode_selected_type(Erules,Type), - emit(["};",nl," Err -> exit({error,{selctive_decode,Err}})",nl, + emit(["};",nl," Err -> exit({error,{selective_decode,Err}})",nl, " end.",nl]). gen_decode_selected_type(_Erules,TypeDef) -> diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 59b4b3d261..bd5b81991d 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -321,19 +321,13 @@ effective_constr(_,[]) -> []; effective_constr('SingleValue',List) -> SVList = lists:flatten(lists:map(fun(X)->element(2,X)end,List)), - % sort and remove duplicates - SortedSVList = lists:sort(SVList), - RemoveDup = fun([],_) ->[]; - ([H],_) -> [H]; - ([H,H|T],F) -> F([H|T],F); - ([H|T],F) -> [H|F(T,F)] - end, - - case RemoveDup(SortedSVList,RemoveDup) of + %% Sort and remove duplicates before generating SingleValue or ValueRange + %% In case of ValueRange, also check for 'MIN and 'MAX' + case lists:usort(SVList) of [N] -> [{'SingleValue',N}]; - L when is_list(L) -> - [{'ValueRange',{hd(L),lists:last(L)}}] + L when is_list(L) -> + [{'ValueRange',{least_Lb(L),greatest_Ub(L)}}] end; effective_constr('ValueRange',List) -> LBs = lists:map(fun({_,{Lb,_}})-> Lb end,List), @@ -1405,19 +1399,21 @@ get_object_field(Name,ObjectFields) -> %% have been specified within a SEQUENCE, therefore we construct a fake sequence type here %% so that we can generate code for it extaddgroup2sequence(ExtList) -> - extaddgroup2sequence(ExtList,[]). + extaddgroup2sequence(ExtList,0,[]). -extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],Acc) -> +extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],ExtNum,Acc) -> Number = case Number0 of undefined -> 1; _ -> Number0 end, {ExtGroupComps,['ExtensionAdditionGroupEnd'|T2]} = lists:splitwith(fun(Elem) -> is_record(Elem,'ComponentType') end,T), - extaddgroup2sequence(T2,[#'ComponentType'{ - name='ExtAddGroup', - typespec=#type{def=#'SEQUENCE'{ - extaddgroup=Number, - components=ExtGroupComps}}, - prop='OPTIONAL'}|Acc]); -extaddgroup2sequence([C|T],Acc) -> - extaddgroup2sequence(T,[C|Acc]); -extaddgroup2sequence([],Acc) -> + extaddgroup2sequence(T2,ExtNum+1, + [#'ComponentType'{ + name=list_to_atom("ExtAddGroup"++ + integer_to_list(ExtNum+1)), + typespec=#type{def=#'SEQUENCE'{ + extaddgroup=Number, + components=ExtGroupComps}}, + prop='OPTIONAL'}|Acc]); +extaddgroup2sequence([C|T],ExtNum,Acc) -> + extaddgroup2sequence(T,ExtNum,[C|Acc]); +extaddgroup2sequence([],_,Acc) -> lists:reverse(Acc). diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index 4add659d79..16eec92847 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -670,18 +670,13 @@ effective_constr(_,[]) -> []; effective_constr('SingleValue',List) -> SVList = lists:flatten(lists:map(fun(X)->element(2,X)end,List)), - % sort and remove duplicates - RemoveDup = fun([],_) ->[]; - ([H],_) -> [H]; - ([H,H|T],F) -> F([H|T],F); - ([H|T],F) -> [H|F(T,F)] - end, - - case RemoveDup(SVList,RemoveDup) of + %% Sort and remove duplicates before generating SingleValue or ValueRange + %% In case of ValueRange, also check for 'MIN and 'MAX' + case lists:usort(SVList) of [N] -> [{'SingleValue',N}]; - L when is_list(L) -> - [{'ValueRange',{hd(L),lists:last(L)}}] + L when is_list(L) -> + [{'ValueRange',{least_Lb(L),greatest_Ub(L)}}] end; effective_constr('ValueRange',List) -> LBs = lists:map(fun({_,{Lb,_}})-> Lb end,List), @@ -1808,19 +1803,21 @@ dec_enumerated_cases([],_,_) -> %% have been specified within a SEQUENCE, therefore we construct a fake sequence type here %% so that we can generate code for it extaddgroup2sequence(ExtList) -> - extaddgroup2sequence(ExtList,[]). + extaddgroup2sequence(ExtList,0,[]). -extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],Acc) -> +extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],ExtNum,Acc) -> Number = case Number0 of undefined -> 1; _ -> Number0 end, {ExtGroupComps,['ExtensionAdditionGroupEnd'|T2]} = lists:splitwith(fun(Elem) -> is_record(Elem,'ComponentType') end,T), - extaddgroup2sequence(T2,[#'ComponentType'{ - name='ExtAddGroup', - typespec=#type{def=#'SEQUENCE'{ - extaddgroup=Number, - components=ExtGroupComps}}, - prop='OPTIONAL'}|Acc]); -extaddgroup2sequence([C|T],Acc) -> - extaddgroup2sequence(T,[C|Acc]); -extaddgroup2sequence([],Acc) -> + extaddgroup2sequence(T2,ExtNum+1, + [#'ComponentType'{ + name=list_to_atom("ExtAddGroup"++ + integer_to_list(ExtNum+1)), + typespec=#type{def=#'SEQUENCE'{ + extaddgroup=Number, + components=ExtGroupComps}}, + prop='OPTIONAL'}|Acc]); +extaddgroup2sequence([C|T],ExtNum,Acc) -> + extaddgroup2sequence(T,ExtNum,[C|Acc]); +extaddgroup2sequence([],_,Acc) -> lists:reverse(Acc). diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl index 007d390a1b..7301f49085 100644 --- a/lib/asn1/src/asn1ct_parser2.erl +++ b/lib/asn1/src/asn1ct_parser2.erl @@ -769,9 +769,11 @@ resolve_module(_Type, Current, undefined) -> Current; resolve_module(Type, Current, Imports) -> case [Mod || #'SymbolsFromModule'{symbols = S, module = Mod} <- Imports, - #'Externaltypereference'{type = T} <- S, + #'Externaltypereference'{type = T} <- S, Type == T] of - [#'Externaltypereference'{type = Mod}] -> Mod; + [#'Externaltypereference'{type = Mod}|_] -> Mod; + %% This allows the same symbol to be imported several times + %% which ought to be checked elsewhere and flagged as an error [] -> Current end. diff --git a/lib/asn1/src/asn1rt_ber_bin_v2.erl b/lib/asn1/src/asn1rt_ber_bin_v2.erl index 17e66f77c9..420e3e1d91 100644 --- a/lib/asn1/src/asn1rt_ber_bin_v2.erl +++ b/lib/asn1/src/asn1rt_ber_bin_v2.erl @@ -610,8 +610,8 @@ match_tags(Vlist = [{T,_V}|_], [T]) -> Vlist; match_tags(Tlv, []) -> Tlv; -match_tags({Tag,_V},[T|_Tt]) -> - {error,{asn1,{wrong_tag,{Tag,T}}}}. +match_tags(Tlv = {Tag,_V},[T|_Tt]) -> + exit({error,{asn1,{wrong_tag,{{expected,T},{got,Tag,Tlv}}}}}). cindex(Ix,Val,Cname) -> diff --git a/lib/asn1/src/asn1rt_check.erl b/lib/asn1/src/asn1rt_check.erl index d9856901b8..35b993fc71 100644 --- a/lib/asn1/src/asn1rt_check.erl +++ b/lib/asn1/src/asn1rt_check.erl @@ -311,7 +311,8 @@ transform_to_EXTERNAL1990([Data_val_desc,Data_value],Acc) when is_binary(Data_value)-> list_to_tuple(lists:reverse([{'single-ASN1-type',Data_value}, Data_val_desc|Acc])); -transform_to_EXTERNAL1990([Data_value],Acc) when is_list(Data_value)-> +transform_to_EXTERNAL1990([Data_value],Acc) + when is_list(Data_value); is_binary(Data_value) -> list_to_tuple(lists:reverse([{'octet-aligned',Data_value}|Acc])). diff --git a/lib/asn1/src/asn1rt_per_bin.erl b/lib/asn1/src/asn1rt_per_bin.erl index a124c7553d..85988aa21d 100644 --- a/lib/asn1/src/asn1rt_per_bin.erl +++ b/lib/asn1/src/asn1rt_per_bin.erl @@ -18,7 +18,6 @@ %% %% -module(asn1rt_per_bin). - %% encoding / decoding of PER aligned -include("asn1_records.hrl"). @@ -57,7 +56,7 @@ encode_NumericString/2, decode_NumericString/2, encode_ObjectDescriptor/2, decode_ObjectDescriptor/1 ]). --export([complete_bytes/1, getbits/2, getoctets/2]). +-export([complete_bytes/1, getbits/2, getoctets/2, minimum_bits/1]). -define('16K',16384). -define('32K',32768). @@ -695,21 +694,28 @@ encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val -> {octets,[Val2]}; Range =< 65536 -> {octets,<<Val2:16>>}; - Range =< 16#1000000 -> - Octs = eint_positive(Val2), - [{bits,2,length(Octs)-1},{octets,Octs}]; - Range =< 16#100000000 -> - Octs = eint_positive(Val2), - [{bits,2,length(Octs)-1},{octets,Octs}]; - Range =< 16#10000000000 -> - Octs = eint_positive(Val2), - [{bits,3,length(Octs)-1},{octets,Octs}]; + Range =< (1 bsl (255*8)) -> + Octs = binary:encode_unsigned(Val2), + RangeOcts = binary:encode_unsigned(Range - 1), + OctsLen = erlang:byte_size(Octs), + RangeOctsLen = erlang:byte_size(RangeOcts), + LengthBitsNeeded = minimum_bits(RangeOctsLen - 1), + [{bits, LengthBitsNeeded, OctsLen - 1}, {octets, Octs}]; true -> exit({not_supported,{integer_range,Range}}) end; encode_constrained_number(Range,Val) -> exit({error,{asn1,{integer_range,Range,value,Val}}}). +%% For some reason the minimum bits needed in the length field in encoding of +%% constrained whole numbers must always be atleast 2? +minimum_bits(N) when N < 4 -> 2; +minimum_bits(N) when N < 8 -> 3; +minimum_bits(N) when N < 16 -> 4; +minimum_bits(N) when N < 32 -> 5; +minimum_bits(N) when N < 64 -> 6; +minimum_bits(N) when N < 128 -> 7; +minimum_bits(_N) -> 8. decode_constrained_number(Buffer,{Lb,Ub}) -> Range = Ub - Lb + 1, @@ -738,18 +744,12 @@ decode_constrained_number(Buffer,{Lb,Ub}) -> getoctets(Buffer,1); Range =< 65536 -> getoctets(Buffer,2); - Range =< 16#1000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,3}), - {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; - Range =< 16#100000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,4}), - {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; - Range =< 16#10000000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,5}), - {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; + Range =< (1 bsl (255*8)) -> + OList = binary:bin_to_list(binary:encode_unsigned(Range - 1)), + RangeOctLen = length(OList), + {Len, Bytes} = decode_length(Buffer, {1, RangeOctLen}), + {Octs, RestBytes} = getoctets_as_list(Bytes, Len), + {binary:decode_unsigned(binary:list_to_bin(Octs)), RestBytes}; true -> exit({not_supported,{integer_range,Range}}) end, diff --git a/lib/asn1/src/asn1rt_per_bin_rt2ct.erl b/lib/asn1/src/asn1rt_per_bin_rt2ct.erl index 750b59aba6..46d4bcb065 100644 --- a/lib/asn1/src/asn1rt_per_bin_rt2ct.erl +++ b/lib/asn1/src/asn1rt_per_bin_rt2ct.erl @@ -18,7 +18,6 @@ %% %% -module(asn1rt_per_bin_rt2ct). - %% encoding / decoding of PER aligned -include("asn1_records.hrl"). @@ -605,19 +604,13 @@ encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val -> Range =< 65536 -> % Size = {octets,<<Val2:16>>}; [20,2,<<Val2:16>>]; - Range =< 16#1000000 -> - Octs = eint_positive(Val2), -% [{bits,2,length(Octs)-1},{octets,Octs}]; - Len = length(Octs), - [10,2,Len-1,20,Len,Octs]; - Range =< 16#100000000 -> - Octs = eint_positive(Val2), - Len = length(Octs), - [10,2,Len-1,20,Len,Octs]; - Range =< 16#10000000000 -> - Octs = eint_positive(Val2), - Len = length(Octs), - [10,3,Len-1,20,Len,Octs]; + Range =< (1 bsl (255*8)) -> + Octs = binary:encode_unsigned(Val2), + RangeOcts = binary:encode_unsigned(Range - 1), + OctsLen = erlang:byte_size(Octs), + RangeOctsLen = erlang:byte_size(RangeOcts), + LengthBitsNeeded = asn1rt_per_bin:minimum_bits(RangeOctsLen - 1), + [10,LengthBitsNeeded,OctsLen-1,20,OctsLen,Octs]; true -> exit({not_supported,{integer_range,Range}}) end; @@ -661,18 +654,12 @@ decode_constrained_number(Buffer,{Lb,_Ub},Range) -> getoctets(Buffer,1); Range =< 65536 -> getoctets(Buffer,2); - Range =< 16#1000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,3}), - {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; - Range =< 16#100000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,4}), - {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; - Range =< 16#10000000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,5}), - {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; + Range =< (1 bsl (255*8)) -> + OList = binary:bin_to_list(binary:encode_unsigned(Range - 1)), + RangeOctLen = length(OList), + {Len, Bytes} = decode_length(Buffer, {1, RangeOctLen}), + {Octs, RestBytes} = getoctets_as_bin(Bytes, Len), + {binary:decode_unsigned(Octs), RestBytes}; true -> exit({not_supported,{integer_range,Range}}) end, diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile index 1621abbec9..6e6374baf1 100644 --- a/lib/asn1/test/Makefile +++ b/lib/asn1/test/Makefile @@ -156,13 +156,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/asn1_SUITE_data - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) asn1.spec asn1.cover $(INSTALL_PROGS) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - cd asn1_SUITE_data; tar cfh $(RELSYSDIR)/asn1_SUITE_data.tar * - cd $(RELSYSDIR)/asn1_SUITE_data; tar xf $(RELSYSDIR)/asn1_SUITE_data.tar - rm $(RELSYSDIR)/asn1_SUITE_data.tar + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/asn1_SUITE_data" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) asn1.spec asn1.cover $(INSTALL_PROGS) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + cd asn1_SUITE_data; tar cfh "$(RELSYSDIR)/asn1_SUITE_data.tar" * + cd "$(RELSYSDIR)/asn1_SUITE_data"; tar xf "$(RELSYSDIR)/asn1_SUITE_data.tar" + rm "$(RELSYSDIR)/asn1_SUITE_data.tar" release_docs_spec: diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 3b9a7532c0..56f31de638 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -773,6 +773,7 @@ per_open_type(Config, Rule, Opts) -> testConstraints(Config) -> test(Config, fun testConstraints/3). testConstraints(Config, Rule, Opts) -> asn1_test_lib:compile("Constraints", Config, [Rule|Opts]), + asn1_test_lib:compile("LargeConstraints", Config, [Rule|Opts]), testConstraints:int_constraints(Rule). @@ -1236,6 +1237,27 @@ testName2Number(Config) -> 0 = 'S1AP-IEs':name2num_CauseMisc('control-processing-overload'), 'unknown-PLMN' = 'S1AP-IEs':num2name_CauseMisc(5), + + %% OTP-10144 + %% Test that n2n option generates name2num and num2name functions supporting + %% values not within the extension root if the enumeration type has an + %% extension marker. + N2NOptionsExt = [{n2n, 'NoExt'}, {n2n, 'Ext'}, {n2n, 'Ext2'}], + asn1_test_lib:compile("EnumN2N", Config, N2NOptionsExt), + %% Previously, name2num and num2name was not generated if the type didn't + %% have an extension marker: + 0 = 'EnumN2N':name2num_NoExt('blue'), + 2 = 'EnumN2N':name2num_NoExt('green'), + blue = 'EnumN2N':num2name_NoExt(0), + green = 'EnumN2N':num2name_NoExt(2), + + %% Test enumeration extension: + 7 = 'EnumN2N':name2num_Ext2('orange'), + orange = 'EnumN2N':num2name_Ext2(7), + %% 7 is not defined in Ext, only in Ext2. + {asn1_enum, 7} = 'EnumN2N':num2name_Ext(7), + 7 = 'EnumN2N':name2num_Ext({asn1_enum, 7}), + 42 = 'EnumN2N':name2num_Ext2({asn1_enum, 42}), ok. ticket_7407(Config) -> diff --git a/lib/asn1/test/asn1_SUITE_data/Constraints.py b/lib/asn1/test/asn1_SUITE_data/Constraints.py index de48c4c2ca..87243121f7 100644 --- a/lib/asn1/test/asn1_SUITE_data/Constraints.py +++ b/lib/asn1/test/asn1_SUITE_data/Constraints.py @@ -4,9 +4,14 @@ BEGIN -- Single Value SingleValue ::= INTEGER (1) SingleValue2 ::= INTEGER (1..20) +predefined INTEGER ::= 1 +SingleValue3 ::= INTEGER (predefined | 5 | 10) Range2to19 ::= INTEGER (1<..<20) Range10to20 ::= INTEGER (10..20) ContainedSubtype ::= INTEGER (INCLUDES Range10to20) +-- Some ranges for additional constrained number testing. +LongLong ::= INTEGER (0..18446744073709551615) +Range256to65536 ::= INTEGER (256..65536) FixedSize ::= OCTET STRING (SIZE(10)) FixedSize2 ::= OCTET STRING (SIZE(10|20)) VariableSize ::= OCTET STRING (SIZE(1..10)) diff --git a/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1 b/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1 new file mode 100644 index 0000000000..a724f2f3f5 --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1 @@ -0,0 +1,25 @@ +EnumN2N DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + +NoExt ::= ENUMERATED { + blue(0), + red(1), + green(2) +} + +Ext ::= ENUMERATED { + blue(0), + red(1), + green(2), + ... +} + +Ext2 ::= ENUMERATED { + blue(0), + red(1), + green(2), + ..., + orange(7) +} + +END diff --git a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn index fc244c30a2..cacef8b922 100644 --- a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn +++ b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn @@ -79,4 +79,22 @@ Ax3 ::= SEQUENCE { } -- { a 253, b TRUE, s {sa 17, sb TRUE, sextaddgroup 11}} + +-- This is to test the case with more than one ExtensionAdditionGroup +-- which did not work before + +AS-Config ::= SEQUENCE { + a INTEGER, + b BOOLEAN, + c OCTET STRING, + ..., + [[ sourceSystemInformationBlockType1Ext OCTET STRING OPTIONAL, + sourceOtherConfig-r9 INTEGER + ]], + [[ sourceSCellConfigList-r10 OCTET STRING OPTIONAL + ]] +} + + + END diff --git a/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py b/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py new file mode 100644 index 0000000000..68c7616b62 --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py @@ -0,0 +1,9 @@ +LargeConstraints DEFINITIONS ::= +BEGIN + +-- Maximum number that can be encoded as a constrained whole number: 1 bsl (255*8) +-- The number of octets needed to represent a number cannot be more than 255 +-- As the length field is encoded as a 8-bit bitfield. +RangeMax ::= INTEGER (1..126238304966058622268417487065116999845484776053576109500509161826268184136202698801551568013761380717534054534851164138648904527931605160527688095259563605939964364716019515983399209962459578542172100149937763938581219604072733422507180056009672540900709554109516816573779593326332288314873251559077853068444977864803391962580800682760017849589281937637993445539366428356761821065267423102149447628375691862210717202025241630303118559188678304314076943801692528246980959705901641444238894928620825482303431806955690226308773426829503900930529395181208739591967195841536053143145775307050594328881077553168201547776) + +END diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl index 1ce68ec522..543c106e8a 100644 --- a/lib/asn1/test/testConstraints.erl +++ b/lib/asn1/test/testConstraints.erl @@ -52,8 +52,6 @@ int_constraints(Rules) -> ?line {error,_Reason2} = asn1_wrapper:encode('Constraints','SingleValue',1000) end, - - %%========================================================== %% SingleValue2 ::= INTEGER (1..20) @@ -86,7 +84,21 @@ int_constraints(Rules) -> asn1_wrapper:encode('Constraints','SingleValue',1000) end, + %%========================================================== + %% SingleValue3 ::= INTEGER (Predefined | 5 | 10) + %% Testcase for OTP-10139. A single value subtyping of an integer type + %% where one value is predefined. + %%========================================================== + ?line {ok,BytesSV3} = asn1_wrapper:encode('Constraints','SingleValue3',1), + ?line {ok,1} = asn1_wrapper:decode('Constraints','SingleValue3', + lists:flatten(BytesSV3)), + ?line {ok,BytesSV3_2} = asn1_wrapper:encode('Constraints','SingleValue3',5), + ?line {ok,5} = asn1_wrapper:decode('Constraints','SingleValue3', + lists:flatten(BytesSV3_2)), + ?line {ok,BytesSV3_3} = asn1_wrapper:encode('Constraints','SingleValue3',10), + ?line {ok,10} = asn1_wrapper:decode('Constraints','SingleValue3', + lists:flatten(BytesSV3_3)), %%========================================================== %% Range2to19 ::= INTEGER (1<..<20) @@ -116,7 +128,65 @@ int_constraints(Rules) -> ?line {error,_Reason6} = asn1_wrapper:encode('Constraints','Range2to19',20) end, + + %%========================================================== + %% Tests for Range above 16^4 up to maximum supported by asn1 assuming the + %% octet length field is encoded on max 8 bits + %%========================================================== + LastNumWithoutLengthEncoding = 65536, + ?line {ok,BytesFoo} = asn1_wrapper:encode('Constraints','Range256to65536', + LastNumWithoutLengthEncoding), + ?line {ok,LastNumWithoutLengthEncoding} = + asn1_wrapper:decode('Constraints','Range256to65536',lists:flatten(BytesFoo)), + + FirstNumWithLengthEncoding = 65537, + ?line {ok,BytesBar} = asn1_wrapper:encode('LargeConstraints','RangeMax', + FirstNumWithLengthEncoding), + ?line {ok,FirstNumWithLengthEncoding} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBar)), + + FirstNumOver16_6 = 16777217, + ?line {ok, BytesBaz} = + asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_6), + ?line {ok, FirstNumOver16_6} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBaz)), + + FirstNumOver16_8 = 4294967297, + ?line {ok, BytesQux} = + asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_8), + ?line {ok, FirstNumOver16_8} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesQux)), + + FirstNumOver16_10 = 1099511627776, + ?line {ok, BytesBur} = + asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_10), + ?line {ok, FirstNumOver16_10} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBur)), + + FirstNumOver16_10 = 1099511627776, + ?line {ok, BytesBur} = + asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_10), + ?line {ok, FirstNumOver16_10} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBur)), + + HalfMax = 1 bsl (128*8), + ?line {ok, BytesHalfMax} = + asn1_wrapper:encode('LargeConstraints','RangeMax', HalfMax), + ?line {ok, HalfMax} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesHalfMax)), + + Max = 1 bsl (255*8), + ?line {ok, BytesMax} = + asn1_wrapper:encode('LargeConstraints','RangeMax', Max), + ?line {ok, Max} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesMax)), + %% Random number within longlong range + LongLong = 12672809400538808320, + ?line {ok, BytesLongLong} = + asn1_wrapper:encode('Constraints','LongLong', LongLong), + ?line {ok, LongLong} = + asn1_wrapper:decode('Constraints','LongLong',lists:flatten(BytesLongLong)), %%========================================================== %% Constraint Combinations (Duboisson p. 285) diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile index d9651f13b0..99161ce68a 100644 --- a/lib/common_test/doc/src/Makefile +++ b/lib/common_test/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2011. All Rights Reserved. +# Copyright Ericsson AB 2003-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 @@ -46,7 +46,8 @@ CT_MODULES = \ ct_rpc \ ct_snmp \ unix_telnet \ - ct_slave + ct_slave \ + ct_netconfc CT_XML_FILES = $(CT_MODULES:=.xml) @@ -123,7 +124,7 @@ $(HTMLDIR)/%.gif: %.gif docs: pdf html man -$(CT_XML_FILES): +$(CT_XML_FILES): %.xml: ../../src/%.erl escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -preprocess true -i $(XMERL_DIR)/include \ -i ../../../test_server/include -i ../../include \ -i ../../../../erts/lib/kernel/include -i ../../../../lib/kernel/include \ @@ -158,18 +159,18 @@ 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/man1 - $(INSTALL_DATA) $(MAN1DIR)/* $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN1DIR)/* "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml index 6babdb93af..a0fa45c71f 100644 --- a/lib/common_test/doc/src/common_test_app.xml +++ b/lib/common_test/doc/src/common_test_app.xml @@ -99,11 +99,11 @@ be executed by Common Test. A test case is represented by an atom, the name of the test case function. A test case group is represented by a <c>group</c> tuple, where <c>GroupName</c>, - an atom, is the name of the group (defined in <c>groups/0</c>). + an atom, is the name of the group (defined in <c><seealso marker="#Module:groups-0">groups/0</seealso></c>). Execution properties for groups may also be specified, both for a top level group and for any of its sub-groups. Group execution properties specified here, will override - properties in the group definition (see <c>groups/0</c>). + properties in the group definition (see <c><seealso marker="#Module:groups-0">groups/0</seealso></c>). (With value <c>default</c>, the group definition properties will be used).</p> @@ -162,7 +162,7 @@ <v> Func = atom()</v> <v> Args = list()</v> <v> Fun = fun()</v> - <v> Required = Key | {Key,SubKeys}</v> + <v> Required = Key | {Key,SubKeys} | {Key,SubKey} | {Key,SubKey,SubKeys}</v> <v> Key = atom()</v> <v> SubKeys = SubKey | [SubKey]</v> <v> SubKey = atom()</v> @@ -184,8 +184,8 @@ test cases in the suite).</p> <p>The <c>timetrap</c> tag sets the maximum time each - test case is allowed to execute (including <c>init_per_testcase/2</c> - and <c>end_per_testcase/2</c>). If the timetrap time is + test case is allowed to execute (including <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c> + and <c><seealso marker="#Module:end_per_testcase-2">end_per_testcase/2</seealso></c>). If the timetrap time is exceeded, the test case fails with reason <c>timetrap_timeout</c>. A <c>TimeFunc</c> function can be used to set a new timetrap by returning a <c>TimeVal</c>. It may also be @@ -201,11 +201,11 @@ in any of the configuration files, all test cases are skipped. For more information about the 'require' functionality, see the reference manual for the function - <c>ct:require/[1,2]</c>.</p> + <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c>.</p> <p>With <c>userdata</c>, it is possible for the user to specify arbitrary test suite related information which can be - read by calling <c>ct:userdata/2</c>.</p> + read by calling <c><seealso marker="ct#userdata-2">ct:userdata/2</seealso></c>.</p> <p>The <c>ct_hooks</c> tag specifies which <seealso marker="ct_hooks_chapter">Common Test Hooks</seealso> @@ -264,7 +264,7 @@ <p>This function is called as the last test case in the suite. It is meant to be used for cleaning up after - <c>init_per_suite/1</c>. + <c><seealso marker="#Module:init_per_suite-1">init_per_suite/1</seealso></c>. For information on <c>save_config</c>, please see <seealso marker="dependencies_chapter#save_config">Dependencies between Test Cases and Suites</seealso> in the User's Guide.</p> @@ -289,7 +289,7 @@ <v> Func = atom()</v> <v> Args = list()</v> <v> Fun = fun()</v> - <v> Required = Key | {Key,SubKeys}</v> + <v> Required = Key | {Key,SubKeys} | {Key,Subkey} | {Key,Subkey,SubKeys}</v> <v> Key = atom()</v> <v> SubKeys = SubKey | [SubKey]</v> <v> SubKey = atom()</v> @@ -309,13 +309,14 @@ <p>This is the test case group info function. It is supposed to return a list of tagged tuples that specify various properties related to the execution of a test case group (i.e. its test cases - and sub-groups). Properties set by <c>groups/1</c> override + and sub-groups). Properties set by + <c><seealso marker="#Module:group-1">group/1</seealso></c> override properties with the same key that have been previously set by - <c>suite/0</c>.</p> + <c><seealso marker="#Module:suite-0">suite/0</seealso></c>.</p> <p>The <c>timetrap</c> tag sets the maximum time each - test case is allowed to execute (including <c>init_per_testcase/2</c> - and <c>end_per_testcase/2</c>). If the timetrap time is + test case is allowed to execute (including <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c> + and <c><seealso marker="#Module:end_per_testcase-2">end_per_testcase/2</seealso></c>). If the timetrap time is exceeded, the test case fails with reason <c>timetrap_timeout</c>. A <c>TimeFunc</c> function can be used to set a new timetrap by returning a <c>TimeVal</c>. It may also be @@ -330,11 +331,11 @@ in any of the configuration files, all test cases in this group are skipped. For more information about the 'require' functionality, see the reference manual for the function - <c>ct:require/[1,2]</c>.</p> + <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c>.</p> <p>With <c>userdata</c>, it is possible for the user to specify arbitrary test case group related information which can be - read by calling <c>ct:userdata/2</c>.</p> + read by calling <c><seealso marker="ct#userdata-2">ct:userdata/2</seealso></c>.</p> <p>The <c>ct_hooks</c> tag specifies which <seealso marker="ct_hooks_chapter">Common Test Hooks</seealso> @@ -367,7 +368,7 @@ test case group. It typically contains initializations which are common for all test cases and sub-groups in the group, and which shall only be performed once. <c>GroupName</c> is the name of the - group, as specified in the group definition (see <c>groups/0</c>). The + group, as specified in the group definition (see <c><seealso marker="#Module:groups-0">groups/0</seealso></c>). The <c>Config</c> parameter is the configuration data which can be modified here. The return value of this function is given as <c>Config</c> to all test cases and sub-groups in the group. If <c>{skip,Reason}</c> @@ -396,10 +397,10 @@ <p> OPTIONAL </p> <p>This function is called after the execution of a test case group is finished. - It is meant to be used for cleaning up after <c>init_per_group/2</c>. + It is meant to be used for cleaning up after <c><seealso marker="#Module:init_per_group-2">init_per_group/2</seealso></c>. By means of <c>{return_group_result,Status}</c>, it is possible to return a status value for a nested sub-group. The status can be retrieved in - <c>end_per_group/2</c> for the group on the level above. The status will also + <c><seealso marker="#Module:end_per_group-2">end_per_group/2</seealso></c> for the group on the level above. The status will also be used by Common Test for deciding if execution of a group should proceed in case the property <c>sequence</c> or <c>repeat_until_*</c> is set.</p> @@ -450,7 +451,7 @@ <p> OPTIONAL </p> <p> This function is called after each test case, and can be used - to clean up after <c>init_per_testcase/2</c> and the test case. + to clean up after <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c> and the test case. Any return value (besides <c>{fail,Reason}</c> and <c>{save_config,SaveConfig}</c>) is ignored. By returning <c>{fail,Reason}</c>, <c>TestCase</c> will be marked as failed (even though it was actually successful in the sense that it returned @@ -476,7 +477,7 @@ <v> Func = atom()</v> <v> Args = list()</v> <v> Fun = fun()</v> - <v> Required = Key | {Key,SubKeys}</v> + <v> Required = Key | {Key,SubKeys} | {Key,Subkey} | {Key,Subkey,SubKeys}</v> <v> Key = atom()</v> <v> SubKeys = SubKey | [SubKey]</v> <v> SubKey = atom()</v> @@ -492,15 +493,15 @@ <p>This is the test case info function. It is supposed to return a list of tagged tuples that specify various properties related to the execution of this particular test case. - Properties set by <c>Testcase/0</c> override + Properties set by <c><seealso marker="#Module:Testcase-0">Testcase/0</seealso></c> override properties that have been previously set for the test case - by <c>group/1</c> or <c>suite/0</c>.</p> + by <c><seealso marker="#Module:group-1">group/1</seealso></c> or <c><seealso marker="#Module:suite-0">suite/0</seealso></c>.</p> <p>The <c>timetrap</c> tag sets the maximum time the test case is allowed to execute. If the timetrap time is exceeded, the test case fails with reason - <c>timetrap_timeout</c>. <c>init_per_testcase/2</c> - and <c>end_per_testcase/2</c> are included in the + <c>timetrap_timeout</c>. <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c> + and <c><seealso marker="#Module:end_per_testcase-2">end_per_testcase/2</seealso></c> are included in the timetrap time. A <c>TimeFunc</c> function can be used to set a new timetrap by returning a <c>TimeVal</c>. It may also be used to trigger a timetrap timeout by, at some point, returning a @@ -514,15 +515,15 @@ configuration files, the test case is skipped. For more information about the 'require' functionality, see the reference manual for the function - <c>ct:require/[1,2]</c>.</p> + <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c>.</p> <p>If <c>timetrap</c> and/or <c>require</c> is not set, the - default values specified by <c>suite/0</c> (or - <c>group/1</c>) will be used.</p> + default values specified by <c><seealso marker="#Module:suite-0">suite/0</seealso></c> (or + <c><seealso marker="#Module:group-1">group/1</seealso></c>) will be used.</p> <p>With <c>userdata</c>, it is possible for the user to specify arbitrary test case related information which can be - read by calling <c>ct:userdata/3</c>.</p> + read by calling <c><seealso marker="ct#userdata-3">ct:userdata/3</seealso></c>.</p> <p>Other tuples than the ones defined will simply be ignored.</p> @@ -550,7 +551,7 @@ <p>This is the implementation of a test case. Here you must call the functions you want to test, and do whatever you need to check the result. If something fails, make sure the - function causes a runtime error, or call <c>ct:fail/1/2</c> + function causes a runtime error, or call <c><seealso marker="ct#fail-1">ct:fail/1/2</seealso></c> (which also causes the test case process to terminate).</p> <p>Elements from the <c>Config</c> list can e.g. be read diff --git a/lib/common_test/doc/src/config_file_chapter.xml b/lib/common_test/doc/src/config_file_chapter.xml index 6a860bb58b..3e6fb21659 100644 --- a/lib/common_test/doc/src/config_file_chapter.xml +++ b/lib/common_test/doc/src/config_file_chapter.xml @@ -29,6 +29,8 @@ <file>config_file_chapter.xml</file> </header> + <marker id="top"></marker> + <section> <title>General</title> @@ -78,7 +80,7 @@ test is skipped (unless a default value has been specified, see the <seealso marker="write_test_chapter#info_function">test case info function</seealso> chapter for details). There is also a function - <c>ct:require/[1,2]</c> which can be called from a test case + <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c> which can be called from a test case in order to check if a specific variable is available. The return value from this function must be checked explicitly and appropriate action be taken depending on the result (e.g. to skip the test case @@ -88,7 +90,7 @@ info-list should look like this: <c>{require,CfgVarName}</c> or <c>{require,AliasName,CfgVarName}</c>. The arguments <c>AliasName</c> and <c>CfgVarName</c> are the same as the - arguments to <c>ct:require/[1,2]</c> which are described in the + arguments to <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c> which are described in the reference manual for <seealso marker="ct">ct</seealso>. <c>AliasName</c> becomes an alias for the configuration variable, and can be used as reference to the configuration data value. @@ -101,7 +103,8 @@ (or test case) and improve readability.</item> </list> <p>To read the value of a config variable, use the function - <c>get_config/[1,2,3]</c> which is also described in the reference + <c><seealso marker="ct#get_config-1">get_config/1/2/3</seealso></c> + which is also described in the reference manual for <seealso marker="ct">ct</seealso>.</p> <p>Example:</p> <pre> @@ -118,7 +121,7 @@ <section> <title>Using configuration variables defined in multiple files</title> <p>If a configuration variable is defined in multiple files and you - want to access all possible values, you may use the <c>ct:get_config/3</c> + want to access all possible values, you may use the <c><seealso marker="ct#get_config-3">ct:get_config/3</seealso></c> function and specify <c>all</c> in the options list. The values will then be returned in a list and the order of the elements corresponds to the order that the config files were specified at startup. Please see @@ -130,7 +133,7 @@ <marker id="encrypted_config_files"></marker> <p>It is possible to encrypt configuration files containing sensitive data if these files must be stored in open and shared directories.</p> - <p>Call <c>ct:encrypt_config_file/[2,3]</c> to have Common Test encrypt a + <p>Call <c><seealso marker="ct#encrypt_config_file-2">ct:encrypt_config_file/2/3</seealso></c> to have Common Test encrypt a specified file using the DES3 function in the OTP <c>crypto</c> application. The encrypted file can then be used as a regular configuration file, in combination with other encrypted files or normal text files. The key @@ -139,7 +142,7 @@ <c>decrypt_file</c> flag/option, or a key file in a predefined location.</p> <p>Common Test also provides decryption functions, - <c>ct:decrypt_config_file/[2,3]</c>, for recreating the original text + <c><seealso marker="ct#decrypt_config_file-2">ct:decrypt_config_file/2/3</seealso></c>, for recreating the original text files.</p> <p>Please see the <seealso marker="ct">ct</seealso> reference manual for @@ -149,8 +152,8 @@ <section> <title>Opening connections by using configuration data</title> <p>There are two different methods for opening a connection - by means of the support functions in e.g. <c>ct_ssh</c>, <c>ct_ftp</c>, - and <c>ct_telnet</c>:</p> + by means of the support functions in e.g. <c><seealso marker="ct_ssh">ct_ssh</seealso></c>, <c><seealso marker="ct_ftp">ct_ftp</seealso></c>, + and <c><seealso marker="ct_telnet">ct_telnet</seealso></c>:</p> <list> <item>Using a configuration target name (an alias) as reference.</item> <item>Using the configuration variable as reference.</item> @@ -295,7 +298,7 @@ <pre> [{ftp_host, [{ftp, "targethost"}, {username, "tester"}, {password, "letmein"}]}, - {lm_directory, "/test/loadmodules"}]</pre> + {lm_directory, "/test/loadmodules"}]</pre> </section> diff --git a/lib/common_test/doc/src/cover_chapter.xml b/lib/common_test/doc/src/cover_chapter.xml index b7162cb542..fc609ee137 100644 --- a/lib/common_test/doc/src/cover_chapter.xml +++ b/lib/common_test/doc/src/cover_chapter.xml @@ -100,7 +100,7 @@ <p><c>$ ct_run -dir $TESTOBJS/db -cover $TESTOBJS/db/config/db.coverspec</c></p> <p>You may also pass the cover specification file name in a - call to <c>ct:run_test/1</c>, by adding a <c>{cover,CoverSpec}</c> + call to <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, by adding a <c>{cover,CoverSpec}</c> tuple to the <c>Opts</c> argument. Also, you can of course enable code coverage in your test specifications (read more in the chapter about diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml index 1dbb841fb0..c938851e0e 100644 --- a/lib/common_test/doc/src/ct_hooks_chapter.xml +++ b/lib/common_test/doc/src/ct_hooks_chapter.xml @@ -192,12 +192,12 @@ <section> <title>External configuration data and Logging</title> <p>It's possible in the CTH to read configuration data values - by calling <c>ct:get_config/1/2/3</c> (as explained in the + by calling <c><seealso marker="ct#get_config-1">ct:get_config/1/2/3</seealso></c> (as explained in the <seealso marker="config_file_chapter#require_config_data"> External configuration data</seealso> chapter). The config variables in question must, as always, first have been <c>required</c> by means of a suite-, group-, or test case info function, - or the <c>ct:require/1/2</c> function. Note that the latter can also be used + or the <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c> function. Note that the latter can also be used in CT hook functions.</p> <p>The CT hook functions may call any of the logging functions available in the <c>ct</c> interface to print information to the log files, or to @@ -453,7 +453,7 @@ terminate(State) -> <cell>Captures all test results and outputs them as surefire XML into a file. The file which is created is by default called junit_report.xml. The name can be by setting the path option for this hook. e.g. - <code>-ct_hooks cth_surefix [{path,"/tmp/report.xml"}]</code> + <code>-ct_hooks cth_surefire [{path,"/tmp/report.xml"}]</code> Surefire XML can forinstance be used by Jenkins to display test results.</cell> </row> diff --git a/lib/common_test/doc/src/ct_master_chapter.xml b/lib/common_test/doc/src/ct_master_chapter.xml index f4f0ecad62..21deed099d 100644 --- a/lib/common_test/doc/src/ct_master_chapter.xml +++ b/lib/common_test/doc/src/ct_master_chapter.xml @@ -124,7 +124,8 @@ <p><c>NodeRef = NodeAlias | node() | master</c></p> <p>A <c>NodeAlias</c> (<c>atom()</c>) is used in a test specification as a - reference to a node name (so the actual node name only needs to be declared once). + reference to a node name (so the actual node name only needs to be declared once, + which can of course also be achieved using constants). The alias is declared with a <c>node</c> term:</p> <p><c>{node, NodeAlias, NodeName}</c></p> @@ -141,30 +142,32 @@ CT Master:</p> <pre> - {node, node1, ct_node@host_x}. - {node, node2, ct_node@host_y}. - - {logdir, master, "/home/test/master_logs"}. - {logdir, "/home/test/logs"}. + {define, 'Top', "/home/test"}. + {define, 'T1', "'Top'/t1"}. + {define, 'T2', "'Top'/t2"}. + {define, 'T3', "'Top'/t3"}. + {define, 'CfgFile', "config.cfg"}. + {define, 'Node', ct_node}. + + {node, node1, 'Node@host_x'}. + {node, node2, 'Node@host_y'}. + + {logdir, master, "'Top'/master_logs"}. + {logdir, "'Top'/logs"}. - {config, node1, "/home/test/t1/cfg/config.cfg"}. - {config, node2, "/home/test/t2/cfg/config.cfg"}. - {config, "/home/test/t3/cfg/config.cfg"}. + {config, node1, "'T1'/'CfgFile'"}. + {config, node2, "'T2'/'CfgFile'"}. + {config, "'T3'/'CfgFile'"}. - {alias, t1, "/home/test/t1"}. - {alias, t2, "/home/test/t2"}. - {alias, t3, "/home/test/t3"}. + {suites, node1, 'T1', all}. + {skip_suites, node1, 'T1', [t1B_SUITE,t1D_SUITE], "Not implemented"}. + {skip_cases, node1, 'T1', t1A_SUITE, [test3,test4], "Irrelevant"}. + {skip_cases, node1, 'T1', t1C_SUITE, [test1], "Ignore"}. - {suites, node1, t1, all}. - {skip_suites, node1, t1, [t1B_SUITE,t1D_SUITE], "Not implemented"}. - {skip_cases, node1, t1, t1A_SUITE, [test3,test4], "Irrelevant"}. - {skip_cases, node1, t1, t1C_SUITE, [test1], "Ignore"}. + {suites, node2, 'T2', [t2B_SUITE,t2C_SUITE]}. + {cases, node2, 'T2', t2A_SUITE, [test4,test1,test7]}. - {suites, node2, t2, [t2B_SUITE,t2C_SUITE]}. - {cases, node2, t2, t2A_SUITE, [test4,test1,test7]}. - - {skip_suites, t3, all, "Not implemented"}. - </pre> + {skip_suites, 'T3', all, "Not implemented"}.</pre> <p>This example specifies the same tests as the original example. But now if started with a call to <c>ct_master:run(TestSpecName)</c>, the @@ -190,10 +193,6 @@ name as the Common Test node in question (typically <c>ct@somehost</c> if started with the <c>ct_run</c> program), will be performed. Tests without explicit node association will always be performed too of course!</p> - - <note><p>It is recommended that absolute paths are used for log directories, - config files and test directory aliases in the test specifications so that - current working directory settings are not important.</p></note> </section> <section> diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml index 078b9b958c..9cc5495af7 100644 --- a/lib/common_test/doc/src/ct_run.xml +++ b/lib/common_test/doc/src/ct_run.xml @@ -36,6 +36,8 @@ OS command line. </comsummary> + <marker id="top"></marker> + <description> <p>The <c>ct_run</c> program is automatically installed with Erlang/OTP and Common Test (please see the Installation chapter in the Common @@ -46,7 +48,7 @@ particular mode.</p> <p>There is an interface function that corresponds to this program, - called <c>ct:run_test/1</c>, for starting Common Test from the Erlang + called <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, for starting Common Test from the Erlang shell (or an Erlang program). Please see the <c>ct</c> man page for details.</p> @@ -72,6 +74,10 @@ following <c>-erl_args</c> on the command line. These directories are added to the code path normally (i.e. on specified form)</p> + <p>Exit status is set before the program ends. Value <c>0</c> indicates a successful + test result, <c>1</c> indicates one or more failed or auto-skipped test cases, and + <c>2</c> indicates test execution failure.</p> + <p>If <c>ct_run</c> is called with option:</p> <pre>-help</pre> <p>it prints all valid start flags to stdout.</p> @@ -88,11 +94,13 @@ [-step [config | keep_inactive]] [-config ConfigFile1 ConfigFile2 .. ConfigFileN] [-userconfig CallbackModule1 ConfigString1 and CallbackModule2 - ConfigString2 and .. and CallbackModuleN ConfigStringN] + ConfigString2 and .. CallbackModuleN ConfigStringN] [-decrypt_key Key] | [-decrypt_file KeyFile] [-label Label] [-logdir LogDir] [-logopts LogOpts] + [-verbosity GenVLevel | [Category1 VLevel1 and + Category2 VLevel2 and .. CategoryN VLevelN]] [-silent_connections [ConnType1 ConnType2 .. ConnTypeN]] [-stylesheet CSSFile] [-cover CoverCfgFile] @@ -107,7 +115,11 @@ [-repeat N [-force_stop]] | [-duration HHMMSS [-force_stop]] | [-until [YYMoMoDD]HHMMSS [-force_stop]] - [-basic_html]</pre> + [-basic_html] + [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and .. + CTHModuleN CTHOptsN] + [-exit_status ignore_config] + </pre> </section> <section> <title>Run tests using test specification</title> @@ -120,6 +132,8 @@ [-label Label] [-logdir LogDir] [-logopts LogOpts] + [-verbosity GenVLevel | [Category1 VLevel1 and + Category2 VLevel2 and .. CategoryN VLevelN]] [-allow_user_terms] [-silent_connections [ConnType1 ConnType2 .. ConnTypeN]] [-stylesheet CSSFile] @@ -135,7 +149,11 @@ [-repeat N [-force_stop]] | [-duration HHMMSS [-force_stop]] | [-until [YYMoMoDD]HHMMSS [-force_stop]] - [-basic_html]</pre> + [-basic_html] + [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and .. + CTHModuleN CTHOptsN] + [-exit_status ignore_config] + </pre> </section> <section> <title>Run tests in web based GUI</title> @@ -147,6 +165,8 @@ [-userconfig CallbackModule1 ConfigString1 and CallbackModule2 ConfigString2 and .. and CallbackModuleN ConfigStringN] [-logopts LogOpts] + [-verbosity GenVLevel | [Category1 VLevel1 and + Category2 VLevel2 and .. CategoryN VLevelN]] [-decrypt_key Key] | [-decrypt_file KeyFile] [-include InclDir1 InclDir2 .. InclDirN] [-no_auto_compile] diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml index a5886b9687..b95a18e47e 100644 --- a/lib/common_test/doc/src/event_handler_chapter.xml +++ b/lib/common_test/doc/src/event_handler_chapter.xml @@ -64,7 +64,7 @@ <marker id="usage"></marker> <title>Usage</title> <p>Event handlers may be installed by means of an <c>event_handler</c> - start flag (<c>ct_run</c>) or option (<c>ct:run_test/1</c>), where the + start flag (<c>ct_run</c>) or option (<c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>), where the argument specifies the names of one or more event handler modules. Example:</p> <p><c>$ ct_run -suite test/my_SUITE -event_handler handlers/my_evh1 @@ -78,7 +78,7 @@ example).</p> <p>An event_handler tuple in the argument <c>Opts</c> has the following - definition (see also <c>ct:run_test/1</c> in the reference manual):</p> + definition (see also <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> in the reference manual):</p> <pre> {event_handler,EventHandlers} @@ -205,7 +205,7 @@ {error,{RunTimeError,StackTrace}} | {timetrap_timeout,integer()} | {failed,{Suite,end_per_testcase,FailInfo}}</c>, reason for failure.</p> - <p><c>RequireInfo = {not_available,atom()}</c>, why require has failed.</p> + <p><c>RequireInfo = {not_available,atom() | tuple()}</c>, why require has failed.</p> <p><c>FailInfo = {timetrap_timeout,integer()} | {RunTimeError,StackTrace} | UserTerm</c>, @@ -233,7 +233,7 @@ reason for auto skipping <c>Func</c>.</p> <p><c>FailReason = {Suite,ConfigFunc,FailInfo}} | {Suite,FailedCaseInSequence}</c>, reason for failure.</p> - <p><c>RequireInfo = {not_available,atom()}</c>, why require has failed.</p> + <p><c>RequireInfo = {not_available,atom() | tuple()}</c>, why require has failed.</p> <p><c>ConfigFunc = init_per_suite | init_per_group</c></p> <p><c>FailInfo = {timetrap_timeout,integer()} | {RunTimeError,StackTrace} | @@ -308,7 +308,7 @@ manager can look like.</p> <note><p>To ensure that printouts to standard out (or printouts made with - <c>ct:log/2/3</c> or <c>ct:pal/2/3</c>) get written to the test case log + <c><seealso marker="ct#log-2">ct:log/2/3</seealso></c> or <c><seealso marker="ct:pal-2">ct:pal/2/3</seealso></c>) get written to the test case log file, and not to the Common Test framework log, you can syncronize with the Common Test server by matching on the <c>tc_start</c> and <c>tc_done</c> events. In the period between these events, all IO gets directed to the diff --git a/lib/common_test/doc/src/example_chapter.xml b/lib/common_test/doc/src/example_chapter.xml index f269dba2cd..3bba7bd783 100644 --- a/lib/common_test/doc/src/example_chapter.xml +++ b/lib/common_test/doc/src/example_chapter.xml @@ -28,7 +28,8 @@ <rev></rev> <file>example_chapter.xml</file> </header> - + + <marker id="top"></marker> <section> <title>Test suite example</title> diff --git a/lib/common_test/doc/src/getting_started_chapter.xml b/lib/common_test/doc/src/getting_started_chapter.xml index 039578dd2e..3cf04bb1a2 100644 --- a/lib/common_test/doc/src/getting_started_chapter.xml +++ b/lib/common_test/doc/src/getting_started_chapter.xml @@ -90,7 +90,7 @@ <p>As you can understand from the illustration above, Common Test requires that a test case generates a runtime error to indicate failure (e.g. by causing a bad match error or by calling <c>exit/1</c>, preferrably - through the <c>ct:fail/1,2</c> help function). A succesful execution is + through the <c><seealso marker="ct#fail-1">ct:fail/1,2</seealso></c> help function). A succesful execution is indicated by means of a normal return from the test case function. </p> </section> @@ -214,11 +214,56 @@ <section> <title>What happens next?</title> - <p> - You will find detailed information about the basics introduced here in this - chapter in the following chapters in the User's Guide, as well as - presentations of many more useful features. Have fun! - </p> + + <p>Well, you might already be asking yourself questions such as:</p> + + <list> + <item>"How and where can I specify variable data for my tests that mustn't + be hard-coded in the test suites (such as host names, addresses, + user login data, etc)?" The + <seealso marker="config_file_chapter#top">External Configuration Data</seealso> + chapter will give you that information. + </item> + <item>"Is there a way to declare a number of different tests and run them + in one session without having to write my own scripts? And can such + declarations be used for regression testing?" The + <seealso marker="run_test_chapter#test_specifications">Test Specifications</seealso> + chapter answers these questions. + </item> + <item>"Can test cases and/or test runs be automatically repeated?" Learn more about + <seealso marker="write_test_chapter#test_case_groups">Test Case Groups</seealso> + and also read about start flags/options in the + <seealso marker="run_test_chapter#ct_run">Running Tests</seealso> chapter and + the Reference Manual. + </item> + <item>"Will Common Test execute my test cases in sequence or in parallel?" The + <seealso marker="write_test_chapter#test_case_groups">Test Case Groups</seealso> + section in the Running Tests chapter will give you the answer. + </item> + <item>"What's the syntax for timetraps (mentioned above), and how do I set them?" + This is explained in the + <seealso marker="write_test_chapter#timetraps">Timetrap Timeouts</seealso> + part of the Writing Test Suites chapter. + </item> + <item>"What functions are available for logging and printing?" Check the + <seealso marker="write_test_chapter#logging">Logging</seealso> + section in the Writing Test Suites chapter. + </item> + <item>"I need data files for my tests. Where do I store them preferrably?" + You should read about + <seealso marker="write_test_chapter#data_priv_dir">Data and Private + Directories</seealso> for information about this. + </item> + <item>"May I start with a test suite example, please?" + <seealso marker="example_chapter#top">Sure!</seealso> + </item> + </list> + <p>You will probably want to get started on your own first test suites now, while + at the same time digging deeper into the Common Test User's Guide and Reference Manual. + You will find that there's lots more to learn about the things that have been introduced + in this chapter. You will of course also be presented many more useful features, such as the + ones listed above. Have fun! + </p> </section> </chapter> diff --git a/lib/common_test/doc/src/ref_man.xml b/lib/common_test/doc/src/ref_man.xml index a9fdef7359..6fede88434 100644 --- a/lib/common_test/doc/src/ref_man.xml +++ b/lib/common_test/doc/src/ref_man.xml @@ -4,7 +4,7 @@ <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2003</year><year>2011</year> + <year>2003</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -71,6 +71,7 @@ <xi:include href="ct_cover.xml"/> <xi:include href="ct_ftp.xml"/> <xi:include href="ct_ssh.xml"/> + <xi:include href="ct_netconfc.xml"/> <xi:include href="ct_rpc.xml"/> <xi:include href="ct_snmp.xml"/> <xi:include href="ct_telnet.xml"/> diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index 30486d3eec..ea62df27cc 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -178,6 +178,8 @@ <item><c><![CDATA[-basic_html]]></c>, switches off html enhancements that might not be compatible with older browsers.</item> <item><c><![CDATA[-logopts <opts>]]></c>, makes it possible to modify aspects of the logging behaviour, see <seealso marker="run_test_chapter#logopts">Log options</seealso> below.</item> + <item><c><![CDATA[-verbosity <levels>]]></c>, sets <seealso marker="write_test_chapter#logging">verbosity levels + for printouts</seealso>.</item> </list> <note><p>Directories passed to Common Test may have either relative or absolute paths.</p></note> @@ -196,60 +198,73 @@ the current working directory of the Erlang Runtime System during the test run!</p> </note> - <p>For more information about the <c>ct_run</c> program, see the - <seealso marker="install_chapter#general">Installation</seealso> chapter. - </p> - </section> - - <section> - <title>Running tests from the Web based GUI</title> - - <p>The web based GUI, VTS, is started with the <c>ct_run</c> - program. From the GUI you can load config files, and select - directories, suites and cases to run. You can also state the - config files, directories, suites and cases on the command line - when starting the web based GUI. - </p> - + <p>The <c>ct_run</c> program sets the exit status before shutting down. The following values + are defined:</p> <list> - <item><c>ct_run -vts</c></item> - <item><c><![CDATA[ct_run -vts -config <configfilename>]]></c></item> - <item><c><![CDATA[ct_run -vts -config <configfilename> -suite <suitewithfullpath> - -case <casename>]]></c></item> + <item><c>0</c> indicates a successful testrun, i.e. one without failed or auto-skipped test cases.</item> + <item><c>1</c> indicates that one or more test cases have failed, or have been auto-skipped.</item> + <item><c>2</c> indicates that the test execution has failed because of e.g. compilation errors, an + illegal return value from an info function, etc.</item> </list> + <p>If auto-skipped test cases should not affect the exit status, you may change the default + behaviour using start flag:</p> + <pre>-exit_status ignore_config</pre> - <p>From the GUI you can run tests and view the result and the logs. + <p>For more information about the <c>ct_run</c> program, see the + <seealso marker="ct_run#top">Reference Manual</seealso> and the + <seealso marker="install_chapter#general">Installation</seealso> chapter. </p> - - <p>Note that <c>ct_run -vts</c> will try to open the Common Test start - page in an existing web browser window or start the browser if it is - not running. Which browser should be started may be specified with - the browser start command option:</p> - <p><c><![CDATA[ct_run -vts -browser <browser_start_cmd>]]></c></p> - <p>Example:</p> - <p><c><![CDATA[$ ct_run -vts -browser 'firefox&']]></c></p> - <p>Note that the browser must run as a separate OS process or VTS will hang!</p> - <p>If no specific browser start command is specified, Firefox will - be the default browser on Unix platforms and Internet Explorer on Windows. - If Common Test fails to start a browser automatically, or <c>'none'</c> is - specified as the value for -browser (i.e. <c>-browser none</c>), start your - favourite browser manually and type in the URL that Common Test - displays in the shell.</p> </section> - + <section> <title>Running tests from the Erlang shell or from an Erlang program</title> <p>Common Test provides an Erlang API for running tests. The main (and most flexible) function for specifying and executing tests is called - <c>ct:run_test/1</c>. This function takes the same start parameters as - the <c>ct_run</c> program described above, only the flags are instead + <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>. + This function takes the same start parameters as + the <c><seealso marker="run_test_chapter#ct_run">ct_run</seealso></c> + program described above, only the flags are instead given as options in a list of key-value tuples. E.g. a test specified with <c>ct_run</c> like:</p> + <p><c>$ ct_run -suite ./my_SUITE -logdir ./results</c></p> - <p>is with <c>ct:run_test/1</c> specified as:</p> + <p>is with <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> specified as:</p> <p><c>1> ct:run_test([{suite,"./my_SUITE"},{logdir,"./results"}]).</c></p> - <p>For detailed documentation, please see the <c>ct</c> manual page.</p> + + <p>The function returns the test result, represented by the tuple: + <c>{Ok,Failed,{UserSkipped,AutoSkipped}}</c>, where each element is an + integer. If test execution fails, the function returns the tuple: + <c>{error,Reason}</c>, where the term <c>Reason</c> explains the + failure.</p> + + <section> + <title>Releasing the Erlang shell</title> + <p>During execution of tests, started with + <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, + the Erlang shell process, controlling stdin, will remain the top + level process of the Common Test system of processes. The result + is that the Erlang shell is not available for interaction during + the test run. If this is not desirable, maybe because the shell is needed + for debugging purposes or for interaction with the SUT during test + execution, you may set the <c>release_shell</c> start option to + <c>true</c> (in the call to <c>ct:run_test/1</c> or by + using the corresponding test specification term, see below). This will + make Common Test release the shell immediately after the test suite + compilation stage. To accomplish this, a test runner process + is spawned to take control of the test execution, and the effect is that + <c>ct:run_test/1</c> returns the pid of this process rather than the + test result - which instead is printed to tty at the end of the test run.</p> + <note><p>Note that in order to use the + <c><seealso marker="ct#break-1">ct:break/1/2</seealso></c> and + <c><seealso marker="ct#continue-0">ct:continue/0/1</seealso></c> functions, + <c>release_shell</c> <em>must</em> be set to <c>true</c>.</p></note> + </section> + + <p>For detailed documentation about + <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, + please see the + <c><seealso marker="ct#run_test-1">ct</seealso></c> manual page.</p> </section> <section> @@ -266,9 +281,9 @@ for trying out various operations during test suite development.</p> <p>To invoke the interactive shell mode, you can start an Erlang shell - manually and call <c>ct:install/1</c> to install any configuration + manually and call <c><seealso marker="ct#install-1">ct:install/1</seealso></c> to install any configuration data you might need (use <c>[]</c> as argument otherwise), then - call <c>ct:start_interactive/0</c> to start Common Test. If you use + call <c><seealso marker="ct#start_interactive-0">ct:start_interactive/0</seealso></c> to start Common Test. If you use the <c>ct_run</c> program, you may start the Erlang shell and Common Test in the same go by using the <c>-shell</c> and, optionally, the <c>-config</c> and/or <c>-userconfig</c> flag. Examples: @@ -287,7 +302,8 @@ <p>If any functions using "required config data" (e.g. ct_telnet or ct_ftp functions) are to be called from the erlang shell, config - data must first be required with <c>ct:require/[1,2]</c>. This is + data must first be required with <c><seealso marker="ct#require-1"> + ct:require/1/2</seealso></c>. This is equivalent to a <c>require</c> statement in the <seealso marker="write_test_chapter#suite">Test Suite Info Function</seealso> or in the <seealso @@ -314,11 +330,11 @@ is not supported.</p> <p>If you wish to exit the interactive mode (e.g. to start an - automated test run with <c>ct:run_test/1</c>), call the function - <c>ct:stop_interactive/0</c>. This shuts down the + automated test run with <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>), call the function + <c><seealso marker="ct#stop_interactive-0">ct:stop_interactive/0</seealso></c>. This shuts down the running <c>ct</c> application. Associations between configuration names and data created with <c>require</c> are - consequently deleted. <c>ct:start_interactive/0</c> will get you + consequently deleted. <c><seealso marker="ct#start_interactive-0">ct:start_interactive/0</seealso></c> will get you back into interactive mode, but the previous state is not restored.</p> </section> @@ -326,7 +342,7 @@ <title>Step by step execution of test cases with the Erlang Debugger</title> <p>By means of <c>ct_run -step [opts]</c>, or by passing the - <c>{step,Opts}</c> option to <c>ct:run_test/1</c>, it is possible + <c>{step,Opts}</c> option to <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, it is possible to get the Erlang Debugger started automatically and use its graphical interface to investigate the state of the current test case and to execute it step by step and/or set execution breakpoints.</p> @@ -352,31 +368,34 @@ <marker id="test_specifications"></marker> <section> - <title>Using test specifications</title> + <title>Test Specifications</title> <p>The most flexible way to specify what to test, is to use a so called test specification. A test specification is a sequence of - Erlang terms. The terms may be declared in a text file or passed - to the test server at runtime as a list - (see <c>run_testspec/1</c> in the manual page - for <c>ct</c>). There are two general types of terms: - configuration terms and test specification terms.</p> + Erlang terms. The terms are normally declared in a text file (see + <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>), but + may also be passed to Common Test on the form of a list (see + <c><seealso marker="ct#run_testspec-1">ct:run_testspec/1</seealso></c>). + There are two general types of terms: configuration terms and test + specification terms.</p> <p>With configuration terms it is possible to e.g. label the test run (similar to <c>ct_run -label</c>), evaluate arbitrary expressions - before starting a test, import configuration - data (similar to - <c>ct_run -config/-userconfig</c>), specify HTML log directories (similar - to - <c>ct_run -logdir</c>), give aliases to test nodes and test - directories (to make a specification easier to read and - maintain), enable code coverage analysis (see - the <seealso marker="cover_chapter#cover">Code Coverage - Analysis</seealso> chapter) and specify event_handler plugins - (see the <seealso marker="event_handler_chapter#event_handling"> - Event Handling</seealso> chapter). There is also a term for - specifying include directories that should be passed on to the - compiler when automatic compilation is performed (similar - to <c>ct_run -include</c>, see above).</p> + before starting the test, import configuration data (similar to + <c>ct_run -config/-userconfig</c>), specify the top level HTML log + directory (similar to <c>ct_run -logdir</c>), enable code coverage + analysis (similar to <c>ct_run -cover</c>), install Common Test Hooks + (similar to <c>ct_run -ch_hooks</c>), install event_handler plugins + (similar to <c>ct_run -event_handler</c>), specify include directories + that should be passed to the compiler for automatic compilation + (similar to <c>ct_run -include</c>), disable the auto compilation + feature (similar to <c>ct_run -no_auto_compile</c>), set verbosity + levels (similar to <c>ct_run -verbosity</c>), and more.</p> + <p>Configuration terms can be combined with <c>ct_run</c> start flags, + or <c>ct:run_test/1</c> options. The result will for some flags/options + and terms be that the values are merged (e.g. configuration files, + include directories, verbosity levels, silent connections), and for + others that the start flags/options override the test specification + terms (e.g. log directory, label, style sheet, auto compilation).</p> <p>With test specification terms it is possible to state exactly which tests should run and in which order. A test term specifies either one or more suites, one or more test case groups, or one @@ -391,11 +410,12 @@ S, is a test of all cases in S. However, if a term specifying test case X and Y in S is merged with a term specifying case Z in S, the result is a test of X, Y and Z in S. To disable this - behaviour, it is possible in test specification to set the - <c>merge_tests</c> term to <c>false</c>.</p> + behaviour, i.e. to instead perform each test sequentially in a "script-like" + manner, the term <c>merge_tests</c> can be set to <c>false</c> in + the test specification.</p> <p>A test term can also specify one or more test suites, groups, or test cases to be skipped. Skipped suites, groups and cases - are not executed and show up in the HTML test log files as + are not executed and show up in the HTML log files as SKIPPED.</p> <p>When a test case group is specified, the resulting test executes the @@ -425,15 +445,30 @@ <p>Below is the test specification syntax. Test specifications can be used to run tests both in a single test host environment and in a distributed Common Test environment (Large Scale - Testing). The node parameters in the init term are only + Testing). The node parameters in the <c>init</c> term are only relevant in the latter (see the <seealso marker="ct_master_chapter#test_specifications">Large - Scale Testing</seealso> chapter for information). For details on - the event_handler term, see the + Scale Testing</seealso> chapter for information). For more information + about the various terms, please see the corresponding sections in the + User's Guide, such as e.g. the + <seealso marker="run_test_chapter#ct_run"><c>ct_run</c> + program</seealso> for an overview of available start flags + (since most flags have a corresponding configuration term), and + more detailed explanation of e.g. + <seealso marker="write_test_chapter#logging">Logging</seealso> + (for the <c>verbosity</c>, <c>stylesheet</c> and <c>basic_html</c> terms), + <seealso marker="config_file_chapter#top">External Configuration Data</seealso> + (for the <c>config</c> and <c>userconfig</c> terms), <seealso marker="event_handler_chapter#event_handling">Event - Handling</seealso> chapter.</p> + Handling</seealso> (for the <c>event_handler</c> term), + <seealso marker="ct_hooks_chapter#installing">Common Test Hooks</seealso> + (for the <c>ct_hooks</c> term), etc.</p> <p>Config terms:</p> <pre> + {merge_tests, Bool}. + + {define, Constant, Value}. + {node, NodeAlias, Node}. {init, InitOptions}. @@ -442,6 +477,15 @@ {label, Label}. {label, NodeRefs, Label}. + {verbosity, VerbosityLevels}. + {verbosity, NodeRefs, VerbosityLevels}. + + {stylesheet, CSSFile}. + {stylesheet, NodeRefs, CSSFile}. + + {silent_connections, ConnTypes}. + {silent_connections, NodeRefs, ConnTypes}. + {multiply_timetraps, N}. {multiply_timetraps, NodeRefs, N}. @@ -454,19 +498,23 @@ {include, IncludeDirs}. {include, NodeRefs, IncludeDirs}. + {auto_compile, Bool}, + {auto_compile, NodeRefs, Bool}, + {config, ConfigFiles}. + {config, ConfigDir, ConfigBaseNames}. {config, NodeRefs, ConfigFiles}. + {config, NodeRefs, ConfigDir, ConfigBaseNames}. {userconfig, {CallbackModule, ConfigStrings}}. {userconfig, NodeRefs, {CallbackModule, ConfigStrings}}. - {alias, DirAlias, Dir}. - - {merge_tests, Bool}. - {logdir, LogDir}. {logdir, NodeRefs, LogDir}. + {logopts, LogOpts}. + {logopts, NodeRefs, LogOpts}. + {create_priv_dir, PrivDirOption}. {create_priv_dir, NodeRefs, PrivDirOption}. @@ -479,83 +527,176 @@ {ct_hooks, NodeRefs, CTHModules}. {enable_builtin_hooks, Bool}. - </pre> + + {basic_html, Bool}. + {basic_html, NodeRefs, Bool}. + + {release_shell, Bool}.</pre> + <p>Test terms:</p> <pre> - {suites, DirRef, Suites}. - {suites, NodeRefs, DirRef, Suites}. + {suites, Dir, Suites}. + {suites, NodeRefs, Dir, Suites}. - {groups, DirRef, Suite, Groups}. - {groups, NodeRefsDirRef, Suite, Groups}. + {groups, Dir, Suite, Groups}. + {groups, NodeRefs, Dir, Suite, Groups}. - {groups, DirRef, Suite, GroupSpec, {cases,Cases}}. - {groups, NodeRefsDirRef, Suite, GroupSpec, {cases,Cases}}. + {groups, Dir, Suite, GroupSpec, {cases,Cases}}. + {groups, NodeRefs, Dir, Suite, GroupSpec, {cases,Cases}}. - {cases, DirRef, Suite, Cases}. - {cases, NodeRefs, DirRef, Suite, Cases}. + {cases, Dir, Suite, Cases}. + {cases, NodeRefs, Dir, Suite, Cases}. - {skip_suites, DirRef, Suites, Comment}. - {skip_suites, NodeRefs, DirRef, Suites, Comment}. + {skip_suites, Dir, Suites, Comment}. + {skip_suites, NodeRefs, Dir, Suites, Comment}. - {skip_groups, DirRef, Suite, GroupNames, Comment}. - {skip_groups, NodeRefs, DirRef, Suite, GroupNames, Comment}. + {skip_groups, Dir, Suite, GroupNames, Comment}. + {skip_groups, NodeRefs, Dir, Suite, GroupNames, Comment}. - {skip_cases, DirRef, Suite, Cases, Comment}. - {skip_cases, NodeRefs, DirRef, Suite, Cases, Comment}. - </pre> + {skip_cases, Dir, Suite, Cases, Comment}. + {skip_cases, NodeRefs, Dir, Suite, Cases, Comment}.</pre> + <p>Types:</p> <pre> - NodeAlias = atom() - InitOptions = term() - Node = node() - NodeRef = NodeAlias | Node | master - NodeRefs = all_nodes | [NodeRef] | NodeRef - N = integer() - Bool = true | false - CoverSpecFile = string() - IncludeDirs = string() | [string()] - ConfigFiles = string() | [string()] - DirAlias = atom() - Dir = string() - LogDir = string() - PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc - EventHandlers = atom() | [atom()] - InitArgs = [term()] - CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}] - CTHModule = atom() - CTHInitArgs = term() - DirRef = DirAlias | Dir - Suites = atom() | [atom()] | all - Suite = atom() - Groups = GroupSpec | [GroupSpec] | all - GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec} - GroupName = atom() - GroupNames = GroupName | [GroupName] - Cases = atom() | [atom()] | all - Comment = string() | "" - </pre> - <p>Example:</p> + Bool = true | false + Constant = atom() + Value = term() + NodeAlias = atom() + Node = node() + NodeRef = NodeAlias | Node | master + NodeRefs = all_nodes | [NodeRef] | NodeRef + InitOptions = term() + Label = atom() | string() + VerbosityLevels = integer() | [{Category,integer()}] + Category = atom() + CSSFile = string() + ConnTypes = all | [atom()] + N = integer() + CoverSpecFile = string() + IncludeDirs = string() | [string()] + ConfigFiles = string() | [string()] + ConfigDir = string() + ConfigBaseNames = string() | [string()] + CallbackModule = atom() + ConfigStrings = string() | [string()] + LogDir = string() + LogOpts = [term()] + PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc + EventHandlers = atom() | [atom()] + InitArgs = [term()] + CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}] + CTHModule = atom() + CTHInitArgs = term() + Dir = string() + Suites = atom() | [atom()] | all + Suite = atom() + Groups = GroupSpec | [GroupSpec] | all + GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec} + GroupName = atom() + GroupNames = GroupName | [GroupName] + Cases = atom() | [atom()] | all + Comment = string() | ""</pre> + + <p>The difference between the <c>config</c> terms above, is that with + <c>ConfigDir</c>, <c>ConfigBaseNames</c> is a list of base names, + i.e. without directory paths. <c>ConfigFiles</c> must be full names, + including paths. E.g, these two terms have the same meaning:</p> <pre> - {logdir, "/home/test/logs"}. + {config, ["/home/testuser/tests/config/nodeA.cfg", + "/home/testuser/tests/config/nodeB.cfg"]}. + + {config, "/home/testuser/tests/config", ["nodeA.cfg","nodeB.cfg"]}.</pre> + + <note><p>Any relative paths specified in the test specification, will be + relative to the directory which contains the test specification file, if + <c>ct_run -spec TestSpecFile ...</c> or + <c>ct:run:test([{spec,TestSpecFile},...])</c> + executes the test. The path will be relative to the top level log directory, if + <c>ct:run:testspec(TestSpec)</c> executes the test.</p></note> - {config, "/home/test/t1/cfg/config.cfg"}. - {config, "/home/test/t2/cfg/config.cfg"}. - {config, "/home/test/t3/cfg/config.cfg"}. + <p>The <c>define</c> term introduces a constant, which is used to + replace the name <c>Constant</c> with <c>Value</c>, wherever it's found in + the test specification. This replacement happens during an initial iteration + through the test specification. Constants may be used anywhere in the test + specification, e.g. in arbitrary lists and tuples, and even in strings + and inside the value part of other constant definitions! A constant can + also be part of a node name, but that is the only place where a constant + can be part of an atom.</p> + + <note><p>For the sake of readability, the name of the constant must always + begin with an upper case letter, or a <c>$</c>, <c>?</c>, or <c>_</c>. + This also means that it must always be single quoted (obviously, since + the constant name is actually an atom, not text).</p></note> + + <p>The main benefit of constants is that they can be used to reduce the size + (and avoid repetition) of long strings, such as file paths. Compare these + terms:</p> + + <pre> + %% 1a. no constant + {config, "/home/testuser/tests/config", ["nodeA.cfg","nodeB.cfg"]}. + {suites, "/home/testuser/tests/suites", all}. - {alias, t1, "/home/test/t1"}. - {alias, t2, "/home/test/t2"}. - {alias, t3, "/home/test/t3"}. + %% 1b. with constant + {define, 'TESTDIR', "/home/testuser/tests"}. + {config, "'TESTDIR'/config", ["nodeA.cfg","nodeB.cfg"]}. + {suites, "'TESTDIR'/suites", all}. + + %% 2a. no constants + {config, [testnode@host1, testnode@host2], "../config", ["nodeA.cfg","nodeB.cfg"]}. + {suites, [testnode@host1, testnode@host2], "../suites", [x_SUITE, y_SUITE]}. + + %% 2b. with constants + {define, 'NODE', testnode}. + {define, 'NODES', ['NODE'@host1, 'NODE'@host2]}. + {config, 'NODES', "../config", ["nodeA.cfg","nodeB.cfg"]}. + {suites, 'NODES', "../suites", [x_SUITE, y_SUITE]}.</pre> + + <p>Constants make the test specification term <c>alias</c>, in previous + versions of Common Test, redundant. This term has been deprecated but will + remain supported in upcoming Common Test releases. Replacing <c>alias</c> + terms with <c>define</c> is strongly recommended though! Here's an example + of such a replacement:</p> + + <pre> + %% using the old alias term + {config, "/home/testuser/tests/config/nodeA.cfg"}. + {alias, suite_dir, "/home/testuser/tests/suites"}. + {groups, suite_dir, x_SUITE, group1}. + + %% replacing with constants + {define, 'TestDir', "/home/testuser/tests"}. + {define, 'CfgDir', "'TestDir'/config"}. + {define, 'SuiteDir', "'TestDir'/suites"}. + {config, 'CfgDir', "nodeA.cfg"}. + {groups, 'SuiteDir', x_SUITE, group1}.</pre> + + <p>Actually, constants could well replace the <c>node</c> term too, but + this still has declarative value, mainly when used in combination + with <c>NodeRefs == all_nodes</c> (see types above).</p> + + <p>Here follows a simple test specification example:</p> + <pre> + {define, 'Top', "/home/test"}. + {define, 'T1', "'Top'/t1"}. + {define, 'T2', "'Top'/t2"}. + {define, 'T3', "'Top'/t3"}. + {define, 'CfgFile', "config.cfg"}. + + {logdir, "'Top'/logs"}. - {suites, t1, all}. - {skip_suites, t1, [t1B_SUITE,t1D_SUITE], "Not implemented"}. - {skip_cases, t1, t1A_SUITE, [test3,test4], "Irrelevant"}. - {skip_cases, t1, t1C_SUITE, [test1], "Ignore"}. + {config, ["'T1'/'CfgFile'", "'T2'/'CfgFile'", "'T3'/'CfgFile'"]}. - {suites, t2, [t2B_SUITE,t2C_SUITE]}. - {cases, t2, t2A_SUITE, [test4,test1,test7]}. + {suites, 'T1', all}. + {skip_suites, 'T1', [t1B_SUITE,t1D_SUITE], "Not implemented"}. + {skip_cases, 'T1', t1A_SUITE, [test3,test4], "Irrelevant"}. + {skip_cases, 'T1', t1C_SUITE, [test1], "Ignore"}. - {skip_suites, t3, all, "Not implemented"}. - </pre> + {suites, 'T2', [t2B_SUITE,t2C_SUITE]}. + {cases, 'T2', t2A_SUITE, [test4,test1,test7]}. + + {skip_suites, 'T3', all, "Not implemented"}.</pre> + <p>The example specifies the following:</p> <list> <item>The specified logdir directory will be used for storing @@ -563,8 +704,6 @@ date and time).</item> <item>The variables in the specified test system config files will be imported for the test.</item> - <item>Aliases are given for three test system directories. The suites in - this example are stored in "/home/test/tX/test".</item> <item>The first test to run includes all suites for system t1. Excluded from the test are however the t1B and t1D suites. Also test cases test3 and test4 in t1A as well as the test1 case in t1C are excluded from @@ -575,9 +714,9 @@ <item>Lastly, all suites for systems t3 are to be completely skipped and this should be explicitly noted in the log files.</item> </list> - <p>It is possible to specify initialization options for nodes defined in the - test specification. Currently, there are options to start the node and/or to - evaluate any function on the node. + <p>With the <c>init</c> term it's possible to specify initialization options + for nodes defined in the test specification. Currently, there are options + to start the node and/or to evaluate any function on the node. See the <seealso marker="ct_master_chapter#ct_slave">Automatic startup of the test target nodes</seealso> chapter for details.</p> <p>It is possible for the user to provide a test specification that @@ -586,11 +725,49 @@ <c>ct_run</c>. This forces Common Test to ignore unrecognizable terms. Note that in this mode, Common Test is not able to check the specification for errors as efficiently as if the scanner runs in default mode. - If <c>ct:run_test/1</c> is used for starting the tests, the relaxed scanner + If <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> is used for starting the tests, the relaxed scanner mode is enabled by means of the tuple: <c>{allow_user_terms,true}</c></p> </section> + + <section> + <title>Running tests from the Web based GUI</title> + + <p>The web based GUI, VTS, is started with the + <c><seealso marker="run_test_chapter#ct_run">ct_run</seealso></c> + program. From the GUI you can load config files, and select + directories, suites and cases to run. You can also state the + config files, directories, suites and cases on the command line + when starting the web based GUI. + </p> + + <list> + <item><c>ct_run -vts</c></item> + <item><c><![CDATA[ct_run -vts -config <configfilename>]]></c></item> + <item><c><![CDATA[ct_run -vts -config <configfilename> -suite <suitewithfullpath> + -case <casename>]]></c></item> + </list> + + <p>From the GUI you can run tests and view the result and the logs. + </p> + + <p>Note that <c>ct_run -vts</c> will try to open the Common Test start + page in an existing web browser window or start the browser if it is + not running. Which browser should be started may be specified with + the browser start command option:</p> + <p><c><![CDATA[ct_run -vts -browser <browser_start_cmd>]]></c></p> + <p>Example:</p> + <p><c><![CDATA[$ ct_run -vts -browser 'firefox&']]></c></p> + <p>Note that the browser must run as a separate OS process or VTS will hang!</p> + <p>If no specific browser start command is specified, Firefox will + be the default browser on Unix platforms and Internet Explorer on Windows. + If Common Test fails to start a browser automatically, or <c>'none'</c> is + specified as the value for -browser (i.e. <c>-browser none</c>), start your + favourite browser manually and type in the URL that Common Test + displays in the shell.</p> + </section> <section> + <marker id="log_files"></marker> <title>Log files</title> <p>As the execution of the test suites proceed, events are logged in @@ -718,17 +895,30 @@ <p>instead of each <c>x</c> printed on a new line, which is the default behaviour.</p> </section> + <section> + <marker id="table_sorting"></marker> + <title>Sorting HTML table columns</title> + <p>By clicking the name in the column header of any table (e.g. "Ok", "Case", "Time", etc), + the table rows are sorted in whatever order makes sense for the type of value (e.g. + numerical for "Ok" or "Time", and alphabetical for "Case"). The sorting is performed + by means of JavaScript code, automatically inserted into the HTML log files. Common Test + uses the <url href="http://jquery.com">jQuery</url> library and the + <url href="http://tablesorter.com">tablesorter</url> plugin, with customized sorting + functions, for this implementation.</p> + </section> </section> <section> <marker id="html_stylesheet"></marker> <title>HTML Style Sheets</title> - <p>Common Test uses a CSS file to control the look of the HTML - files generated during test runs. If, for some reason, the - log files are not displayed correctly in the HTML browser of your - choice, or you prefer the "pre Common Test v1.6 look" - of the log files (i.e. not using CSS), use the start flag/option - <c>basic_html</c> to revert to the old style.</p> + <p>Common Test uses an HTML Style Sheet (CSS file) to control the look of + the HTML log files generated during test runs. If, for some reason, the + log files are not displayed correctly in the browser of your + choice, or you prefer a more primitive ("pre Common Test v1.6") look + of the logs, use the start flag/option:</p> + <pre>basic_html</pre> + <p>This disables the use of Style Sheets, as well as JavaScripts (see + table sorting above).</p> <p>Common Test includes an <em>optional</em> feature to allow user HTML style sheets for customizing printouts. The @@ -881,75 +1071,82 @@ <section> <marker id="silent_connections"></marker> <title>Silent Connections</title> - <p>The protocol handling processes in Common Test, implemented by ct_telnet, ct_ftp etc, - do verbose printing to the test case logs. This can be switched off by means - of the <c>-silent_connections</c> flag:</p> + <p>The protocol handling processes in Common Test, implemented by ct_telnet, + ct_ssh, ct_ftp etc, do verbose printing to the test case logs. This can be switched off + by means of the <c>-silent_connections</c> flag:</p> <pre> ct_run -silent_connections [conn_types] </pre> - <p>where <c>conn_types</c> specifies <c>telnet, ftp, rpc</c> and/or <c>snmp</c>.</p> + <p>where <c>conn_types</c> specifies <c>ssh, telnet, ftp, rpc</c> and/or <c>snmp</c>.</p> <p>Example:</p> <pre> - ct_run ... -silent_connections telnet ftp</pre> - <p>switches off logging for telnet and ftp connections.</p> + ct_run ... -silent_connections ssh telnet</pre> + <p>switches off logging for ssh and telnet connections.</p> <pre> ct_run ... -silent_connections</pre> <p>switches off logging for all connection types.</p> - <p>Basic and important information such as opening and closing a connection, - fatal communication error and reconnection attempts will always be printed even - if logging has been suppressed for the connection type in question. However, operations - such as sending and receiving data may be performed silently.</p> + <p>Fatal communication error and reconnection attempts will always be printed even + if logging has been suppressed for the connection type in question. However, operations + such as sending and receiving data will be performed silently.</p> <p>It is possible to also specify <c>silent_connections</c> in a test suite. This is accomplished by returning a tuple, <c>{silent_connections,ConnTypes}</c>, in the <c>suite/0</c> or test case info list. If <c>ConnTypes</c> is a list of atoms - (<c>telnet, ftp, rpc</c> and/or <c>snmp</c>), output for any corresponding connections + (<c>ssh, telnet, ftp, rpc</c> and/or <c>snmp</c>), output for any corresponding connections will be suppressed. Full logging is per default enabled for any connection of type not specified in <c>ConnTypes</c>. Hence, if <c>ConnTypes</c> is the empty list, logging is enabled for all connections.</p> - <p>The <c>silent_connections</c> setting returned from a test case info function overrides, - for the test case in question, any setting made with <c>suite/0</c> (which is the setting - used for all cases in the suite). Example:</p> + <p>Example:</p> <pre> -module(my_SUITE). + + suite() -> [..., {silent_connections,[telnet,ssh]}, ...]. + ... - suite() -> [..., {silent_connections,[telnet,ftp]}, ...]. - ... + my_testcase1() -> - [{silent_connections,[ftp]}]. + [{silent_connections,[ssh]}]. + my_testcase1(_) -> - ... + ... + my_testcase2(_) -> - ... + ... </pre> <p>In this example, <c>suite/0</c> tells Common Test to suppress - printouts from telnet and ftp connections. This is valid for + printouts from telnet and ssh connections. This is valid for all test cases. However, <c>my_testcase1/0</c> specifies that - for this test case, only ftp should be silent. The result is + for this test case, only ssh should be silent. The result is that <c>my_testcase1</c> will get telnet info (if any) printed - in the log, but not ftp info. <c>my_testcase2</c> will get no + in the log, but not ssh info. <c>my_testcase2</c> will get no info from either connection printed.</p> - <p>The <c>-silent_connections</c> tag (or - <c>silent_connections</c> tagged tuple in the call to - <c>ct:run_test/1</c>) overrides any settings in the test - suite.</p> + <p><c>silent_connections</c> may also be specified with a term + in a test specification + (see <seealso marker="run_test_chapter#test_specifications">Test + Specifications</seealso>). Connections provided with the + <c>silent_connections</c> start flag/option, will be merged with + any connections listed in the test specification.</p> + + <p>The <c>silent_connections</c> start flag/option and test + specification term, overrides any settings made by the info functions + inside the test suite.</p> - <p>Note that in the current Common Test version, the + <note><p>Note that in the current Common Test version, the <c>silent_connections</c> feature only works for telnet - connections. Support for other connection types will be added - in future Common Test versions.</p> + and ssh connections! Support for other connection types will be added + in future Common Test versions.</p></note> </section> </chapter> diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml index 7b7e7af8ea..248d7de8b6 100644 --- a/lib/common_test/doc/src/write_test_chapter.xml +++ b/lib/common_test/doc/src/write_test_chapter.xml @@ -47,7 +47,7 @@ module for details about these functions.</p> <p>The CT application also includes other modules named - <c><![CDATA[ct_<something>]]></c> that + <c><![CDATA[ct_<component>]]></c> that provide various support, mainly simplified use of communication protocols such as rpc, snmp, ftp, telnet, etc.</p> @@ -173,7 +173,7 @@ </p> <p>The <c>end_per_testcase/2</c> function is called even after a - test case terminates due to a call to <c>ct:abort_current_testcase/1</c>, + test case terminates due to a call to <c><seealso marker="ct#abort_current_testcase-1">ct:abort_current_testcase/1</seealso></c>, or after a timetrap timeout. However, <c>end_per_testcase</c> will then execute on a different process than the test case function, and in this situation, <c>end_per_testcase</c> will @@ -243,7 +243,8 @@ <note><p>The test case function argument <c>Config</c> should not be confused with the information that can be retrieved from - configuration files (using ct:get_config/[1,2]). The Config argument + configuration files (using <c><seealso marker="ct#get_config-1"> + ct:get_config/1/2</seealso></c>). The Config argument should be used for runtime configuration of the test suite and the test cases, while configuration files should typically contain data related to the SUT. These two types of configuration data are handled @@ -302,7 +303,7 @@ <item> <p> Use this to specify arbitrary data related to the testcase. This - data can be retrieved at any time using the <c>ct:userdata/3</c> + data can be retrieved at any time using the <c><seealso marker="ct#userdata-3">ct:userdata/3</seealso></c> utility function. </p> </item> @@ -338,7 +339,8 @@ <pre> testcase2() -> - [{require, unix_telnet, {unix, [telnet, username, password]}}, + [{require, unix_telnet, unix}, + {require, {unix, [telnet, username, password]}}, {default_config, unix, [{telnet, "my_telnet_host"}, {username, "aladdin"}, {password, "sesame"}]}}].</pre> @@ -346,7 +348,8 @@ </taglist> <p>See the <seealso marker="config_file_chapter#require_config_data">Config files</seealso> - chapter and the <c>ct:require/[1,2]</c> function in the + chapter and the <c><seealso marker="ct#require-1"> + ct:require/1/2</seealso></c> function in the <seealso marker="ct">ct</seealso> reference manual for more information about <c>require</c>.</p> @@ -823,7 +826,7 @@ Common Test to create one dedicated private directory per test case and execution instead. This is accomplished by means of the flag/option: <c>create_priv_dir</c> (to be used with the - <c>ct_run</c> program, the <c>ct:run_test/1</c> function, or + <c>ct_run</c> program, the <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> function, or as test specification term). There are three possible values for this option: <list> @@ -839,7 +842,7 @@ become very inefficient for test runs with many test cases and/or repetitions. Therefore, in case the manual version is instead used, the test case must tell Common Test to create priv_dir when it needs it. - It does this by calling the function <c>ct:make_priv_dir/0</c>. + It does this by calling the function <c><seealso marker="ct#make_priv_dir-0">ct:make_priv_dir/0</seealso></c>. </p> <note><p>You should not depend on current working directory for @@ -883,11 +886,11 @@ of its sub-groups. If a timetrap value is defined by <c>group/1</c> for a sub-group, it overrides that of its higher level groups. Timetrap values set by individual test cases (by means of the test case info - function) overrides both group- and suite- level timetraps.</p> + function) override both group- and suite- level timetraps.</p> <p>It is also possible to dynamically set/reset a timetrap during the excution of a test case, or configuration function. This is done by calling - <c>ct:timetrap/1</c>. This function cancels the current timetrap + <c><seealso marker="ct#timetrap-1">ct:timetrap/1</seealso></c>. This function cancels the current timetrap and starts a new one (that stays active until timeout, or end of the current function).</p> @@ -900,12 +903,12 @@ <p>If a test case needs to suspend itself for a time that also gets multipled by <c>multiply_timetraps</c> (and possibly also scaled up if - <c>scale_timetraps</c> is enabled), the function <c>ct:sleep/1</c> + <c>scale_timetraps</c> is enabled), the function <c><seealso marker="ct#sleep-1">ct:sleep/1</seealso></c> may be used (instead of e.g. <c>timer:sleep/1</c>).</p> <p>A function (<c>fun/0</c> or <c>MFA</c>) may be specified as timetrap value in the suite-, group- and test case info function, as - well as argument to the <c>ct:timetrap/1</c> function. Examples:</p> + well as argument to the <c><seealso marker="ct#timetrap-1">ct:timetrap/1</seealso></c> function. Examples:</p> <p><c>{timetrap,{my_test_utils,timetrap,[?MODULE,system_start]}}</c></p> <p><c>ct:timetrap(fun() -> my_timetrap(TestCaseName, Config) end)</c></p> @@ -932,6 +935,99 @@ </section> <section> + <marker id="logging"></marker> + <title>Logging - categories and verbosity levels</title> + <p>Common Test provides three main functions for printing strings:</p> + <list> + <item><c>ct:log(Category, Importance, Format, Args)</c></item> + <item><c>ct:print(Category, Importance, Format, Args)</c></item> + <item><c>ct:pal(Category, Importance, Format, Args)</c></item> + </list> + <p>The <c>log/1/2/3/4</c> function will print a string to the test case + log file. The <c>print/1/2/3/4</c> function will print the string to screen, + and the <c>pal/1/2/3/4</c> function will print the same string both to file and + screen. (The functions are documented in the <c>ct</c> reference manual).</p> + + <p>The optional <c>Category</c> argument may be used to categorize the + log printout, and categories can be used for two things:</p> + <list> + <item>To compare the importance of the printout to a specific + verbosity level, and</item> + <item>to format the printout according to a user specific HTML + Style Sheet (CSS).</item> + </list> + + <p>The <c>Importance</c> argument specifies a level of importance + which, compared to a verbosity level (general and/or set per category), + determines if the printout should be visible or not. <c>Importance</c> + is an arbitrary integer in the range 0..99. Pre-defined constants + exist in the <c>ct.hrl</c> header file. The default importance level, + <c>?STD_IMPORTANCE</c> (used if the <c>Importance</c> argument is not + provided), is 50. This is also the importance used for standard IO, e.g. + from printouts made with <c>io:format/2</c>, <c>io:put_chars/1</c>, etc.</p> + + <p><c>Importance</c> is compared to a verbosity level set by means of the + <c>verbosity</c> start flag/option. The verbosity level can be set per + category and/or generally. The default verbosity level, <c>?STD_VERBOSITY</c>, + is 50, i.e. all standard IO gets printed. If a lower verbosity level is set, + standard IO printouts will be ignored. Common Test performs the following test:</p> + <pre>Importance >= (100-VerbosityLevel)</pre> + <p>This also means that verbosity level 0 effectively turns all logging off + (with the exception of printouts made by Common Test itself).</p> + + <p>The general verbosity level is not associated with any particular + category. This level sets the threshold for the standard IO printouts, + uncategorized <c>ct:log/print/pal</c> printouts, as well as + printouts for categories with undefined verbosity level.</p> + + <p>Example:</p> + <pre> + + Some printouts during test case execution: + + io:format("1. Standard IO, importance = ~w~n", [?STD_IMPORTANCE]), + ct:log("2. Uncategorized, importance = ~w", [?STD_IMPORTANCE]), + ct:log(info, "3. Categorized info, importance = ~w", [?STD_IMPORTANCE]]), + ct:log(info, ?LOW_IMPORTANCE, "4. Categorized info, importance = ~w", [?LOW_IMPORTANCE]), + ct:log(error, "5. Categorized error, importance = ~w", [?HI_IMPORTANCE]), + ct:log(error, ?HI_IMPORTANCE, "6. Categorized error, importance = ~w", [?MAX_IMPORTANCE]), + + If starting the test without specifying any verbosity levels: + + $ ct_run ... + + the following gets printed: + + 1. Standard IO, importance = 50 + 2. Uncategorized, importance = 50 + 3. Categorized info, importance = 50 + 5. Categorized error, importance = 75 + 6. Categorized error, importance = 99 + + If starting the test with: + + $ ct_run -verbosity 1 and info 75 + + the following gets printed: + + 3. Categorized info, importance = 50 + 4. Categorized info, importance = 25 + 6. Categorized error, importance = 99 + </pre> + + <p>How categories can be mapped to CSS tags is documented in the + <seealso marker="run_test_chapter#html_stylesheet">Running Tests</seealso> + chapter.</p> + + <p>The <c>Format</c> and <c>Args</c> arguments in <c>ct:log/print/pal</c> are + always passed on to the <c>io:format/3</c> function in <c>stdlib</c> + (please see the <c>io</c> manual page for details).</p> + + <p>For more information about log files, please see the + <seealso marker="run_test_chapter#log_files">Running Tests</seealso> chapter.</p> + </section> + + <section> <title>Illegal dependencies</title> <p>Even though it is highly efficient to write test suites with @@ -941,7 +1037,6 @@ Erlang/OTP test suites.</p> <list> - <item>Depending on current directory, and writing there:<br></br> <p>This is a common error in test suites. It is assumed that @@ -953,19 +1048,10 @@ </p> </item> - <item>Depending on the Clearcase (file version control system) - paths and files:<br></br> - - <p>The test suites are stored in Clearcase but are not - (necessarily) run within this environment. The directory - structure may vary from test run to test run. - </p> - </item> - <item>Depending on execution order:<br></br> - <p>During development of test suites, no assumption should be made - (preferrably) about the execution order of the test cases or suites. + <p>During development of test suites, no assumption should preferrably + be made about the execution order of the test cases or suites. E.g. a test case should not assume that a server it depends on, has already been started by a previous test case. There are several reasons for this: diff --git a/lib/common_test/include/ct.hrl b/lib/common_test/include/ct.hrl index 5a77108e1a..43b1b1c11f 100644 --- a/lib/common_test/include/ct.hrl +++ b/lib/common_test/include/ct.hrl @@ -19,3 +19,16 @@ -include_lib("test_server/include/test_server.hrl"). +%% the log level is used as argument to any CT logging function +-define(MIN_IMPORTANCE, 0 ). +-define(LOW_IMPORTANCE, 25). +-define(STD_IMPORTANCE, 50). +-define(HI_IMPORTANCE, 75). +-define(MAX_IMPORTANCE, 99). + +%% verbosity thresholds to filter out logging printouts +-define(MIN_VERBOSITY, 0 ). %% turn logging off +-define(LOW_VERBOSITY, 25 ). +-define(STD_VERBOSITY, 50 ). +-define(HI_VERBOSITY, 75 ). +-define(MAX_VERBOSITY, 100). diff --git a/lib/common_test/priv/Makefile.in b/lib/common_test/priv/Makefile.in index d9033f6ef1..2f06f7df65 100644 --- a/lib/common_test/priv/Makefile.in +++ b/lib/common_test/priv/Makefile.in @@ -60,6 +60,7 @@ FILES = vts.tool SCRIPTS = IMAGES = tile1.jpg CSS = ct_default.css +JS = jquery-latest.js jquery.tablesorter.min.js # # Rules @@ -85,12 +86,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk ifeq ($(XNIX),true) release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv" else release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv" endif release_docs_spec: @@ -107,6 +108,7 @@ else FILES = vts.tool IMAGES = tile1.jpg CSS = ct_default.css +JS = jquery-latest.js jquery.tablesorter.min.js # # Rules @@ -125,8 +127,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/common_test/priv/ct_default.css b/lib/common_test/priv/ct_default.css index 8ae6990cd8..1188f8f676 100644 --- a/lib/common_test/priv/ct_default.css +++ b/lib/common_test/priv/ct_default.css @@ -136,7 +136,18 @@ th { } thead th { - background: #2C5755; text-align: center; + background: #3F3F3F; color: #fff; + font-family: arial, sans-serif; font-size: 120%; + letter-spacing: -0.5px; + font-weight: bold; text-align: center; + padding-right: .5em; vertical-align: top; + text-decoration: underline; +} + +tfoot td { + font-family: arial, sans-serif; font-size: 110%; + letter-spacing: -0.5px; + font-weight: bold; } .odd td { @@ -167,10 +178,6 @@ th a, td a:active { color: #85ABD5; } -tfoot th, tfoot td { - background: #3F3F3F; color: #fff; -} - th + td { padding-left: .5em; } diff --git a/lib/common_test/priv/jquery-latest.js b/lib/common_test/priv/jquery-latest.js new file mode 100644 index 0000000000..ac7e7009dc --- /dev/null +++ b/lib/common_test/priv/jquery-latest.js @@ -0,0 +1,154 @@ +/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
diff --git a/lib/common_test/priv/jquery.tablesorter.min.js b/lib/common_test/priv/jquery.tablesorter.min.js new file mode 100644 index 0000000000..b8605df1e7 --- /dev/null +++ b/lib/common_test/priv/jquery.tablesorter.min.js @@ -0,0 +1,4 @@ + +(function($){$.extend({tablesorter:new +function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1 +var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);
\ No newline at end of file diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile index 6a16c6f3af..f7dce195d7 100644 --- a/lib/common_test/src/Makefile +++ b/lib/common_test/src/Makefile @@ -70,14 +70,18 @@ MODULES= \ ct_hooks\ ct_hooks_lock\ cth_log_redirect\ - cth_surefire + cth_surefire \ + ct_netconfc \ + ct_conn_log_h \ + cth_conn_log TARGET_MODULES= $(MODULES:%=$(EBIN)/%) BEAM_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) ERL_FILES= $(MODULES:=.erl) HRL_FILES = \ - ct_util.hrl + ct_util.hrl \ + ct_netconfc.hrl EXTERNAL_HRL_FILES = \ ../include/ct.hrl \ ../include/ct_event.hrl @@ -133,12 +137,12 @@ $(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) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) "$(RELSYSDIR)/include" release_tests_spec: opt $(INSTALL_DIR) $(RELEASE_PATH)/common_test_test diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src index ae9a51faeb..18c1dec784 100644 --- a/lib/common_test/src/common_test.app.src +++ b/lib/common_test/src/common_test.app.src @@ -33,6 +33,8 @@ ct_master_event, ct_master_logs, ct_master_status, + ct_netconfc, + ct_conn_log_h, ct_repeat, ct_rpc, ct_run, @@ -49,6 +51,7 @@ ct_config_xml, ct_slave, cth_log_redirect, + cth_conn_log, cth_surefire ]}, {registered, [ct_logs, diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 571d99029f..455fd1e4f1 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -25,24 +25,24 @@ %%% %%% <p><strong>Test Suite Support Macros</strong></p> %%% -%%% <p>The <code>config</code> macro is defined in <code>ct.hrl</code>. This +%%% <p>The <c>config</c> macro is defined in <c>ct.hrl</c>. This %%% macro should be used to retrieve information from the -%%% <code>Config</code> variable sent to all test cases. It is used with two +%%% <c>Config</c> variable sent to all test cases. It is used with two %%% arguments, where the first is the name of the configuration -%%% variable you wish to retrieve, and the second is the <code>Config</code> +%%% variable you wish to retrieve, and the second is the <c>Config</c> %%% variable supplied to the test case.</p> %%% %%% <p>Possible configuration variables include:</p> %%% <ul> -%%% <li><code>data_dir</code> - Data file directory.</li> -%%% <li><code>priv_dir</code> - Scratch file directory.</li> -%%% <li>Whatever added by <code>init_per_suite/1</code> or -%%% <code>init_per_testcase/2</code> in the test suite.</li> +%%% <li><c>data_dir</c> - Data file directory.</li> +%%% <li><c>priv_dir</c> - Scratch file directory.</li> +%%% <li>Whatever added by <c>init_per_suite/1</c> or +%%% <c>init_per_testcase/2</c> in the test suite.</li> %%% </ul> %%% @type var_name() = atom(). A variable name which is specified when -%%% <code>ct:require/2</code> is called, -%%% e.g. <code>ct:require(mynodename,{node,[telnet]})</code> +%%% <c>ct:require/2</c> is called, +%%% e.g. <c>ct:require(mynodename,{node,[telnet]})</c> %%% %%% @type target_name() = var_name(). The name of a target. %%% @@ -51,6 +51,8 @@ -module(ct). +-include("ct.hrl"). + %% Command line user interface for running tests -export([install/1, run/1, run/2, run/3, run_test/1, run_testspec/1, step/3, step/4, @@ -60,13 +62,15 @@ -export([require/1, require/2, get_config/1, get_config/2, get_config/3, reload_config/1, - log/1, log/2, log/3, - print/1, print/2, print/3, - pal/1, pal/2, pal/3, + log/1, log/2, log/3, log/4, + print/1, print/2, print/3, print/4, + pal/1, pal/2, pal/3, pal/4, capture_start/0, capture_stop/0, capture_get/0, capture_get/1, fail/1, fail/2, comment/1, comment/2, make_priv_dir/0, testcases/2, userdata/2, userdata/3, - timetrap/1, get_timetrap_info/0, sleep/1]). + timetrap/1, get_timetrap_info/0, sleep/1, + notify/2, sync_notify/2, + break/1, break/2, continue/0, continue/1]). %% New API for manipulating with config handlers -export([add_config/2, remove_config/2]). @@ -95,10 +99,10 @@ %%% <p>Run this function once before first test.</p> %%% %%% <p>Example:<br/> -%%% <code>install([{config,["config_node.ctc","config_user.ctc"]}])</code>.</p> +%%% <c>install([{config,["config_node.ctc","config_user.ctc"]}])</c>.</p> %%% %%% <p>Note that this function is automatically run by the -%%% <code>ct_run</code> program.</p> +%%% <c>ct_run</c> program.</p> install(Opts) -> ct_run:install(Opts). @@ -111,10 +115,10 @@ install(Opts) -> %%% %%% @doc Run the given test case(s). %%% -%%% <p>Requires that <code>ct:install/1</code> has been run first.</p> +%%% <p>Requires that <c>ct:install/1</c> has been run first.</p> %%% %%% <p>Suites (*_SUITE.erl) files must be stored in -%%% <code>TestDir</code> or <code>TestDir/test</code>. All suites +%%% <c>TestDir</c> or <c>TestDir/test</c>. All suites %%% will be compiled when test is run.</p> run(TestDir,Suite,Cases) -> ct_run:run(TestDir,Suite,Cases). @@ -150,8 +154,10 @@ run(TestDirs) -> %%% {multiply_timetraps,M} | {scale_timetraps,Bool} | %%% {repeat,N} | {duration,DurTime} | {until,StopTime} | %%% {force_stop,Bool} | {decrypt,DecryptKeyOrFile} | -%%% {refresh_logs,LogDir} | {logopts,LogOpts} | {basic_html,Bool} | -%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} +%%% {refresh_logs,LogDir} | {logopts,LogOpts} | +%%% {verbosity,VLevels} | {basic_html,Bool} | +%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} | +%%% {release_shell,Bool} %%% TestDirs = [string()] | string() %%% Suites = [string()] | [atom()] | string() | atom() %%% Cases = [atom()] | atom() @@ -182,26 +188,45 @@ run(TestDirs) -> %%% DecryptFile = string() %%% LogOpts = [LogOpt] %%% LogOpt = no_nl | no_src +%%% VLevels = VLevel | [{Category,VLevel}] +%%% VLevel = integer() +%%% Category = atom() %%% CTHs = [CTHModule | {CTHModule, CTHInitArgs}] %%% CTHModule = atom() %%% CTHInitArgs = term() -%%% Result = [TestResult] | {error,Reason} -%%% @doc Run tests as specified by the combination of options in <code>Opts</code>. +%%% Result = {Ok,Failed,{UserSkipped,AutoSkipped}} | TestRunnerPid | {error,Reason} +%%% Ok = integer() +%%% Failed = integer() +%%% UserSkipped = integer() +%%% AutoSkipped = integer() +%%% TestRunnerPid = pid() +%%% Reason = term() +%%% @doc <p>Run tests as specified by the combination of options in <c>Opts</c>. %%% The options are the same as those used with the -%%% <seealso marker="ct_run#ct_run"><code>ct_run</code></seealso> program. -%%% Note that here a <code>TestDir</code> can be used to point out the path to -%%% a <code>Suite</code>. Note also that the option <code>testcase</code> -%%% corresponds to the <code>-case</code> option in the <code>ct_run</code> -%%% program. Configuration files specified in <code>Opts</code> will be -%%% installed automatically at startup. +%%% <seealso marker="ct_run#ct_run"><c>ct_run</c></seealso> program. +%%% Note that here a <c>TestDir</c> can be used to point out the path to +%%% a <c>Suite</c>. Note also that the option <c>testcase</c> +%%% corresponds to the <c>-case</c> option in the <c>ct_run</c> +%%% program. Configuration files specified in <c>Opts</c> will be +%%% installed automatically at startup.</p> +%%% <p><c>TestRunnerPid</c> is returned if <c>release_shell == true</c> +%%% (see <c>break/1</c> for details).</p> +%%% <p><c>Reason</c> indicates what type of error has been encountered.</p> run_test(Opts) -> ct_run:run_test(Opts). %%%----------------------------------------------------------------- %%% @spec run_testspec(TestSpec) -> Result %%% TestSpec = [term()] -%%% @doc Run test specified by <code>TestSpec</code>. The terms are +%%% Result = {Ok,Failed,{UserSkipped,AutoSkipped}} | {error,Reason} +%%% Ok = integer() +%%% Failed = integer() +%%% UserSkipped = integer() +%%% AutoSkipped = integer() +%%% Reason = term() +%%% @doc Run test specified by <c>TestSpec</c>. The terms are %%% the same as those used in test specification files. +%%% <p><c>Reason</c> indicates what type of error has been encountered.</p> run_testspec(TestSpec) -> ct_run:run_testspec(TestSpec). @@ -221,8 +246,8 @@ step(TestDir,Suite,Case) -> %%% Opt = config | keep_inactive %%% %%% @doc Step through a test case with the debugger. If the -%%% <code>config</code> option has been given, breakpoints will -%%% be set also on the configuration functions in <code>Suite</code>. +%%% <c>config</c> option has been given, breakpoints will +%%% be set also on the configuration functions in <c>Suite</c>. %%% @see run/3 step(TestDir,Suite,Case,Opts) -> ct_run:step(TestDir,Suite,Case,Opts). @@ -234,20 +259,20 @@ step(TestDir,Suite,Case,Opts) -> %%% %%% <p>From this mode all test case support functions can be executed %%% directly from the erlang shell. The interactive mode can also be -%%% started from the OS command line with <code>ct_run -shell -%%% [-config File...]</code>.</p> +%%% started from the OS command line with <c>ct_run -shell +%%% [-config File...]</c>.</p> %%% %%% <p>If any functions using "required config data" (e.g. telnet or %%% ftp functions) are to be called from the erlang shell, config data -%%% must first be required with <code>ct:require/2</code>.</p> +%%% must first be required with <c>ct:require/2</c>.</p> %%% %%% <p>Example:<br/> -%%% <code>> ct:require(unix_telnet, unix).</code><br/> -%%% <code>ok</code><br/> -%%% <code>> ct_telnet:open(unix_telnet).</code><br/> -%%% <code>{ok,<0.105.0>}</code><br/> -%%% <code>> ct_telnet:cmd(unix_telnet, "ls .").</code><br/> -%%% <code>{ok,["ls","file1 ...",...]}</code></p> +%%% <c>> ct:require(unix_telnet, unix).</c><br/> +%%% <c>ok</c><br/> +%%% <c>> ct_telnet:open(unix_telnet).</c><br/> +%%% <c>{ok,<0.105.0>}</c><br/> +%%% <c>> ct_telnet:cmd(unix_telnet, "ls .").</c><br/> +%%% <c>{ok,["ls","file1 ...",...]}</c></p> start_interactive() -> ct_util:start(interactive). @@ -265,27 +290,34 @@ stop_interactive() -> %%%----------------------------------------------------------------- %%% @spec require(Required) -> ok | {error,Reason} -%%% Required = Key | {Key,SubKeys} +%%% Required = Key | {Key,SubKeys} | {Key,SubKey,SubKeys} %%% Key = atom() %%% SubKeys = SubKey | [SubKey] %%% SubKey = atom() %%% -%%% @doc Check if the required configuration is available. +%%% @doc Check if the required configuration is available. It is possible +%%% to specify arbitrarily deep tuples as <c>Required</c>. Note that it is +%%% only the last element of the tuple which can be a list of <c>SubKey</c>s. %%% -%%% <p>Example: require the variable <code>myvar</code>:<br/> -%%% <code>ok = ct:require(myvar)</code></p> +%%% <p>Example 1: require the variable <c>myvar</c>:</p> +%%% <pre>ok = ct:require(myvar).</pre> %%% %%% <p>In this case the config file must at least contain:</p> -%%% <pre> -%%% {myvar,Value}.</pre> +%%% <pre>{myvar,Value}.</pre> %%% -%%% <p>Example: require the variable <code>myvar</code> with -%%% subvariable <code>sub1</code>:<br/> -%%% <code>ok = ct:require({myvar,sub1})</code></p> +%%% <p>Example 2: require the key <c>myvar</c> with +%%% subkeys <c>sub1</c> and <c>sub2</c>:</p> +%%% <pre>ok = ct:require({myvar,[sub1,sub2]}).</pre> %%% %%% <p>In this case the config file must at least contain:</p> -%%% <pre> -%%% {myvar,[{sub1,Value}]}.</pre> +%%% <pre>{myvar,[{sub1,Value},{sub2,Value}]}.</pre> +%%% +%%% <p>Example 3: require the key <c>myvar</c> with +%%% subkey <c>sub1</c> with <c>subsub1</c>:</p> +%%% <pre>ok = ct:require({myvar,sub1,sub2}).</pre> +%%% +%%% <p>In this case the config file must at least contain:</p> +%%% <pre>{myvar,[{sub1,[{sub2,Value}]}]}.</pre> %%% %%% @see require/2 %%% @see get_config/1 @@ -297,30 +329,36 @@ require(Required) -> %%%----------------------------------------------------------------- %%% @spec require(Name,Required) -> ok | {error,Reason} %%% Name = atom() -%%% Required = Key | {Key,SubKeys} +%%% Required = Key | {Key,SubKey} | {Key,SubKey,SubKey} +%%% SubKey = Key %%% Key = atom() -%%% SubKeys = SubKey | [SubKey] -%%% SubKey = atom() %%% %%% @doc Check if the required configuration is available, and give it -%%% a name. +%%% a name. The semantics for <c>Required</c> is the same as in +%%% <c>required/1</c> except that it is not possible to specify a list +%%% of <c>SubKey</c>s. %%% -%%% <p>If the requested data is available, the main entry will be -%%% associated with <code>Name</code> so that the value of the element -%%% can be read with <code>get_config/1,2</code> provided -%%% <code>Name</code> instead of the <code>Key</code>.</p> +%%% <p>If the requested data is available, the sub entry will be +%%% associated with <c>Name</c> so that the value of the element +%%% can be read with <c>get_config/1,2</c> provided +%%% <c>Name</c> instead of the whole <c>Required</c> term.</p> %%% %%% <p>Example: Require one node with a telnet connection and an -%%% ftp connection. Name the node <code>a</code>:<br/> <code>ok = -%%% ct:require(a,{node,[telnet,ftp]}).</code><br/> All references -%%% to this node may then use the node name. E.g. you can fetch a -%%% file over ftp like this:<br/> -%%% <code>ok = ct:ftp_get(a,RemoteFile,LocalFile).</code></p> +%%% ftp connection. Name the node <c>a</c>: +%%% <pre>ok = ct:require(a,{machine,node}).</pre> +%%% All references to this node may then use the node name. +%%% E.g. you can fetch a file over ftp like this:</p> +%%% <pre>ok = ct:ftp_get(a,RemoteFile,LocalFile).</pre> %%% %%% <p>For this to work, the config file must at least contain:</p> -%%% <pre> -%%% {node,[{telnet,IpAddr}, -%%% {ftp,IpAddr}]}.</pre> +%%% <pre>{machine,[{node,[{telnet,IpAddr},{ftp,IpAddr}]}]}.</pre> +%%% +%%% <note>The behaviour of this function changed radically in common_test +%%% 1.6.2. In order too keep some backwards compatability it is still possible +%%% to do: <br/><c>ct:require(a,{node,[telnet,ftp]}).</c><br/> +%%% This will associate the name <c>a</c> with the top level <c>node</c> entry. +%%% For this to work, the config file must at least contain:<br/> +%%% <c>{node,[{telnet,IpAddr},{ftp,IpAddr}]}.</c></note> %%% %%% @see require/1 %%% @see get_config/1 @@ -343,7 +381,7 @@ get_config(Required,Default) -> %%%----------------------------------------------------------------- %%% @spec get_config(Required,Default,Opts) -> ValueOrElement -%%% Required = KeyOrName | {KeyOrName,SubKey} +%%% Required = KeyOrName | {KeyOrName,SubKey} | {KeyOrName,SubKey,SubKey} %%% KeyOrName = atom() %%% SubKey = atom() %%% Default = term() @@ -355,43 +393,41 @@ get_config(Required,Default) -> %%% %%% <p>This function returns the matching value(s) or config element(s), %%% given a config variable key or its associated name -%%% (if one has been specified with <code>require/2</code> or a +%%% (if one has been specified with <c>require/2</c> or a %%% require statement).</p> %%% %%% <p>Example, given the following config file:</p> %%% <pre> %%% {unix,[{telnet,IpAddr}, -%%% {username,Username}, -%%% {password,Password}]}.</pre> -%%% <p><code>get_config(unix,Default) -> +%%% {user,[{username,Username}, +%%% {password,Password}]}]}.</pre> +%%% <p><c>ct:get_config(unix,Default) -> %%% [{telnet,IpAddr}, -%%% {username,Username}, -%%% {password,Password}]</code><br/> -%%% <code>get_config({unix,telnet},Default) -> IpAddr</code><br/> -%%% <code>get_config({unix,ftp},Default) -> Default</code><br/> -%%% <code>get_config(unknownkey,Default) -> Default</code></p> +%%% {user, [{username,Username}, +%%% {password,Password}]}]</c><br/> +%%% <c>ct:get_config({unix,telnet},Default) -> IpAddr</c><br/> +%%% <c>ct:get_config({unix,user,username},Default) -> Username</c><br/> +%%% <c>ct:get_config({unix,ftp},Default) -> Default</c><br/> +%%% <c>ct:get_config(unknownkey,Default) -> Default</c></p> %%% %%% <p>If a config variable key has been associated with a name (by -%%% means of <code>require/2</code> or a require statement), the name +%%% means of <c>require/2</c> or a require statement), the name %%% may be used instead of the key to read the value:</p> %%% -%%% <p><code>require(myhost,unix) -> ok</code><br/> -%%% <code>get_config(myhost,Default) -> -%%% [{telnet,IpAddr}, -%%% {username,Username}, -%%% {password,Password}]</code></p> +%%% <p><c>ct:require(myuser,{unix,user}) -> ok.</c><br/> +%%% <c>ct:get_config(myuser,Default) -> +%%% [{username,Username}, +%%% {password,Password}]</c></p> %%% %%% <p>If a config variable is defined in multiple files and you want to -%%% access all possible values, use the <code>all</code> option. The +%%% access all possible values, use the <c>all</c> option. The %%% values will be returned in a list and the order of the elements %%% corresponds to the order that the config files were specified at %%% startup.</p> %%% %%% <p>If you want config elements (key-value tuples) returned as result -%%% instead of values, use the <code>element</code> option. -%%% The returned elements will then be on the form <code>{KeyOrName,Value}</code>, -%%% or (in case a subkey has been specified) -%%% <code>{{KeyOrName,SubKey},Value}</code></p> +%%% instead of values, use the <c>element</c> option. +%%% The returned elements will then be on the form <c>{Required,Value}</c></p> %%% %%% @see get_config/1 %%% @see get_config/2 @@ -402,7 +438,7 @@ get_config(Required,Default,Opts) -> %%%----------------------------------------------------------------- %%% @spec reload_config(Required) -> ValueOrElement -%%% Required = KeyOrName | {KeyOrName,SubKey} +%%% Required = KeyOrName | {KeyOrName,SubKey} | {KeyOrName,SubKey,SubKey} %%% KeyOrName = atom() %%% SubKey = atom() %%% ValueOrElement = term() @@ -421,25 +457,41 @@ reload_config(Required)-> %%%----------------------------------------------------------------- %%% @spec log(Format) -> ok -%%% @equiv log(default,Format,[]) +%%% @equiv log(default,50,Format,[]) log(Format) -> - log(default,Format,[]). + log(default,?STD_IMPORTANCE,Format,[]). %%%----------------------------------------------------------------- %%% @spec log(X1,X2) -> ok -%%% X1 = Category | Format +%%% X1 = Category | Importance | Format %%% X2 = Format | Args -%%% @equiv log(Category,Format,Args) +%%% @equiv log(Category,Importance,Format,Args) log(X1,X2) -> - {Category,Format,Args} = - if is_atom(X1) -> {X1,X2,[]}; - is_list(X1) -> {default,X1,X2} + {Category,Importance,Format,Args} = + if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]}; + is_integer(X1) -> {default,X1,X2,[]}; + is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2} end, - log(Category,Format,Args). + log(Category,Importance,Format,Args). %%%----------------------------------------------------------------- -%%% @spec log(Category,Format,Args) -> ok +%%% @spec log(X1,X2,X3) -> ok +%%% X1 = Category | Importance +%%% X2 = Importance | Format +%%% X3 = Format | Args +%%% @equiv log(Category,Importance,Format,Args) +log(X1,X2,X3) -> + {Category,Importance,Format,Args} = + if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]}; + is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3}; + is_integer(X1) -> {default,X1,X2,X3} + end, + log(Category,Importance,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec log(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -448,30 +500,52 @@ log(X1,X2) -> %%% <p>This function is meant for printing a string directly from a %%% test case to the test case log file.</p> %%% -%%% <p>Default <code>Category</code> is <code>default</code> and -%%% default <code>Args</code> is <code>[]</code>.</p> -log(Category,Format,Args) -> - ct_logs:tc_log(Category,Format,Args). +%%% <p>Default <c>Category</c> is <c>default</c>, +%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>, +%%% and default value for <c>Args</c> is <c>[]</c>.</p> +%%% <p>Please see the User's Guide for details on <c>Category</c> +%%% and <c>Importance</c>.</p> +log(Category,Importance,Format,Args) -> + ct_logs:tc_log(Category,Importance,Format,Args). %%%----------------------------------------------------------------- %%% @spec print(Format) -> ok -%%% @equiv print(default,Format,[]) +%%% @equiv print(default,50,Format,[]) print(Format) -> - print(default,Format,[]). + print(default,?STD_IMPORTANCE,Format,[]). %%%----------------------------------------------------------------- -%%% @equiv print(Category,Format,Args) +%%% @spec print(X1,X2) -> ok +%%% X1 = Category | Importance | Format +%%% X2 = Format | Args +%%% @equiv print(Category,Importance,Format,Args) print(X1,X2) -> - {Category,Format,Args} = - if is_atom(X1) -> {X1,X2,[]}; - is_list(X1) -> {default,X1,X2} + {Category,Importance,Format,Args} = + if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]}; + is_integer(X1) -> {default,X1,X2,[]}; + is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2} end, - print(Category,Format,Args). + print(Category,Importance,Format,Args). %%%----------------------------------------------------------------- -%%% @spec print(Category,Format,Args) -> ok +%%% @spec print(X1,X2,X3) -> ok +%%% X1 = Category | Importance +%%% X2 = Importance | Format +%%% X3 = Format | Args +%%% @equiv print(Category,Importance,Format,Args) +print(X1,X2,X3) -> + {Category,Importance,Format,Args} = + if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]}; + is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3}; + is_integer(X1) -> {default,X1,X2,X3} + end, + print(Category,Importance,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec print(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -480,33 +554,52 @@ print(X1,X2) -> %%% <p>This function is meant for printing a string from a test case %%% to the console.</p> %%% -%%% <p>Default <code>Category</code> is <code>default</code> and -%%% default <code>Args</code> is <code>[]</code>.</p> -print(Category,Format,Args) -> - ct_logs:tc_print(Category,Format,Args). +%%% <p>Default <c>Category</c> is <c>default</c>, +%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>, +%%% and default value for <c>Args</c> is <c>[]</c>.</p> +%%% <p>Please see the User's Guide for details on <c>Category</c> +%%% and <c>Importance</c>.</p> +print(Category,Importance,Format,Args) -> + ct_logs:tc_print(Category,Importance,Format,Args). %%%----------------------------------------------------------------- %%% @spec pal(Format) -> ok -%%% @equiv pal(default,Format,[]) +%%% @equiv pal(default,50,Format,[]) pal(Format) -> - pal(default,Format,[]). + pal(default,?STD_IMPORTANCE,Format,[]). %%%----------------------------------------------------------------- %%% @spec pal(X1,X2) -> ok -%%% X1 = Category | Format +%%% X1 = Category | Importance | Format %%% X2 = Format | Args -%%% @equiv pal(Category,Format,Args) +%%% @equiv pal(Category,Importance,Format,Args) pal(X1,X2) -> - {Category,Format,Args} = - if is_atom(X1) -> {X1,X2,[]}; - is_list(X1) -> {default,X1,X2} + {Category,Importance,Format,Args} = + if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]}; + is_integer(X1) -> {default,X1,X2,[]}; + is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2} end, - pal(Category,Format,Args). + pal(Category,Importance,Format,Args). %%%----------------------------------------------------------------- -%%% @spec pal(Category,Format,Args) -> ok +%%% @spec pal(X1,X2,X3) -> ok +%%% X1 = Category | Importance +%%% X2 = Importance | Format +%%% X3 = Format | Args +%%% @equiv pal(Category,Importance,Format,Args) +pal(X1,X2,X3) -> + {Category,Importance,Format,Args} = + if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]}; + is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3}; + is_integer(X1) -> {default,X1,X2,X3} + end, + pal(Category,Importance,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec pal(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -515,10 +608,13 @@ pal(X1,X2) -> %%% <p>This function is meant for printing a string from a test case, %%% both to the test case log file and to the console.</p> %%% -%%% <p>Default <code>Category</code> is <code>default</code> and -%%% default <code>Args</code> is <code>[]</code>.</p> -pal(Category,Format,Args) -> - ct_logs:tc_pal(Category,Format,Args). +%%% <p>Default <c>Category</c> is <c>default</c>, +%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>, +%%% and default value for <c>Args</c> is <c>[]</c>.</p> +%%% <p>Please see the User's Guide for details on <c>Category</c> +%%% and <c>Importance</c>.</p> +pal(Category,Importance,Format,Args) -> + ct_logs:tc_pal(Category,Importance,Format,Args). %%%----------------------------------------------------------------- %%% @spec capture_start() -> ok @@ -535,7 +631,7 @@ capture_start() -> %%% @spec capture_stop() -> ok %%% %%% @doc Stop capturing text strings (a session started with -%%% <code>capture_start/0</code>). +%%% <c>capture_start/0</c>). %%% %%% @see capture_start/0 %%% @see capture_get/1 @@ -558,9 +654,9 @@ capture_get() -> %%% %%% @doc Return and purge the list of text strings buffered %%% during the latest session of capturing printouts to stdout. -%%% With <code>ExclCategories</code> it's possible to specify -%%% log categories that should be ignored in <code>ListOfStrings</code>. -%%% If <code>ExclCategories = []</code>, no filtering takes place. +%%% With <c>ExclCategories</c> it's possible to specify +%%% log categories that should be ignored in <c>ListOfStrings</c>. +%%% If <c>ExclCategories = []</c>, no filtering takes place. %%% %%% @see capture_start/0 %%% @see capture_stop/0 @@ -585,7 +681,7 @@ capture_get([]) -> %%% Reason = term() %%% %%% @doc Terminate a test case with the given error -%%% <code>Reason</code>. +%%% <c>Reason</c>. fail(Reason) -> try exit({test_case_failed,Reason}) @@ -605,7 +701,7 @@ fail(Reason) -> %%% %%% @doc Terminate a test case with an error message specified %%% by a format string and a list of values (used as arguments to -%%% <code>io_lib:format/2</code>). +%%% <c>io_lib:format/2</c>). fail(Format, Args) -> try io_lib:format(Format, Args) of Str -> @@ -628,11 +724,11 @@ fail(Format, Args) -> %%% @spec comment(Comment) -> void() %%% Comment = term() %%% -%%% @doc Print the given <code>Comment</code> in the comment field in +%%% @doc Print the given <c>Comment</c> in the comment field in %%% the table on the test suite result page. %%% %%% <p>If called several times, only the last comment is printed. -%%% The test case return value <code>{comment,Comment}</code> +%%% The test case return value <c>{comment,Comment}</c> %%% overwrites the string set by this function.</p> comment(Comment) when is_list(Comment) -> Formatted = @@ -655,10 +751,10 @@ comment(Comment) -> %%% @doc Print the formatted string in the comment field in %%% the table on the test suite result page. %%% -%%% <p>The <code>Format</code> and <code>Args</code> arguments are -%%% used in call to <code>io_lib:format/2</code> in order to create -%%% the comment string. The behaviour of <code>comment/2</code> is -%%% otherwise the same as the <code>comment/1</code> function (see +%%% <p>The <c>Format</c> and <c>Args</c> arguments are +%%% used in call to <c>io_lib:format/2</c> in order to create +%%% the comment string. The behaviour of <c>comment/2</c> is +%%% otherwise the same as the <c>comment/1</c> function (see %%% above for details).</p> comment(Format, Args) when is_list(Format), is_list(Args) -> Formatted = @@ -703,11 +799,11 @@ get_target_name(Handle) -> %%% @doc Parse the printout from an SQL table and return a list of tuples. %%% %%% <p>The printout to parse would typically be the result of a -%%% <code>select</code> command in SQL. The returned -%%% <code>Table</code> is a list of tuples, where each tuple is a row +%%% <c>select</c> command in SQL. The returned +%%% <c>Table</c> is a list of tuples, where each tuple is a row %%% in the table.</p> %%% -%%% <p><code>Heading</code> is a tuple of strings representing the +%%% <p><c>Heading</c> is a tuple of strings representing the %%% headings of each column in the table.</p> parse_table(Data) -> ct_util:parse_table(Data). @@ -779,8 +875,8 @@ make_and_load(Dir, Suite) -> %%% SuiteUserData = [term()] %%% Reason = term() %%% -%%% @doc Returns any data specified with the tag <code>userdata</code> -%%% in the list of tuples returned from <code>Suite:suite/0</code>. +%%% @doc Returns any data specified with the tag <c>userdata</c> +%%% in the list of tuples returned from <c>Suite:suite/0</c>. userdata(TestDir, Suite) -> case make_and_load(TestDir, Suite) of E = {error,_} -> @@ -815,9 +911,9 @@ get_userdata(_BadTerm, Spec) -> %%% TCUserData = [term()] %%% Reason = term() %%% -%%% @doc Returns any data specified with the tag <code>userdata</code> -%%% in the list of tuples returned from <code>Suite:group(GroupName)</code> -%%% or <code>Suite:Case()</code>. +%%% @doc Returns any data specified with the tag <c>userdata</c> +%%% in the list of tuples returned from <c>Suite:group(GroupName)</c> +%%% or <c>Suite:Case()</c>. userdata(TestDir, Suite, {group,GroupName}) -> case make_and_load(TestDir, Suite) of E = {error,_} -> @@ -840,8 +936,9 @@ userdata(TestDir, Suite, Case) when is_atom(Case) -> %%%----------------------------------------------------------------- %%% @spec get_status() -> TestStatus | {error,Reason} | no_tests_running %%% TestStatus = [StatusElem] -%%% StatusElem = {current,{Suite,TestCase}} | {successful,Successful} | +%%% StatusElem = {current,TestCaseInfo} | {successful,Successful} | %%% {failed,Failed} | {skipped,Skipped} | {total,Total} +%%% TestCaseInfo = {Suite,TestCase} | [{Suite,TestCase}] %%% Suite = atom() %%% TestCase = atom() %%% Successful = integer() @@ -853,7 +950,8 @@ userdata(TestDir, Suite, Case) when is_atom(Case) -> %%% Reason = term() %%% %%% @doc Returns status of ongoing test. The returned list contains info about -%%% which test case is currently executing, as well as counters for +%%% which test case is currently executing (a list of cases when a +%%% parallel test case group is executing), as well as counters for %%% successful, failed, skipped, and total test cases so far. get_status() -> case get_testdata(curr_tc) of @@ -878,6 +976,10 @@ get_testdata(Key) -> Error; {'EXIT',_Reason} -> no_tests_running; + undefined -> + {error,no_testdata}; + [CurrTC] when Key == curr_tc -> + {ok,CurrTC}; Data -> {ok,Data} end. @@ -891,7 +993,7 @@ get_testdata(Key) -> %%% executing. The function is therefore only safe to call from a function which %%% has been called (or synchronously invoked) by the test case.</p> %%% -%%% <p><code>Reason</code>, the reason for aborting the test case, is printed +%%% <p><c>Reason</c>, the reason for aborting the test case, is printed %%% in the test case log.</p> abort_current_testcase(Reason) -> test_server_ctrl:abort_current_testcase(Reason). @@ -904,13 +1006,13 @@ abort_current_testcase(Reason) -> %%% Reason = term() %%% %%% @doc <p>This function encrypts the source config file with DES3 and -%%% saves the result in file <code>EncryptFileName</code>. The key, +%%% saves the result in file <c>EncryptFileName</c>. The key, %%% a string, must be available in a text file named -%%% <code>.ct_config.crypt</code> in the current directory, or the +%%% <c>.ct_config.crypt</c> in the current directory, or the %%% home directory of the user (it is searched for in that order).</p> %%% <p>See the Common Test User's Guide for information about using %%% encrypted config files when running tests.</p> -%%% <p>See the <code>crypto</code> application for details on DES3 +%%% <p>See the <c>crypto</c> application for details on DES3 %%% encryption/decryption.</p> encrypt_config_file(SrcFileName, EncryptFileName) -> ct_config:encrypt_config_file(SrcFileName, EncryptFileName). @@ -924,13 +1026,13 @@ encrypt_config_file(SrcFileName, EncryptFileName) -> %%% Reason = term() %%% %%% @doc <p>This function encrypts the source config file with DES3 and -%%% saves the result in the target file <code>EncryptFileName</code>. +%%% saves the result in the target file <c>EncryptFileName</c>. %%% The encryption key to use is either the value in -%%% <code>{key,Key}</code> or the value stored in the file specified -%%% by <code>{file,File}</code>.</p> +%%% <c>{key,Key}</c> or the value stored in the file specified +%%% by <c>{file,File}</c>.</p> %%% <p>See the Common Test User's Guide for information about using %%% encrypted config files when running tests.</p> -%%% <p>See the <code>crypto</code> application for details on DES3 +%%% <p>See the <c>crypto</c> application for details on DES3 %%% encryption/decryption.</p> encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) -> ct_config:encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile). @@ -942,11 +1044,11 @@ encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) -> %%% TargetFileName = string() %%% Reason = term() %%% -%%% @doc <p>This function decrypts <code>EncryptFileName</code>, previously -%%% generated with <code>encrypt_config_file/2/3</code>. The original +%%% @doc <p>This function decrypts <c>EncryptFileName</c>, previously +%%% generated with <c>encrypt_config_file/2/3</c>. The original %%% file contents is saved in the target file. The encryption key, a %%% string, must be available in a text file named -%%% <code>.ct_config.crypt</code> in the current directory, or the +%%% <c>.ct_config.crypt</c> in the current directory, or the %%% home directory of the user (it is searched for in that order).</p> decrypt_config_file(EncryptFileName, TargetFileName) -> ct_config:decrypt_config_file(EncryptFileName, TargetFileName). @@ -959,8 +1061,8 @@ decrypt_config_file(EncryptFileName, TargetFileName) -> %%% KeyOrFile = {key,string()} | {file,string()} %%% Reason = term() %%% -%%% @doc <p>This function decrypts <code>EncryptFileName</code>, previously -%%% generated with <code>encrypt_config_file/2/3</code>. The original +%%% @doc <p>This function decrypts <c>EncryptFileName</c>, previously +%%% generated with <c>encrypt_config_file/2/3</c>. The original %%% file contents is saved in the target file. The key must have the %%% the same value as that used for encryption.</p> decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) -> @@ -977,7 +1079,7 @@ decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) -> %%% given callback module and configuration string. Callback module %%% should be either loaded or present in the code part. Loaded %%% configuration variables can later be removed using -%%% <code>remove_config/2</code> function.</p> +%%% <c>remove_config/2</c> function.</p> add_config(Callback, Config)-> ct_config:add_config(Callback, Config). @@ -1006,9 +1108,9 @@ remove_config(Callback, Config) -> %%% A = list() %%% %%% @doc <p>Use this function to set a new timetrap for the running test case. -%%% If the argument is <code>Func</code>, the timetrap will be triggered -%%% when this function returns. <code>Func</code> may also return a new -%%% <code>Time</code> value, which in that case will be the value for the +%%% If the argument is <c>Func</c>, the timetrap will be triggered +%%% when this function returns. <c>Func</c> may also return a new +%%% <c>Time</c> value, which in that case will be the value for the %%% new timetrap.</p> timetrap(Time) -> test_server:timetrap_cancel(), @@ -1047,3 +1149,131 @@ sleep({seconds,Ss}) -> sleep(trunc(Ss * 1000)); sleep(Time) -> test_server:adjusted_sleep(Time). + +%%%----------------------------------------------------------------- +%%% @spec notify(Name,Data) -> ok +%%% Name = atom() +%%% Data = term() +%%% +%%% @doc <p>Sends a asynchronous notification of type <c>Name</c> with +%%% <c>Data</c>to the common_test event manager. This can later be +%%% caught by any installed event manager. </p> +%%% @see //stdlib/gen_event +notify(Name,Data) -> + ct_event:notify(Name, Data). + +%%%----------------------------------------------------------------- +%%% @spec sync_notify(Name,Data) -> ok +%%% Name = atom() +%%% Data = term() +%%% +%%% @doc <p>Sends a synchronous notification of type <c>Name</c> with +%%% <c>Data</c>to the common_test event manager. This can later be +%%% caught by any installed event manager. </p> +%%% @see //stdlib/gen_event +sync_notify(Name,Data) -> + ct_event:sync_notify(Name, Data). + +%%%----------------------------------------------------------------- +%%% @spec break(Comment) -> ok | {error,Reason} +%%% Comment = string() +%%% Reason = {multiple_cases_running,TestCases} | +%%% 'enable break with release_shell option' +%%% TestCases = [atom()] +%%% +%%% @doc <p>This function will cancel any active timetrap and pause the +%%% execution of the current test case until the user calls the +%%% <c>continue/0</c> function. It gives the user the opportunity +%%% to interact with the erlang node running the tests, e.g. for +%%% debugging purposes or for manually executing a part of the +%%% test case. If a parallel group is executing, <c>break/2</c> +%%% should be called instead.</p> +%%% <p>A cancelled timetrap will not be automatically +%%% reactivated after the break, but must be started exlicitly with +%%% <c>ct:timetrap/1</c></p> +%%% <p>In order for the break/continue functionality to work, +%%% Common Test must release the shell process controlling stdin. +%%% This is done by setting the <c>release_shell</c> start option +%%% to <c>true</c>. See the User's Guide for more information.</p> + +break(Comment) -> + case {ct_util:get_testdata(starter), + ct_util:get_testdata(release_shell)} of + {ct,ReleaseSh} when ReleaseSh /= true -> + Warning = "ct:break/1 can only be used if release_shell == true.\n", + ct_logs:log("Warning!", Warning, []), + io:format(user, "Warning! " ++ Warning, []), + {error,'enable break with release_shell option'}; + _ -> + case get_testdata(curr_tc) of + {ok,{_,_TestCase}} -> + test_server:break(?MODULE, Comment); + {ok,Cases} when is_list(Cases) -> + {error,{'multiple cases running', + [TC || {_,TC} <- Cases]}}; + Error = {error,_} -> + Error; + Error -> + {error,Error} + end + end. + +%%%----------------------------------------------------------------- +%%% @spec break(TestCase, Comment) -> ok | {error,Reason} +%%% TestCase = atom() +%%% Comment = string() +%%% Reason = 'test case not running' | +%%% 'enable break with release_shell option' +%%% +%%% @doc <p>This function works the same way as <c>break/1</c>, +%%% only the <c>TestCase</c> argument makes it possible to +%%% pause a test case executing in a parallel group. The +%%% <c>continue/1</c> function should be used to resume +%%% execution of <c>TestCase</c>.</p> +%%% <p>See <c>break/1</c> for more details.</p> +break(TestCase, Comment) -> + case {ct_util:get_testdata(starter), + ct_util:get_testdata(release_shell)} of + {ct,ReleaseSh} when ReleaseSh /= true -> + Warning = "ct:break/2 can only be used if release_shell == true.\n", + ct_logs:log("Warning!", Warning, []), + io:format(user, "Warning! " ++ Warning, []), + {error,'enable break with release_shell option'}; + _ -> + case get_testdata(curr_tc) of + {ok,Cases} when is_list(Cases) -> + case lists:keymember(TestCase, 2, Cases) of + true -> + test_server:break(?MODULE, TestCase, Comment); + false -> + {error,'test case not running'} + end; + {ok,{_,TestCase}} -> + test_server:break(?MODULE, TestCase, Comment); + Error = {error,_} -> + Error; + Error -> + {error,Error} + end + end. + +%%%----------------------------------------------------------------- +%%% @spec continue() -> ok +%%% +%%% @doc <p>This function must be called in order to continue after a +%%% test case (not executing in a parallel group) has called +%%% <c>break/1</c>.</p> +continue() -> + test_server:continue(). + +%%%----------------------------------------------------------------- +%%% @spec continue(TestCase) -> ok +%%% TestCase = atom() +%%% +%%% @doc <p>This function must be called in order to continue after a +%%% test case has called <c>break/2</c>. If the paused test case, +%%% <c>TestCase</c>, executes in a parallel group, this +%%% function - rather than <c>continue/0</c> - must be used +%%% in order to let the test case proceed.</p> +continue(TestCase) -> + test_server:continue(TestCase). diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 9277af5bc1..463b7d180c 100644 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -122,8 +122,8 @@ return({To,Ref},Result) -> loop(StartDir) -> receive - {{require,Name,Tag,SubTags},From} -> - Result = do_require(Name,Tag,SubTags), + {{require,Name,Key},From} -> + Result = do_require(Name,Key), return(From,Result), loop(StartDir); {{set_default_config,{Config,Scope}},From} -> @@ -168,16 +168,19 @@ reload_config(KeyOrName) -> call({reload_config, KeyOrName}). process_default_configs(Opts) -> - case lists:keysearch(config, 1, Opts) of - {value,{_,Files=[File|_]}} when is_list(File) -> - Files; - {value,{_,File=[C|_]}} when is_integer(C) -> - [File]; - {value,{_,[]}} -> - []; - false -> - [] - end. + lists:flatmap(fun({config,[_|_] = FileOrFiles}) -> + case {io_lib:printable_list(FileOrFiles), + io_lib:printable_list(hd(FileOrFiles))} of + {true,true} -> + FileOrFiles; + {true,false} -> + [FileOrFiles]; + _ -> + [] + end; + (_) -> + [] + end,Opts). process_user_configs(Opts, Acc) -> case lists:keytake(userconfig, 1, Opts) of @@ -319,75 +322,58 @@ get_config(KeyOrName,Default) -> get_config(KeyOrName,Default,[]). get_config(KeyOrName,Default,Opts) when is_atom(KeyOrName) -> - case lookup_config(KeyOrName) of - [] -> - Default; - [{_Ref,Val}|_] = Vals -> - case {lists:member(all,Opts),lists:member(element,Opts)} of - {true,true} -> - [{KeyOrName,V} || {_R,V} <- lists:sort(Vals)]; - {true,false} -> - [V || {_R,V} <- lists:sort(Vals)]; - {false,true} -> - {KeyOrName,Val}; - {false,false} -> - Val - end + case get_config({KeyOrName}, Default, Opts) of + %% If only an atom is given, then we need to unwrap the + %% key if it is returned + {{KeyOrName}, Val} -> + {KeyOrName, Val}; + [{{KeyOrName}, _Val}|_] = Res -> + [{K, Val} || {{K},Val} <- Res, K == KeyOrName]; + Else -> + Else end; -get_config({KeyOrName,SubKey},Default,Opts) -> - case lookup_config(KeyOrName) of +%% This useage of get_config is only used by internal ct functions +%% and may change at any time +get_config({DeepKey,SubKey}, Default, Opts) when is_tuple(DeepKey) -> + get_config(erlang:append_element(DeepKey, SubKey), Default, Opts); +get_config(KeyOrName,Default,Opts) when is_tuple(KeyOrName) -> + case lookup_config(element(1,KeyOrName)) of [] -> - Default; + format_value([Default],KeyOrName,Opts); Vals -> - Vals1 = case [Val || {_Ref,Val} <- lists:sort(Vals)] of - Result=[L|_] when is_list(L) -> - case L of - [{_,_}|_] -> - Result; - _ -> - [] - end; - _ -> - [] - end, - case get_subconfig([SubKey],Vals1,[],Opts) of - {ok,[{_,SubVal}|_]=SubVals} -> - case {lists:member(all,Opts),lists:member(element,Opts)} of - {true,true} -> - [{{KeyOrName,SubKey},Val} || {_,Val} <- SubVals]; - {true,false} -> - [Val || {_SubKey,Val} <- SubVals]; - {false,true} -> - {{KeyOrName,SubKey},SubVal}; - {false,false} -> - SubVal - end; - _ -> - Default - end + NewVals = + lists:map( + fun({Val}) -> + get_config(tl(tuple_to_list(KeyOrName)), + Val,Default,Opts) + end,Vals), + format_value(NewVals,KeyOrName,Opts) end. -get_subconfig(SubKeys,Values) -> - get_subconfig(SubKeys,Values,[],[]). - -get_subconfig(SubKeys,[Value|Rest],Mapped,Opts) -> - case do_get_config(SubKeys,Value,[]) of - {ok,SubMapped} -> - case lists:member(all,Opts) of - true -> - get_subconfig(SubKeys,Rest,Mapped++SubMapped,Opts); - false -> - {ok,SubMapped} - end; - _Error -> - get_subconfig(SubKeys,Rest,Mapped,Opts) +get_config([],Vals,_Default,_Opts) -> + Vals; +get_config([[]],Vals,Default,Opts) -> + get_config([],Vals,Default,Opts); +%% This case is used by {require,{unix,[port,host]}} functionality +get_config([SubKeys], Vals, Default, _Opts) when is_list(SubKeys) -> + case do_get_config(SubKeys, Vals, []) of + {ok, SubVals} -> + [SubVal || {_,SubVal} <- SubVals]; + + _ -> + Default end; -get_subconfig(SubKeys,[],[],_) -> - {error,{not_available,SubKeys}}; -get_subconfig(_SubKeys,[],Mapped,_) -> - {ok,Mapped}. +get_config([Key|Rest], Vals, Default, Opts) -> + case do_get_config([Key], Vals, []) of + {ok, [{Key,NewVals}]} -> + get_config(Rest, NewVals, Default, Opts); + _ -> + Default + end. +do_get_config([Key|_], Available, _Mapped) when not is_list(Available) -> + {error,{not_available,Key}}; do_get_config([Key|Required],Available,Mapped) -> case lists:keysearch(Key,1,Available) of {value,{Key,Value}} -> @@ -403,8 +389,7 @@ do_get_config([],_Available,Mapped) -> get_all_config() -> ets:select(?attr_table,[{#ct_conf{name='$1',key='$2',value='$3', default='$4',_='_'}, - [], - [{{'$1','$2','$3','$4'}}]}]). + [],[{{'$1','$2','$3','$4'}}]}]). lookup_config(KeyOrName) -> case lookup_name(KeyOrName) of @@ -415,13 +400,23 @@ lookup_config(KeyOrName) -> end. lookup_name(Name) -> - ets:select(?attr_table,[{#ct_conf{ref='$1',value='$2',name=Name,_='_'}, - [], - [{{'$1','$2'}}]}]). + ets:select(?attr_table,[{#ct_conf{value='$1',name=Name,_='_'}, + [],[{{'$1'}}]}]). lookup_key(Key) -> - ets:select(?attr_table,[{#ct_conf{key=Key,ref='$1',value='$2',name='_UNDEF',_='_'}, - [], - [{{'$1','$2'}}]}]). + ets:select(?attr_table,[{#ct_conf{key=Key,value='$1',name='_UNDEF',_='_'}, + [],[{{'$1'}}]}]). + +format_value([SubVal|_] = SubVals, KeyOrName, Opts) -> + case {lists:member(all,Opts),lists:member(element,Opts)} of + {true,true} -> + [{KeyOrName,Val} || Val <- SubVals]; + {true,false} -> + [Val || Val <- SubVals]; + {false,true} -> + {KeyOrName,SubVal}; + {false,false} -> + SubVal + end. lookup_handler_for_config({Key, _Subkey}) -> lookup_handler_for_config(Key); @@ -475,65 +470,78 @@ release_allocated([H|T]) -> release_allocated([]) -> ok. -allocate(Name,Key,SubKeys) -> - case ets:match_object(?attr_table,#ct_conf{key=Key,name='_UNDEF',_='_'}) of - [] -> +allocate(Name,Key) -> + Ref = make_ref(), + case get_config(Key,Ref,[all,element]) of + [{_,Ref}] -> {error,{not_available,Key}}; - Available -> - case allocate_subconfig(Name,SubKeys,Available,false) of - ok -> - ok; - Error -> - Error - end + Configs -> + associate(Name,Key,Configs), + ok end. -allocate_subconfig(Name,SubKeys,[C=#ct_conf{value=Value}|Rest],Found) -> - case do_get_config(SubKeys,Value,[]) of - {ok,_SubMapped} -> - ets:insert(?attr_table,C#ct_conf{name=Name}), - allocate_subconfig(Name,SubKeys,Rest,true); - _Error -> - allocate_subconfig(Name,SubKeys,Rest,Found) - end; -allocate_subconfig(_Name,_SubKeys,[],true) -> + +associate('_UNDEF',_Key,_Configs) -> ok; -allocate_subconfig(_Name,SubKeys,[],false) -> - {error,{not_available,SubKeys}}. +associate(Name,{Key,SubKeys},Configs) when is_atom(Key), is_list(SubKeys) -> + associate_int(Name,Configs,"true"); +associate(Name,_Key,Configs) -> + associate_int(Name,Configs,os:getenv("COMMON_TEST_ALIAS_TOP")). + +associate_int(Name,Configs,"true") -> + lists:map(fun({K,_Config}) -> + Cs = ets:match_object( + ?attr_table, + #ct_conf{key=element(1,K), + name='_UNDEF',_='_'}), + [ets:insert(?attr_table,C#ct_conf{name=Name}) + || C <- Cs] + end,Configs); +associate_int(Name,Configs,_) -> + lists:map(fun({K,Config}) -> + Key = if is_tuple(K) -> element(1,K); + is_atom(K) -> K + end, + + Cs = ets:match_object( + ?attr_table, + #ct_conf{key=Key, + name='_UNDEF',_='_'}), + [ets:insert(?attr_table,C#ct_conf{name=Name, + value=Config}) + || C <- Cs] + end,Configs). + + delete_config(Default) -> ets:match_delete(?attr_table,#ct_conf{default=Default,_='_'}), ok. -require(Key) when is_atom(Key) -> - require({Key,[]}); -require({Key,SubKeys}) when is_atom(Key) -> - allocate('_UNDEF',Key,to_list(SubKeys)); +require(Key) when is_atom(Key); is_tuple(Key) -> + allocate('_UNDEF',Key); require(Key) -> {error,{invalid,Key}}. -require(Name,Key) when is_atom(Key) -> - require(Name,{Key,[]}); -require(Name,{Key,SubKeys}) when is_atom(Name), is_atom(Key) -> - call({require,Name,Key,to_list(SubKeys)}); +require(Name,Key) when is_atom(Name),is_atom(Key) orelse is_tuple(Key) -> + call({require,Name,Key}); require(Name,Keys) -> {error,{invalid,{Name,Keys}}}. -to_list(X) when is_list(X) -> X; -to_list(X) -> [X]. - -do_require(Name,Key,SubKeys) when is_list(SubKeys) -> +do_require(Name,Key) -> case get_key_from_name(Name) of {error,_} -> - allocate(Name,Key,SubKeys); + allocate(Name,Key); {ok,Key} -> %% already allocated - check that it has all required subkeys - Vals = [Val || {_Ref,Val} <- lookup_name(Name)], - case get_subconfig(SubKeys,Vals) of - {ok,_SubMapped} -> - ok; - Error -> - Error + R = make_ref(), + case get_config(Key,R,[]) of + R -> + {error,{not_available,Key}}; + {error,_} = Error -> + Error; + _Error -> + ok end; {ok,OtherKey} -> {error,{name_in_use,Name,OtherKey}} @@ -760,13 +768,13 @@ check_config_files(Configs) -> end, lists:keysearch(error, 1, lists:flatten(lists:map(ConfigChecker, Configs))). -prepare_user_configs([ConfigString|UserConfigs], Acc, new) -> +prepare_user_configs([CallbackMod|UserConfigs], Acc, new) -> prepare_user_configs(UserConfigs, - [{list_to_atom(ConfigString), []}|Acc], + [{list_to_atom(CallbackMod),[]}|Acc], cur); prepare_user_configs(["and"|UserConfigs], Acc, _) -> prepare_user_configs(UserConfigs, Acc, new); -prepare_user_configs([ConfigString|UserConfigs], [{LastMod, LastList}|Acc], cur) -> +prepare_user_configs([ConfigString|UserConfigs], [{LastMod,LastList}|Acc], cur) -> prepare_user_configs(UserConfigs, [{LastMod, [ConfigString|LastList]}|Acc], cur); diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl new file mode 100644 index 0000000000..bf27238121 --- /dev/null +++ b/lib/common_test/src/ct_conn_log_h.erl @@ -0,0 +1,230 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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(ct_conn_log_h). + +%%% +%%% A handler that can be connected to the error_logger event +%%% handler. Writes all ct connection events. See comments in +%%% cth_conn_log for more information. +%%% + +-include("ct_util.hrl"). + +-export([init/1, + handle_event/2, handle_call/2, handle_info/2, + terminate/2]). + +-record(state, {group_leader,logs=[]}). + +-define(WIDTH,80). + +%%%----------------------------------------------------------------- +%%% Callbacks +init({GL,Logs}) -> + open_files(Logs,#state{group_leader=GL}). + +open_files([{ConnMod,{LogType,Logs}}|T],State) -> + case do_open_files(Logs,[]) of + {ok,Fds} -> + open_files(T,State#state{logs=[{ConnMod,{LogType,Fds}} | + State#state.logs]}); + Error -> + Error + end; +open_files([],State) -> + {ok,State}. + + +do_open_files([{Tag,File}|Logs],Acc) -> + case file:open(File, [write]) of + {ok,Fd} -> + do_open_files(Logs,[{Tag,Fd}|Acc]); + {error,Reason} -> + {error,{could_not_open_log,File,Reason}} + end; +do_open_files([],Acc) -> + {ok,lists:reverse(Acc)}. + +handle_event({_Type, GL, _Msg}, State) when node(GL) /= node() -> + {ok, State}; +handle_event({_Type,_GL,{Pid,{ct_connection,Action,ConnName},Report}},State) -> + Info = conn_info(Pid,#conn_log{name=ConnName,action=Action}), + write_report(now(),Info,Report,State), + {ok, State}; +handle_event({_Type,_GL,{Pid,Info=#conn_log{},Report}},State) -> + write_report(now(),conn_info(Pid,Info),Report,State), + {ok, State}; +handle_event({error_report,_,{Pid,_,[{ct_connection,ConnName}|R]}},State) -> + %% Error reports from connection + write_error(now(),conn_info(Pid,#conn_log{name=ConnName}),R,State), + {ok, State}; +handle_event(_, State) -> + {ok, State}. + +handle_info(_, State) -> + {ok, State}. + +handle_call(_Query, State) -> + {ok, {error, bad_query}, State}. + +terminate(_,#state{logs=Logs}) -> + [file:close(Fd) || {_,{_,Fds}} <- Logs, {_,Fd} <- Fds], + ok. + + +%%%----------------------------------------------------------------- +%%% Writing reports +write_report(Time,#conn_log{module=ConnMod}=Info,Data,State) -> + {LogType,Fd} = get_log(Info,State), + io:format(Fd,"~n~s~s~s",[format_head(ConnMod,LogType,Time), + format_title(LogType,Info), + format_data(ConnMod,LogType,Data)]). + +write_error(Time,#conn_log{module=ConnMod}=Info,Report,State) -> + case get_log(Info,State) of + {html,_} -> + %% The error will anyway be written in the html log by the + %% sasl error handler, so don't write it again. + ok; + {LogType,Fd} -> + io:format(Fd,"~n~s~s~s",[format_head(ConnMod,LogType,Time," ERROR"), + format_title(LogType,Info), + format_error(LogType,Report)]) + end. + +get_log(Info,State) -> + case proplists:get_value(Info#conn_log.module,State#state.logs) of + {html,_} -> + {html,State#state.group_leader}; + {LogType,Fds} -> + {LogType,get_fd(Info,Fds)}; + undefined -> + {html,State#state.group_leader} + end. + +get_fd(#conn_log{name=undefined},Fds) -> + proplists:get_value(default,Fds); +get_fd(#conn_log{name=ConnName},Fds) -> + case proplists:get_value(ConnName,Fds) of + undefined -> + proplists:get_value(default,Fds); + Fd -> + Fd + end. + +%%%----------------------------------------------------------------- +%%% Formatting +format_head(ConnMod,LogType,Time) -> + format_head(ConnMod,LogType,Time,""). + +format_head(ConnMod,raw,Time,Text) -> + io_lib:format("~n~p, ~p~s, ",[now_to_time(Time),ConnMod,Text]); +format_head(ConnMod,_,Time,Text) -> + Head = pad_char_end(?WIDTH,pretty_head(now_to_time(Time),ConnMod,Text),$=), + io_lib:format("~n~s",[Head]). + +format_title(raw,#conn_log{client=Client}=Info) -> + io_lib:format("Client ~p ~s ~s",[Client,actionstr(Info),serverstr(Info)]); +format_title(_,Info) -> + Title = pad_char_end(?WIDTH,pretty_title(Info),$=), + io_lib:format("~n~s", [Title]). + +format_data(_,_,NoData) when NoData == ""; NoData == <<>> -> + ""; +format_data(ConnMod,LogType,Data) -> + ConnMod:format_data(LogType,Data). + +format_error(raw,Report) -> + io_lib:format("~n~p~n",[Report]); +format_error(pretty,Report) -> + [io_lib:format("~n ~p: ~p",[K,V]) || {K,V} <- Report]. + + + + +%%%----------------------------------------------------------------- +%%% Helpers +conn_info(LoggingProc, #conn_log{client=undefined} = ConnInfo) -> + conn_info(ConnInfo#conn_log{client=LoggingProc}); +conn_info(_, ConnInfo) -> + conn_info(ConnInfo). + +conn_info(#conn_log{client=Client, module=undefined} = ConnInfo) -> + case ets:lookup(ct_connections,Client) of + [#conn{address=Address,callback=Callback}] -> + ConnInfo#conn_log{address=Address,module=Callback}; + [] -> + ConnInfo + end; +conn_info(ConnInfo) -> + ConnInfo. + + +now_to_time({_,_,MicroS}=Now) -> + {calendar:now_to_local_time(Now),MicroS}. + +pretty_head({{{Y,Mo,D},{H,Mi,S}},MicroS},ConnMod,Text0) -> + Text = string:to_upper(atom_to_list(ConnMod) ++ Text0), + io_lib:format("= ~s ==== ~s-~s-~p::~s:~s:~s,~s ", + [Text,t(D),month(Mo),Y,t(H),t(Mi),t(S), + micro2milli(MicroS)]). + +pretty_title(#conn_log{client=Client}=Info) -> + io_lib:format("= Client ~p ~s Server ~s ", + [Client,actionstr(Info),serverstr(Info)]). + +actionstr(#conn_log{action=send}) -> "----->"; +actionstr(#conn_log{action=recv}) -> "<-----"; +actionstr(#conn_log{action=open}) -> "opened session to"; +actionstr(#conn_log{action=close}) -> "closed session to"; +actionstr(_) -> "<---->". + +serverstr(#conn_log{name=undefined,address=Address}) -> + io_lib:format("~p",[Address]); +serverstr(#conn_log{name=Alias,address=Address}) -> + io_lib:format("~p(~p)",[Alias,Address]). + +month(1) -> "Jan"; +month(2) -> "Feb"; +month(3) -> "Mar"; +month(4) -> "Apr"; +month(5) -> "May"; +month(6) -> "Jun"; +month(7) -> "Jul"; +month(8) -> "Aug"; +month(9) -> "Sep"; +month(10) -> "Oct"; +month(11) -> "Nov"; +month(12) -> "Dec". + +micro2milli(X) -> + pad0(3,integer_to_list(X div 1000)). + +t(X) -> + pad0(2,integer_to_list(X)). + +pad0(N,Str) -> + M = length(Str), + lists:duplicate(N-M,$0) ++ Str. + +pad_char_end(N,Str,Char) -> + case length(lists:flatten(Str)) of + M when M<N -> Str ++ lists:duplicate(N-M,Char); + _ -> Str + end. diff --git a/lib/common_test/src/ct_event.erl b/lib/common_test/src/ct_event.erl index 3e79898ad1..998be35fda 100644 --- a/lib/common_test/src/ct_event.erl +++ b/lib/common_test/src/ct_event.erl @@ -31,7 +31,7 @@ %% API -export([start_link/0, add_handler/0, add_handler/1, stop/0]). --export([notify/1, sync_notify/1]). +-export([notify/1, notify/2, sync_notify/1,sync_notify/2]). -export([is_alive/0]). %% gen_event callbacks @@ -90,6 +90,13 @@ notify(Event) -> end. %%-------------------------------------------------------------------- +%% Function: notify(Name,Data) -> ok +%% Description: Asynchronous notification to event manager. +%%-------------------------------------------------------------------- +notify(Name, Data) -> + notify(#event{ name = Name, data = Data}). + +%%-------------------------------------------------------------------- %% Function: sync_notify(Event) -> ok %% Description: Synchronous notification to event manager. %%-------------------------------------------------------------------- @@ -102,6 +109,13 @@ sync_notify(Event) -> end. %%-------------------------------------------------------------------- +%% Function: sync_notify(Name,Data) -> ok +%% Description: Synchronous notification to event manager. +%%-------------------------------------------------------------------- +sync_notify(Name,Data) -> + sync_notify(#event{ name = Name, data = Data}). + +%%-------------------------------------------------------------------- %% Function: is_alive() -> true | false %% Description: Check if Event Manager is alive. %%-------------------------------------------------------------------- diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index 11575cd0fb..4d47731239 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -27,7 +27,7 @@ -export([init_tc/3, end_tc/3, end_tc/4, get_suite/2, get_all_cases/1]). -export([report/2, warn/1, error_notification/4]). --export([get_logopts/0, format_comment/1, get_html_wrapper/3]). +-export([get_logopts/0, format_comment/1, get_html_wrapper/4]). -export([error_in_suite/1, init_per_suite/1, end_per_suite/1, init_per_group/2, end_per_group/2]). @@ -72,18 +72,25 @@ init_tc(Mod,Func,Config) -> {Suite,{suite0_failed,_}=Failure} -> {skip,Failure}; _ -> - ct_util:set_testdata({curr_tc,{Suite,Func}}), + ct_util:update_testdata(curr_tc, + fun(undefined) -> + [{Suite,Func}]; + (Running) -> + [{Suite,Func}|Running] + end, [create]), case ct_util:read_suite_data({seq,Suite,Func}) of undefined -> init_tc1(Mod,Suite,Func,Config); Seq when is_atom(Seq) -> case ct_util:read_suite_data({seq,Suite,Seq}) of [Func|TCs] -> % this is the 1st case in Seq - %% make sure no cases in this seq are marked as failed - %% from an earlier execution in the same suite + %% make sure no cases in this seq are + %% marked as failed from an earlier execution + %% in the same suite lists:foreach( fun(TC) -> - ct_util:save_suite_data({seq,Suite,TC},Seq) + ct_util:save_suite_data({seq,Suite,TC}, + Seq) end, TCs); _ -> ok @@ -204,20 +211,23 @@ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) -> data={Mod,FuncSpec}}), case catch configure(MergedInfo,MergedInfo,SuiteInfo, - FuncSpec,Config) of + FuncSpec,[],Config) of {suite0_failed,Reason} -> - ct_util:set_testdata({curr_tc,{Mod,{suite0_failed,{require,Reason}}}}), + ct_util:set_testdata({curr_tc,{Mod,{suite0_failed, + {require,Reason}}}}), {skip,{require_failed_in_suite0,Reason}}; {error,Reason} -> {auto_skip,{require_failed,Reason}}; {'EXIT',Reason} -> {auto_skip,Reason}; - {ok,Config1} -> + {ok,PostInitHook,Config1} -> case get('$test_server_framework_test') of undefined -> - ct_suite_init(Suite, FuncSpec, Config1); + ct_suite_init(Suite, FuncSpec, PostInitHook, Config1); Fun -> - case Fun(init_tc, Config1) of + PostInitHookResult = do_post_init_hook(PostInitHook, + Config1), + case Fun(init_tc, [PostInitHookResult ++ Config1]) of NewConfig when is_list(NewConfig) -> {ok,NewConfig}; Else -> @@ -226,14 +236,28 @@ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) -> end end. -ct_suite_init(Suite, Func, [Config]) when is_list(Config) -> +ct_suite_init(Suite, Func, PostInitHook, Config) when is_list(Config) -> case ct_hooks:init_tc(Suite, Func, Config) of NewConfig when is_list(NewConfig) -> - {ok, [NewConfig]}; + PostInitHookResult = do_post_init_hook(PostInitHook, NewConfig), + {ok, [PostInitHookResult ++ NewConfig]}; Else -> Else end. +do_post_init_hook(PostInitHook, Config) -> + lists:flatmap(fun({Tag,Fun}) -> + case lists:keysearch(Tag,1,Config) of + {value,_} -> + []; + false -> + case Fun() of + {error,_} -> []; + Result -> [{Tag,Result}] + end + end + end, PostInitHook). + add_defaults(Mod,Func, GroupPath) -> Suite = get_suite_name(Mod, GroupPath), case (catch Suite:suite()) of @@ -453,15 +477,16 @@ timetrap_first([],Info,[]) -> timetrap_first([],Info,Found) -> ?rev(Found) ++ ?rev(Info). -configure([{require,Required}|Rest],Info,SuiteInfo,Scope,Config) -> +configure([{require,Required}|Rest], + Info,SuiteInfo,Scope,PostInitHook,Config) -> case ct:require(Required) of ok -> - configure(Rest,Info,SuiteInfo,Scope,Config); + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); Error = {error,Reason} -> case required_default('_UNDEF',Required,Info, SuiteInfo,Scope) of ok -> - configure(Rest,Info,SuiteInfo,Scope,Config); + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); _ -> case lists:keymember(Required,2,SuiteInfo) of true -> @@ -471,14 +496,15 @@ configure([{require,Required}|Rest],Info,SuiteInfo,Scope,Config) -> end end end; -configure([{require,Name,Required}|Rest],Info,SuiteInfo,Scope,Config) -> +configure([{require,Name,Required}|Rest], + Info,SuiteInfo,Scope,PostInitHook,Config) -> case ct:require(Name,Required) of ok -> - configure(Rest,Info,SuiteInfo,Scope,Config); + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); Error = {error,Reason} -> case required_default(Name,Required,Info,SuiteInfo,Scope) of ok -> - configure(Rest,Info,SuiteInfo,Scope,Config); + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); _ -> case lists:keymember(Name,2,SuiteInfo) of true -> @@ -488,17 +514,24 @@ configure([{require,Name,Required}|Rest],Info,SuiteInfo,Scope,Config) -> end end end; -configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,Config) -> - configure(Rest,Info,SuiteInfo,Scope,Config); -configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,Config) -> - Dog = test_server:timetrap(Time), - configure(Rest,Info,SuiteInfo,Scope,[{watchdog,Dog}|Config]); -configure([{ct_hooks, Hook} | Rest], Info, SuiteInfo, Scope, Config) -> - configure(Rest, Info, SuiteInfo, Scope, [{ct_hooks, Hook} | Config]); -configure([_|Rest],Info,SuiteInfo,Scope,Config) -> - configure(Rest,Info,SuiteInfo,Scope,Config); -configure([],_,_,_,Config) -> - {ok,[Config]}. +configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); +configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> + PostInitHook1 = + [{watchdog,fun() -> case test_server:get_timetrap_info() of + undefined -> + test_server:timetrap(Time); + _ -> + {error,already_set} + end + end} | PostInitHook], + configure(Rest,Info,SuiteInfo,Scope,PostInitHook1,Config); +configure([{ct_hooks,Hook}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,[{ct_hooks,Hook}|Config]); +configure([_|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); +configure([],_,_,_,PostInitHook,Config) -> + {ok,PostInitHook,Config}. %% the require element in Info may come from suite/0 and %% should be scoped 'suite', or come from the group info @@ -562,10 +595,8 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) -> %% in case Mod == ct_framework, lookup the suite name Suite = get_suite_name(Mod, Args), - case lists:keysearch(watchdog,1,Args) of - {value,{watchdog,Dog}} -> test_server:timetrap_cancel(Dog); - false -> ok - end, + test_server:timetrap_cancel(), + %% save the testcase process pid so that it can be used %% to look up the attached trace window later case ct_util:get_testdata(interpret) of @@ -633,7 +664,22 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) -> end, ct_util:reset_silent_connections(), - + + %% reset the curr_tc state, or delete this TC from the list of + %% executing cases (if in a parallel group) + ClearCurrTC = fun(Running = [_,_|_]) -> + lists:keydelete(Func,2,Running); + ({_,{suite0_failed,_}}) -> + undefined; + ([{_,CurrTC}]) when CurrTC == Func -> + undefined; + (undefined) -> + undefined; + (Unexpected) -> + exit({error,{reset_curr_tc,{Mod,Func},Unexpected}}) + end, + ct_util:update_testdata(curr_tc,ClearCurrTC), + case FinalResult of {skip,{sequence_failed,_,_}} -> %% ct_logs:init_tc is never called for a skipped test case @@ -1634,5 +1680,5 @@ format_comment(Comment) -> %%%----------------------------------------------------------------- %%% @spec get_html_wrapper(TestName, PrintLabel, Cwd) -> Header -get_html_wrapper(TestName, PrintLabel, Cwd) -> - ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd). +get_html_wrapper(TestName, PrintLabel, Cwd, TableCols) -> + ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols). diff --git a/lib/common_test/src/ct_ftp.erl b/lib/common_test/src/ct_ftp.erl index 5db73066a3..723715c986 100644 --- a/lib/common_test/src/ct_ftp.erl +++ b/lib/common_test/src/ct_ftp.erl @@ -66,6 +66,7 @@ %%% {unix,[{ftp,IpAddr}, %%% {username,Username}, %%% {password,Password}]}.</pre> +%%% @see ct:require/2 put(KeyOrName,LocalFile,RemoteFile) -> Fun = fun(Ftp) -> send(Ftp,LocalFile,RemoteFile) end, open_and_do(KeyOrName,Fun). @@ -85,6 +86,7 @@ put(KeyOrName,LocalFile,RemoteFile) -> %%% %%% <p>The config file must be as for put/3.</p> %%% @see put/3 +%%% @see ct:require/2 get(KeyOrName,RemoteFile,LocalFile) -> Fun = fun(Ftp) -> recv(Ftp,RemoteFile,LocalFile) end, open_and_do(KeyOrName,Fun). @@ -105,6 +107,10 @@ get(KeyOrName,RemoteFile,LocalFile) -> %%% simply use <code>Key</code>, the configuration variable name, to %%% specify the target. Note that a connection that has no associated target %%% name can only be closed with the handle value.</p> +%%% +%%% <p>See <c>ct:require/2</c> for how to create a new <c>Name</c></p> +%%% +%%% @see ct:require/2 open(KeyOrName) -> case ct_util:get_key_from_name(KeyOrName) of {ok,node} -> diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl index 5aab4dd2dd..1f01d84601 100644 --- a/lib/common_test/src/ct_gen_conn.erl +++ b/lib/common_test/src/ct_gen_conn.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-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 @@ -27,7 +27,7 @@ -compile(export_all). -export([start/4, stop/1]). --export([call/2, do_within_time/2]). +-export([call/2, call/3, return/2, do_within_time/2]). -ifdef(debug). -define(dbg,true). @@ -39,17 +39,24 @@ name, address, init_data, + reconnect = true, + forward = false, + use_existing = true, + old = false, conn_pid, cb_state, ct_util_server}). %%%----------------------------------------------------------------- -%%% @spec start(Name,Address,InitData,CallbackMod) -> +%%% @spec start(Address,InitData,CallbackMod,Opts) -> %%% {ok,Handle} | {error,Reason} %%% Name = term() %%% CallbackMod = atom() %%% InitData = term() %%% Address = term() +%%% Opts = [Opt] +%%% Opt = {name,Name} | {use_existing_connection,boolean()} | +%%% {reconnect,boolean()} | {forward_messages,boolean()} %%% %%% @doc Open a connection and start the generic connection owner process. %%% @@ -60,50 +67,67 @@ %%% <code>InitData</code> and returna %%% <code>{ok,ConnectionPid,State}</code> or %%% <code>{error,Reason}</code>.</p> +%%% +%%% If no name is given, the <code>Name</code> argument in init/3 will +%%% have the value <code>undefined</code>. +%%% +%%% The callback modules must also export +%%% ``` +%%% handle_msg(Msg,From,State) -> {reply,Reply,State} | +%%% {noreply,State} | +%%% {stop,Reply,State} +%%% terminate(ConnectionPid,State) -> term() +%%% close(Handle) -> term() +%%% ''' +%%% +%%% The <code>close/1</code> callback function is actually a callback +%%% for ct_util, for closing registered connections when +%%% ct_util_server is terminated. <code>Handle</code> is the Pid of +%%% the ct_gen_conn process. +%%% +%%% If option <code>reconnect</code> is <code>true</code>, then the +%%% callback must also export +%%% ``` +%%% reconnect(Address,State) -> {ok,ConnectionPid,State} +%%% ''' +%%% +%%% If option <code>forward_messages</code> is <ocde>true</code>, then +%%% the callback must also export +%%% ``` +%%% handle_msg(Msg,State) -> {noreply,State} | {stop,State} +%%% ''' +%%% +%%% An old interface still exists. This is used by ct_telnet, ct_ftp +%%% and ct_ssh. The start function then has an explicit +%%% <code>Name</code> argument, and no <code>Opts</code> argument. The +%%% callback must export: +%%% +%%% ``` +%%% init(Name,Address,InitData) -> {ok,ConnectionPid,State} +%%% handle_msg(Msg,State) -> {Reply,State} +%%% reconnect(Address,State) -> {ok,ConnectionPid,State} +%%% terminate(ConnectionPid,State) -> term() +%%% close(Handle) -> term() +%%% ''' +%%% +start(Address,InitData,CallbackMod,Opts) when is_list(Opts) -> + do_start(Address,InitData,CallbackMod,Opts); start(Name,Address,InitData,CallbackMod) -> - case ct_util:does_connection_exist(Name,Address,CallbackMod) of - {ok,Pid} -> - log("ct_gen_conn:start","Using existing connection!\n",[]), - {ok,Pid}; - false -> - Self = self(), - Pid = spawn(fun() -> - init_gen(Self, #gen_opts{callback=CallbackMod, - name=Name, - address=Address, - init_data=InitData}) - end), - MRef = erlang:monitor(process,Pid), - receive - {connected,Pid} -> - erlang:demonitor(MRef, [flush]), - ct_util:register_connection(Name,Address,CallbackMod,Pid), - {ok,Pid}; - {Error,Pid} -> - receive {'DOWN',MRef,process,_,_} -> ok end, - Error; - {'DOWN',MRef,process,_,Reason} -> - log("ct_gen_conn:start", - "Connection process died: ~p\n", - [Reason]), - {error,{connection_process_died,Reason}} - end - end. - + do_start(Address,InitData,CallbackMod,[{name,Name},{old,true}]). %%%----------------------------------------------------------------- %%% @spec stop(Handle) -> ok %%% Handle = handle() %%% -%%% @doc Close the telnet connection and stop the process managing it. +%%% @doc Close the connection and stop the process managing it. stop(Pid) -> - call(Pid,stop). + call(Pid,stop,5000). %%%----------------------------------------------------------------- %%% @spec log(Heading,Format,Args) -> ok %%% %%% @doc Log activities on the current connection (tool-internal use only). -%%% @see ct_logs:log/3 +%%% @see ct_logs:log/3 log(Heading,Format,Args) -> log(log,[Heading,Format,Args]). @@ -111,7 +135,7 @@ log(Heading,Format,Args) -> %%% @spec start_log(Heading) -> ok %%% %%% @doc Log activities on the current connection (tool-internal use only). -%%% @see ct_logs:start_log/1 +%%% @see ct_logs:start_log/1 start_log(Heading) -> log(start_log,[Heading]). @@ -119,7 +143,7 @@ start_log(Heading) -> %%% @spec cont_log(Format,Args) -> ok %%% %%% @doc Log activities on the current connection (tool-internal use only). -%%% @see ct_logs:cont_log/2 +%%% @see ct_logs:cont_log/2 cont_log(Format,Args) -> log(cont_log,[Format,Args]). @@ -127,7 +151,7 @@ cont_log(Format,Args) -> %%% @spec end_log() -> ok %%% %%% @doc Log activities on the current connection (tool-internal use only). -%%% @see ct_logs:end_log/0 +%%% @see ct_logs:end_log/0 end_log() -> log(end_log,[]). @@ -148,10 +172,10 @@ do_within_time(Fun,Timeout) -> Silent = get(silent), TmpPid = spawn_link(fun() -> put(silent,Silent), R = Fun(), - Self ! {self(),R} + Self ! {self(),R} end), ConnPid = get(conn_pid), - receive + receive {TmpPid,Result} -> Result; {'EXIT',ConnPid,_Reason}=M -> @@ -159,7 +183,7 @@ do_within_time(Fun,Timeout) -> exit(TmpPid,kill), self() ! M, {error,connection_closed} - after + after Timeout -> exit(TmpPid,kill), receive @@ -176,12 +200,66 @@ do_within_time(Fun,Timeout) -> %%%================================================================= %%% Internal functions -call(Pid,Msg) -> +do_start(Address,InitData,CallbackMod,Opts0) -> + Opts = check_opts(Opts0,#gen_opts{callback=CallbackMod, + address=Address, + init_data=InitData}), + case ct_util:does_connection_exist(Opts#gen_opts.name, + Address,CallbackMod) of + {ok,Pid} when Opts#gen_opts.use_existing -> + log("ct_gen_conn:start","Using existing connection!\n",[]), + {ok,Pid}; + {ok,Pid} when not Opts#gen_opts.use_existing -> + {error,{connection_exists,Pid}}; + false -> + do_start(Opts) + end. + +do_start(Opts) -> + Self = self(), + Pid = spawn(fun() -> init_gen(Self, Opts) end), + MRef = erlang:monitor(process,Pid), + receive + {connected,Pid} -> + erlang:demonitor(MRef, [flush]), + ct_util:register_connection(Opts#gen_opts.name, Opts#gen_opts.address, + Opts#gen_opts.callback, Pid), + {ok,Pid}; + {Error,Pid} -> + receive {'DOWN',MRef,process,_,_} -> ok end, + Error; + {'DOWN',MRef,process,_,Reason} -> + log("ct_gen_conn:start", + "Connection process died: ~p\n", + [Reason]), + {error,{connection_process_died,Reason}} + end. + +check_opts(Opts0) -> + check_opts(Opts0,#gen_opts{}). + +check_opts([{name,Name}|T],Opts) -> + check_opts(T,Opts#gen_opts{name=Name}); +check_opts([{reconnect,Bool}|T],Opts) -> + check_opts(T,Opts#gen_opts{reconnect=Bool}); +check_opts([{forward_messages,Bool}|T],Opts) -> + check_opts(T,Opts#gen_opts{forward=Bool}); +check_opts([{use_existing_connection,Bool}|T],Opts) -> + check_opts(T,Opts#gen_opts{use_existing=Bool}); +check_opts([{old,Bool}|T],Opts) -> + check_opts(T,Opts#gen_opts{old=Bool}); +check_opts([],Opts) -> + Opts. + +call(Pid, Msg) -> + call(Pid, Msg, infinity). + +call(Pid, Msg, Timeout) -> MRef = erlang:monitor(process,Pid), Ref = make_ref(), Pid ! {Msg,{self(),Ref}}, receive - {Ref, Result} -> + {Ref, Result} -> erlang:demonitor(MRef, [flush]), case Result of {retry,_Data} -> @@ -189,8 +267,15 @@ call(Pid,Msg) -> Other -> Other end; - {'DOWN',MRef,process,_,Reason} -> + {'DOWN',MRef,process,_,Reason} -> {error,{process_down,Pid,Reason}} + after Timeout -> + erlang:demonitor(MRef, [flush]), + log("ct_gen_conn", + "Connection process ~p not responding. Killing now!", + [Pid]), + exit(Pid, kill), + {error,{process_down,Pid,forced_termination}} end. return({To,Ref},Result) -> @@ -198,36 +283,47 @@ return({To,Ref},Result) -> init_gen(Parent,Opts) -> process_flag(trap_exit,true), - CtUtilServer = whereis(ct_util_server), - link(CtUtilServer), put(silent,false), - case catch (Opts#gen_opts.callback):init(Opts#gen_opts.name, - Opts#gen_opts.address, - Opts#gen_opts.init_data) of + try (Opts#gen_opts.callback):init(Opts#gen_opts.name, + Opts#gen_opts.address, + Opts#gen_opts.init_data) of {ok,ConnPid,State} when is_pid(ConnPid) -> link(ConnPid), put(conn_pid,ConnPid), + CtUtilServer = whereis(ct_util_server), + link(CtUtilServer), Parent ! {connected,self()}, loop(Opts#gen_opts{conn_pid=ConnPid, cb_state=State, ct_util_server=CtUtilServer}); {error,Reason} -> Parent ! {{error,Reason},self()} + catch + throw:{error,Reason} -> + Parent ! {{error,Reason},self()} end. loop(Opts) -> receive {'EXIT',Pid,Reason} when Pid==Opts#gen_opts.conn_pid -> - log("Connection down!\nOpening new!","Reason: ~p\nAddress: ~p\n", - [Reason,Opts#gen_opts.address]), - case reconnect(Opts) of - {ok, NewPid, NewState} -> - link(NewPid), - put(conn_pid,NewPid), - loop(Opts#gen_opts{conn_pid=NewPid,cb_state=NewState}); - Error -> + case Opts#gen_opts.reconnect of + true -> + log("Connection down!\nOpening new!", + "Reason: ~p\nAddress: ~p\n", + [Reason,Opts#gen_opts.address]), + case reconnect(Opts) of + {ok, NewPid, NewState} -> + link(NewPid), + put(conn_pid,NewPid), + loop(Opts#gen_opts{conn_pid=NewPid,cb_state=NewState}); + Error -> + ct_util:unregister_connection(self()), + log("Reconnect failed. Giving up!","Reason: ~p\n", + [Error]) + end; + false -> ct_util:unregister_connection(self()), - log("Reconnect failed. Giving up!","Reason: ~p\n",[Error]) + log("Connection closed!","Reason: ~p\n",[Reason]) end; {'EXIT',Pid,Reason} -> case Opts#gen_opts.ct_util_server of @@ -252,24 +348,40 @@ loop(Opts) -> loop(Opts); {{retry,{_Error,_Name,_CPid,Msg}}, From} -> log("Rerunning command","Connection reestablished. Rerunning command...",[]), - {Return,NewState} = + {Return,NewState} = (Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state), return(From, Return), - loop(Opts#gen_opts{cb_state=NewState}); - {Msg,From={Pid,_Ref}} when is_pid(Pid) -> - {Return,NewState} = + loop(Opts#gen_opts{cb_state=NewState}); + {Msg,From={Pid,_Ref}} when is_pid(Pid), Opts#gen_opts.old==true -> + {Return,NewState} = (Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state), return(From, Return), - loop(Opts#gen_opts{cb_state=NewState}) + loop(Opts#gen_opts{cb_state=NewState}); + {Msg,From={Pid,_Ref}} when is_pid(Pid) -> + case (Opts#gen_opts.callback):handle_msg(Msg,From, + Opts#gen_opts.cb_state) of + {reply,Reply,NewState} -> + return(From,Reply), + loop(Opts#gen_opts{cb_state=NewState}); + {noreply,NewState} -> + loop(Opts#gen_opts{cb_state=NewState}); + {stop,Reply,NewState} -> + ct_util:unregister_connection(self()), + (Opts#gen_opts.callback):terminate(Opts#gen_opts.conn_pid, + NewState), + return(From,Reply) + end; + Msg when Opts#gen_opts.forward==true -> + case (Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state) of + {noreply,NewState} -> + loop(Opts#gen_opts{cb_state=NewState}); + {stop,NewState} -> + ct_util:unregister_connection(self()), + (Opts#gen_opts.callback):terminate(Opts#gen_opts.conn_pid, + NewState) + end end. -nozero({ok,S}) when is_list(S) -> - {ok,[C || C <- S, - C=/=0, - C=/=13]}; -nozero(M) -> - M. - reconnect(Opts) -> (Opts#gen_opts.callback):reconnect(Opts#gen_opts.address, Opts#gen_opts.cb_state). @@ -277,10 +389,8 @@ reconnect(Opts) -> log(Func,Args) -> case get(silent) of - true when not ?dbg-> + true when not ?dbg-> ok; _ -> apply(ct_logs,Func,Args) end. - - diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 0fe6e03079..1bcc63738e 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -48,7 +48,7 @@ %% @doc Called before any suites are started -spec init(State :: term()) -> ok | - {error, Reason :: term()}. + {fail, Reason :: term()}. init(Opts) -> call(get_new_hooks(Opts, undefined) ++ get_builtin_hooks(Opts), ok, init, []). @@ -192,12 +192,12 @@ call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) -> case lists:keyfind(NewId, #ct_hook_config.id, Hooks) of false when NextFun =:= undefined -> {Hooks ++ [NewHook], - [{NewId, call_init} | Rest]}; + Rest ++ [{NewId, call_init}]}; ExistingHook when is_tuple(ExistingHook) -> {Hooks, Rest}; _ -> {Hooks ++ [NewHook], - [{NewId, call_init}, {NewId,NextFun} | Rest]} + Rest ++ [{NewId, call_init}, {NewId,NextFun}]} end, call(resort(NewRest,NewHooks,Meta), Config, Meta, NewHooks) catch Error:Reason -> @@ -353,11 +353,10 @@ pos(Id,[_|Rest],Num) -> pos(Id,Rest,Num+1). - catch_apply(M,F,A, Default) -> try apply(M,F,A) - catch error:Reason -> + catch _:Reason -> case erlang:get_stacktrace() of %% Return the default if it was the CTH module which did not have the function. [{M,F,A,_}|_] when Reason == undef -> diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 1ccbdc3718..0b7a8bb075 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -28,23 +28,25 @@ -module(ct_logs). --export([init/1,close/2,init_tc/1,end_tc/1]). --export([get_log_dir/0,get_log_dir/1]). --export([log/3,start_log/1,cont_log/2,end_log/0]). --export([set_stylesheet/2,clear_stylesheet/1]). --export([add_external_logs/1,add_link/3]). +-export([init/2, close/2, init_tc/1, end_tc/1]). +-export([get_log_dir/0, get_log_dir/1]). +-export([log/3, start_log/1, cont_log/2, end_log/0]). +-export([set_stylesheet/2, clear_stylesheet/1]). +-export([add_external_logs/1, add_link/3]). -export([make_last_run_index/0]). -export([make_all_suites_index/1,make_all_runs_index/1]). --export([get_ts_html_wrapper/3]). --export([xhtml/2, locate_default_css_file/0, make_relative/1]). +-export([get_ts_html_wrapper/4]). +-export([xhtml/2, locate_priv_file/1, make_relative/1]). +-export([insert_javascript/1]). %% Logging stuff directly from testcase --export([tc_log/3,tc_log/4,tc_log_async/3,tc_print/3,tc_pal/3,ct_log/3, - basic_html/0]). +-export([tc_log/3, tc_log/4, tc_log_async/3, tc_print/3, tc_print/4, + tc_pal/3, tc_pal/4, ct_log/3, basic_html/0]). %% Simulate logger process for use without ct environment running -export([simulate/0]). +-include("ct.hrl"). -include("ct_event.hrl"). -include("ct_util.hrl"). -include_lib("kernel/include/file.hrl"). @@ -56,7 +58,6 @@ -define(all_runs_name, "all_runs.html"). -define(index_name, "index.html"). -define(totals_name, "totals.info"). --define(css_default, "ct_default.css"). -define(table_color1,"#ADD8E6"). -define(table_color2,"#E4F0FE"). @@ -79,9 +80,9 @@ %%% started. A new directory named ct_run.<timestamp> is created %%% and all logs are stored under this directory.</p> %%% -init(Mode) -> +init(Mode, Verbosity) -> Self = self(), - Pid = spawn_link(fun() -> logger(Self,Mode) end), + Pid = spawn_link(fun() -> logger(Self, Mode, Verbosity) end), MRef = erlang:monitor(process,Pid), receive {started,Pid,Result} -> @@ -240,7 +241,7 @@ end_tc(TCPid) -> %%% activity it is. <code>Format</code> and <code>Args</code> is the %%% data to log (as in <code>io:format(Format,Args)</code>).</p> log(Heading,Format,Args) -> - cast({log,sync,self(),group_leader(), + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, [{int_header(),[log_timestamp(now()),Heading]}, {Format,Args}, {int_footer(),[]}]}), @@ -262,7 +263,7 @@ log(Heading,Format,Args) -> %%% @see cont_log/2 %%% @see end_log/0 start_log(Heading) -> - cast({log,sync,self(),group_leader(), + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, [{int_header(),[log_timestamp(now()),Heading]}]}), ok. @@ -277,7 +278,8 @@ cont_log([],[]) -> ok; cont_log(Format,Args) -> maybe_log_timestamp(), - cast({log,sync,self(),group_leader(),[{Format,Args}]}), + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, + [{Format,Args}]}), ok. %%%----------------------------------------------------------------- @@ -288,7 +290,8 @@ cont_log(Format,Args) -> %%% @see start_log/1 %%% @see cont_log/2 end_log() -> - cast({log,sync,self(),group_leader(),[{int_footer(), []}]}), + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, + [{int_footer(), []}]}), ok. @@ -321,10 +324,16 @@ add_link(Heading,File,Type) -> [filename:join("log_private",File),Type,File]). - %%%----------------------------------------------------------------- %%% @spec tc_log(Category,Format,Args) -> ok +%%% @equiv tc_log(Category,?STD_IMPORTANCE,Format,Args) +tc_log(Category,Format,Args) -> + tc_log(Category,?STD_IMPORTANCE,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec tc_log(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -333,19 +342,26 @@ add_link(Heading,File,Type) -> %%% <p>This function is called by <code>ct</code> when logging %%% stuff directly from a testcase (i.e. not from within the CT %%% framework).</p> -tc_log(Category,Format,Args) -> - tc_log(Category,"User",Format,Args). +tc_log(Category,Importance,Format,Args) -> + tc_log(Category,Importance,"User",Format,Args). -tc_log(Category,Printer,Format,Args) -> - cast({log,sync,self(),group_leader(),[{div_header(Category,Printer),[]}, - {Format,Args}, - {div_footer(),[]}]}), +tc_log(Category,Importance,Printer,Format,Args) -> + cast({log,sync,self(),group_leader(),Category,Importance, + [{div_header(Category,Printer),[]}, + {Format,Args}, + {div_footer(),[]}]}), ok. - %%%----------------------------------------------------------------- %%% @spec tc_log_async(Category,Format,Args) -> ok +%%% @equiv tc_log_async(Category,?STD_IMPORTANCE,Format,Args) +tc_log_async(Category,Format,Args) -> + tc_log_async(Category,?STD_IMPORTANCE,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec tc_log_async(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -356,40 +372,66 @@ tc_log(Category,Printer,Format,Args) -> %%% to avoid deadlocks when e.g. the hook that handles SASL printouts %%% prints to the test case log file at the same time test server %%% asks ct_logs for an html wrapper.</p> -tc_log_async(Category,Format,Args) -> - cast({log,async,self(),group_leader(),[{div_header(Category),[]}, - {Format,Args}, - {div_footer(),[]}]}), +tc_log_async(Category,Importance,Format,Args) -> + cast({log,async,self(),group_leader(),Category,Importance, + [{div_header(Category),[]}, + {Format,Args}, + {div_footer(),[]}]}), ok. +%%%----------------------------------------------------------------- +%%% @spec tc_print(Category,Format,Args) +%%% @equiv tc_print(Category,?STD_IMPORTANCE,Format,Args) +tc_print(Category,Format,Args) -> + tc_print(Category,?STD_IMPORTANCE,Format,Args). %%%----------------------------------------------------------------- -%%% @spec tc_print(Category,Format,Args) -> ok +%%% @spec tc_print(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% %%% @doc Console printout from a testcase. %%% %%% <p>This function is called by <code>ct</code> when printing -%%% stuff a testcase on the user console.</p> -tc_print(Category,Format,Args) -> - Head = get_heading(Category), - io:format(user, lists:concat([Head,Format,"\n\n"]), Args), - ok. +%%% stuff from a testcase on the user console.</p> +tc_print(Category,Importance,Format,Args) -> + VLvl = case ct_util:get_testdata({verbosity,Category}) of + undefined -> + ct_util:get_testdata({verbosity,'$unspecified'}); + {error,bad_invocation} -> + ?MAX_VERBOSITY; + Val -> + Val + end, + if Importance >= (100-VLvl) -> + Head = get_heading(Category), + io:format(user, lists:concat([Head,Format,"\n\n"]), Args), + ok; + true -> + ok + end. get_heading(default) -> - io_lib:format("-----------------------------" + io_lib:format("\n-----------------------------" "-----------------------\n~s\n", [log_timestamp(now())]); get_heading(Category) -> - io_lib:format("-----------------------------" + io_lib:format("\n-----------------------------" "-----------------------\n~s ~w\n", [log_timestamp(now()),Category]). %%%----------------------------------------------------------------- %%% @spec tc_pal(Category,Format,Args) -> ok +%%% @equiv tc_pal(Category,?STD_IMPORTANCE,Format,Args) -> ok +tc_pal(Category,Format,Args) -> + tc_pal(Category,?STD_IMPORTANCE,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec tc_pal(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -398,16 +440,17 @@ get_heading(Category) -> %%% <p>This function is called by <code>ct</code> when logging %%% stuff directly from a testcase. The info is written both in the %%% log and on the console.</p> -tc_pal(Category,Format,Args) -> - tc_print(Category,Format,Args), - cast({log,sync,self(),group_leader(),[{div_header(Category),[]}, - {Format,Args}, - {div_footer(),[]}]}), +tc_pal(Category,Importance,Format,Args) -> + tc_print(Category,Importance,Format,Args), + cast({log,sync,self(),group_leader(),Category,Importance, + [{div_header(Category),[]}, + {Format,Args}, + {div_footer(),[]}]}), ok. %%%----------------------------------------------------------------- -%%% @spec tc_pal(Category,Format,Args) -> ok +%%% @spec ct_pal(Category,Format,Args) -> ok %%% Category = atom() %%% Format = string() %%% Args = list() @@ -445,7 +488,7 @@ maybe_log_timestamp() -> {MS,S,_} -> ok; _ -> - cast({log,sync,self(),group_leader(), + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, [{"<i>~s</i>",[log_timestamp({MS,S,US})]}]}) end. @@ -469,7 +512,7 @@ log_timestamp({MS,S,US}) -> stylesheet, async_print_jobs}). -logger(Parent,Mode) -> +logger(Parent, Mode, Verbosity) -> register(?MODULE,self()), %%! Below is a temporary workaround for the limitation of @@ -502,26 +545,27 @@ logger(Parent,Mode) -> %% dir) so logs are independent of Common Test installation {ok,Cwd} = file:get_cwd(), CTPath = code:lib_dir(common_test), - CSSFileSrc = filename:join(filename:join(CTPath, "priv"), - ?css_default), - CSSFileDestTop = filename:join(Cwd, ?css_default), - CSSFileDestRun = filename:join(AbsDir, ?css_default), - case file:copy(CSSFileSrc, CSSFileDestTop) of - {error,Reason0} -> + PrivFiles = [?css_default,?jquery_script,?tablesorter_script], + PrivFilesSrc = [filename:join(filename:join(CTPath, "priv"), F) || + F <- PrivFiles], + PrivFilesDestTop = [filename:join(Cwd, F) || F <- PrivFiles], + PrivFilesDestRun = [filename:join(AbsDir, F) || F <- PrivFiles], + case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of + {error,Src1,Dest1,Reason1} -> io:format(user, "ERROR! "++ - "CSS file ~p could not be copied to ~p. "++ - "Reason: ~p~n", - [CSSFileSrc,CSSFileDestTop,Reason0]), - exit({css_file_error,CSSFileDestTop}); - _ -> - case file:copy(CSSFileSrc, CSSFileDestRun) of - {error,Reason1} -> + "Priv file ~p could not be copied to ~p. "++ + "Reason: ~p~n", + [Src1,Dest1,Reason1]), + exit({priv_file_error,Dest1}); + ok -> + case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of + {error,Src2,Dest2,Reason2} -> io:format(user, "ERROR! "++ - "CSS file ~p could not be copied to ~p. "++ - "Reason: ~p~n", - [CSSFileSrc,CSSFileDestRun,Reason1]), - exit({css_file_error,CSSFileDestRun}); - _ -> + "Priv file ~p could not be copied to ~p. "++ + "Reason: ~p~n", + [Src2,Dest2,Reason2]), + exit({priv_file_error,Dest2}); + ok -> ok end end @@ -541,6 +585,23 @@ logger(Parent,Mode) -> [log_timestamp(now()),"Common Test Logger started"]), Parent ! {started,self(),{Time,filename:absname("")}}, set_evmgr_gl(CtLogFd), + + %% save verbosity levels in dictionary for fast lookups + io:format(CtLogFd, "\nVERBOSITY LEVELS:\n", []), + case proplists:get_value('$unspecified', Verbosity) of + undefined -> ok; + GenLvl -> io:format(CtLogFd, "~-25s~3w~n", + ["general level",GenLvl]) + end, + [begin put({verbosity,Cat},VLvl), + if Cat == '$unspecified' -> + ok; + true -> + io:format(CtLogFd, "~-25w~3w~n", [Cat,VLvl]) + end + end || {Cat,VLvl} <- Verbosity], + io:nl(CtLogFd), + logger_loop(#logger_state{parent=Parent, log_dir=AbsDir, start_time=Time, @@ -549,31 +610,58 @@ logger(Parent,Mode) -> tc_groupleaders=[], async_print_jobs=[]}). +copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) -> + case file:copy(SrcF, DestF) of + {error,Reason} -> + {error,SrcF,DestF,Reason}; + _ -> + copy_priv_files(SrcFs, DestFs) + end; +copy_priv_files([], []) -> + ok. + logger_loop(State) -> receive - {log,SyncOrAsync,Pid,GL,List} -> - case get_groupleader(Pid, GL, State) of - {tc_log,TCGL,TCGLs} -> - case erlang:is_process_alive(TCGL) of - true -> - State1 = print_to_log(SyncOrAsync, Pid, TCGL, - List, State), - logger_loop(State1#logger_state{tc_groupleaders = - TCGLs}); - false -> - %% Group leader is dead, so write to the - %% CtLog instead - Fd = State#logger_state.ct_log_fd, - [begin io:format(Fd,Str,Args),io:nl(Fd) end || + {log,SyncOrAsync,Pid,GL,Category,Importance,List} -> + VLvl = case Category of + ct_internal -> + ?MAX_VERBOSITY; + _ -> + case get({verbosity,Category}) of + undefined -> get({verbosity,'$unspecified'}); + Val -> Val + end + end, + if Importance >= (100-VLvl) -> + case get_groupleader(Pid, GL, State) of + {tc_log,TCGL,TCGLs} -> + case erlang:is_process_alive(TCGL) of + true -> + State1 = print_to_log(SyncOrAsync, Pid, + TCGL, List, State), + logger_loop(State1#logger_state{ + tc_groupleaders = TCGLs}); + false -> + %% Group leader is dead, so write to the + %% CtLog instead + Fd = State#logger_state.ct_log_fd, + [begin io:format(Fd,Str,Args), + io:nl(Fd) end || {Str,Args} <- List], + logger_loop(State) + end; + {ct_log,Fd,TCGLs} -> + [begin io:format(Fd,Str,Args),io:nl(Fd) end || {Str,Args} <- List], - logger_loop(State) + logger_loop(State#logger_state{ + tc_groupleaders = TCGLs}) end; - {ct_log,Fd,TCGLs} -> - [begin io:format(Fd,Str,Args),io:nl(Fd) end || - {Str,Args} <- List], - logger_loop(State#logger_state{tc_groupleaders = TCGLs}) - end; + true -> + logger_loop(State) + end; {{init_tc,TCPid,GL,RefreshLog},From} -> + %% make sure no IO for this test case from the + %% CT logger gets rejected + test_server:permit_io(GL, self()), print_style(GL, State#logger_state.stylesheet), set_evmgr_gl(GL), TCGLs = add_tc_gl(TCPid,GL,State), @@ -659,13 +747,24 @@ create_io_fun(FromPid, State) -> print_to_log(sync, FromPid, TCGL, List, State) -> IoFun = create_io_fun(FromPid, State), - io:format(TCGL, "~s", [lists:foldl(IoFun, [], List)]), + %% in some situations (exceptions), the printout is made from the + %% test server IO process and there's no valid group leader to send to + IoProc = if FromPid /= TCGL -> TCGL; + true -> State#logger_state.ct_log_fd + end, + io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)]), State; print_to_log(async, FromPid, TCGL, List, State) -> IoFun = create_io_fun(FromPid, State), + %% in some situations (exceptions), the printout is made from the + %% test server IO process and there's no valid group leader to send to + IoProc = if FromPid /= TCGL -> TCGL; + true -> State#logger_state.ct_log_fd + end, Printer = fun() -> - io:format(TCGL, "~s", [lists:foldl(IoFun, [], List)]) + test_server:permit_io(IoProc, self()), + io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)]) end, case State#logger_state.async_print_jobs of [] -> @@ -770,7 +869,7 @@ set_evmgr_gl(GL) -> open_ctlog() -> {ok,Fd} = file:open(?ct_log_name,[write]), - io:format(Fd, header("Common Test Framework Log"), []), + io:format(Fd, header("Common Test Framework Log", {[],[1,2],[]}), []), case file:consult(ct_run:variables_file_name("../")) of {ok,Vars} -> io:format(Fd, config_table(Vars), []); @@ -1080,14 +1179,14 @@ total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) -> integer_to_list(UserSkip),integer_to_list(AutoSkip)} end, [xhtml("<tr valign=top>\n", - ["<tr class=\"",odd_or_even(),"\">\n"]), + ["</tbody>\n<tfoot>\n<tr class=\"",odd_or_even(),"\">\n"]), "<td><b>Total</b></td>\n", Label, TimestampCell, "<td align=right><b>",integer_to_list(Success),"<b></td>\n", "<td align=right><b>",integer_to_list(Fail),"<b></td>\n", "<td align=right>",integer_to_list(AllSkip), " (",UserSkipStr,"/",AutoSkipStr,")</td>\n", "<td align=right><b>",integer_to_list(NotBuilt),"<b></td>\n", - AllInfo, "</tr>\n"]. + AllInfo, "</tr>\n</tfoot>\n"]. not_built(_BaseName,_LogDir,_All,[]) -> 0; @@ -1144,10 +1243,12 @@ index_header(Label, StartTime) -> Head = case Label of undefined -> - header("Test Results", format_time(StartTime)); + header("Test Results", format_time(StartTime), + {[],[1],[2,3,4,5]}); _ -> header("Test Results for '" ++ Label ++ "'", - format_time(StartTime)) + format_time(StartTime), + {[],[1],[2,3,4,5]}) end, [Head | ["<center>\n", @@ -1159,15 +1260,17 @@ index_header(Label, StartTime) -> "\">COMMON TEST FRAMEWORK LOG</a>\n</div>"]), xhtml("<br>\n", "<br /><br /><br />\n"), xhtml(["<table border=\"3\" cellpadding=\"5\" " - "bgcolor=\"",?table_color3,"\">\n"], "<table>\n"), + "bgcolor=\"",?table_color3,"\">\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n<tr>\n"]), "<th><b>Test Name</b></th>\n", xhtml(["<th><font color=\"",?table_color3,"\">_</font>Ok" "<font color=\"",?table_color3,"\">_</font></th>\n"], "<th>Ok</th>\n"), "<th>Failed</th>\n", "<th>Skipped", xhtml("<br>", "<br />"), "(User/Auto)</th>\n" - "<th>Missing", xhtml("<br>", "<br />"), "Suites</th>\n" - "\n"]]. + "<th>Missing", xhtml("<br>", "<br />"), "Suites</th>\n", + xhtml("", "</tr>\n</thead>\n<tbody>\n")]]. all_suites_index_header() -> {ok,Cwd} = file:get_cwd(), @@ -1180,12 +1283,14 @@ all_suites_index_header(IndexDir) -> AllRunsLink = xhtml(["<a href=\"",?all_runs_name,"\">",AllRuns,"</a>\n"], ["<div id=\"button_holder\" class=\"btn\">\n" "<a href=\"",?all_runs_name,"\">",AllRuns,"</a>\n</div>"]), - [header("Test Results") | + [header("Test Results", {[3],[1,2,8,9,10],[4,5,6,7]}) | ["<center>\n", AllRunsLink, xhtml("<br><br>\n", "<br /><br />\n"), xhtml(["<table border=\"3\" cellpadding=\"5\" " - "bgcolor=\"",?table_color2,"\">\n"], "<table>\n"), + "bgcolor=\"",?table_color2,"\">\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n<tr>\n"]), "<th>Test Name</th>\n", "<th>Label</th>\n", "<th>Test Run Started</th>\n", @@ -1198,7 +1303,7 @@ all_suites_index_header(IndexDir) -> "<th>Node</th>\n", "<th>CT Log</th>\n", "<th>Old Runs</th>\n", - "\n"]]. + xhtml("", "</tr>\n</thead>\n<tbody>\n")]]. all_runs_header() -> {ok,Cwd} = file:get_cwd(), @@ -1210,10 +1315,12 @@ all_runs_header() -> "<a href=\"",?index_name, "\">TEST INDEX PAGE</a>\n</div>"]), xhtml("<br>\n", "<br /><br />\n")], - [header(Title) | + [header(Title, {[1],[2,3,5],[4,6,7,8,9,10]}) | ["<center>\n", IxLink, xhtml(["<table border=\"3\" cellpadding=\"5\" " - "bgcolor=\"",?table_color1,"\">\n"], "<table>\n"), + "bgcolor=\"",?table_color1,"\">\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n<tr>\n"]), "<th><b>History</b></th>\n" "<th><b>Node</b></th>\n" "<th><b>Label</b></th>\n" @@ -1225,23 +1332,29 @@ all_runs_header() -> "<th>Ok</th>\n"), "<th>Failed</th>\n" "<th>Skipped<br>(User/Auto)</th>\n" - "<th>Missing<br>Suites</th>\n" - "\n"]]. + "<th>Missing<br>Suites</th>\n", + xhtml("", "</tr>\n</thead>\n<tbody>\n")]]. -header(Title) -> - header1(Title, ""). -header(Title, SubTitle) -> - header1(Title, SubTitle). +header(Title, TableCols) -> + header1(Title, "", TableCols). +header(Title, SubTitle, TableCols) -> + header1(Title, SubTitle, TableCols). -header1(Title, SubTitle) -> +header1(Title, SubTitle, TableCols) -> SubTitleHTML = if SubTitle =/= "" -> ["<center>\n", "<h3>" ++ SubTitle ++ "</h3>\n", xhtml("</center>\n<br>\n", "</center>\n<br />\n")]; - true -> xhtml("<br>\n", "<br />\n") + true -> xhtml("<br>", "<br />") end, CSSFile = xhtml(fun() -> "" end, - fun() -> make_relative(locate_default_css_file()) end), + fun() -> make_relative(locate_priv_file(?css_default)) end), + JQueryFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?jquery_script)) end), + TableSorterFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?tablesorter_script)) end), [xhtml(["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", "<html>\n"], ["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n", @@ -1252,7 +1365,17 @@ header1(Title, SubTitle) -> "<title>" ++ Title ++ " " ++ SubTitle ++ "</title>\n", "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", xhtml("", - ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]), + ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">\n"]), + xhtml("", + ["<script type=\"text/javascript\" src=\"",JQueryFile, + "\"></script>\n"]), + xhtml("", + ["<script type=\"text/javascript\" src=\"",TableSorterFile, + "\"></script>\n"]), + xhtml(fun() -> "" end, + fun() -> insert_javascript({tablesorter,?sortable_table_name, + TableCols}) + end), "</head>\n", body_tag(), "<center>\n", @@ -1264,6 +1387,10 @@ index_footer() -> ["</table>\n" "</center>\n" | footer()]. +all_runs_index_footer() -> + ["</tbody>\n</table>\n" + "</center>\n" | footer()]. + footer() -> ["<center>\n", xhtml("<br><br>\n<hr>\n", "<br /><br />\n"), @@ -1275,7 +1402,8 @@ footer() -> xhtml("<br>\n", "<br />\n"), xhtml("</font></p>\n", "</div>\n"), "</center>\n" - "</body>\n"]. + "</body>\n" + "</html>\n"]. body_tag() -> @@ -1291,7 +1419,7 @@ current_time() -> format_time({{Y, Mon, D}, {H, Min, S}}) -> Weekday = weekday(calendar:day_of_the_week(Y, Mon, D)), - lists:flatten(io_lib:format("~s ~s ~p ~w ~2.2.0w:~2.2.0w:~2.2.0w", + lists:flatten(io_lib:format("~s ~s ~2.2.0w ~w ~2.2.0w:~2.2.0w:~2.2.0w", [Weekday, month(Mon), D, Y, H, Min, S])). weekday(1) -> "Mon"; @@ -1417,8 +1545,12 @@ config_table_header() -> [ xhtml(["<h2>Configuration</h2>\n" "<table border=\"3\" cellpadding=\"5\" bgcolor=\"",?table_color1,"\"\n"], - "<h4>CONFIGURATION</h4>\n<table>\n"), - "<tr><th>Key</th><th>Value</th></tr>\n"]. + ["<h4>CONFIGURATION</h4>\n", + "<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n"]), + "<tr><th>Key</th><th>Value</th></tr>\n", + xhtml("", "</thead>\n<tbody>\n") + ]. config_table1([{Key,Value}|Vars]) -> [xhtml(["<tr><td>", atom_to_list(Key), "</td>\n", @@ -1428,7 +1560,7 @@ config_table1([{Key,Value}|Vars]) -> "<td>", io_lib:format("~p",[Value]), "</td>\n</tr>\n"]) | config_table1(Vars)]; config_table1([]) -> - ["</table>\n"]. + ["</tbody>\n</table>\n"]. make_all_runs_index(When) -> @@ -1442,7 +1574,8 @@ make_all_runs_index(When) -> DirsSorted = (catch sort_all_runs(Dirs)), Header = all_runs_header(), Index = [runentry(Dir) || Dir <- DirsSorted], - Result = file:write_file(AbsName,Header++Index++index_footer()), + Result = file:write_file(AbsName,Header++Index++ + all_runs_index_footer()), if When == start -> ok; true -> io:put_chars("done\n") end, @@ -1981,7 +2114,7 @@ simulate() -> simulate_logger_loop() -> receive - {log,_,_,_,List} -> + {log,_,_,_,_,_,List} -> S = [[io_lib:format(Str,Args),io_lib:nl()] || {Str,Args} <- List], io:format("~s",[S]), simulate_logger_loop(); @@ -2078,34 +2211,34 @@ basic_html() -> end. %%%----------------------------------------------------------------- -%%% @spec locate_default_css_file() -> CSSFile +%%% @spec locate_priv_file(FileName) -> PrivFile %%% %%% @doc %%% -locate_default_css_file() -> +locate_priv_file(FileName) -> {ok,CWD} = file:get_cwd(), - CSSFileInCwd = filename:join(CWD, ?css_default), - case filelib:is_file(CSSFileInCwd) of + PrivFileInCwd = filename:join(CWD, FileName), + case filelib:is_file(PrivFileInCwd) of true -> - CSSFileInCwd; + PrivFileInCwd; false -> - CSSResultFile = + PrivResultFile = case {whereis(?MODULE),self()} of {Self,Self} -> %% executed on the ct_logs process - filename:join(get(ct_run_dir), ?css_default); + filename:join(get(ct_run_dir), FileName); _ -> %% executed on other process than ct_logs {ok,RunDir} = get_log_dir(true), - filename:join(RunDir, ?css_default) + filename:join(RunDir, FileName) end, - case filelib:is_file(CSSResultFile) of + case filelib:is_file(PrivResultFile) of true -> - CSSResultFile; + PrivResultFile; false -> %% last resort, try use css file in CT installation CTPath = code:lib_dir(common_test), - filename:join(filename:join(CTPath, "priv"), ?css_default) + filename:join(filename:join(CTPath, "priv"), FileName) end end. @@ -2144,7 +2277,7 @@ make_relative1(DirTs, CwdTs) -> %%% %%% @doc %%% -get_ts_html_wrapper(TestName, PrintLabel, Cwd) -> +get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols) -> TestName1 = if is_list(TestName) -> lists:flatten(TestName); true -> @@ -2204,17 +2337,36 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd) -> "Open Telecom Platform</a><br />\n", "Updated: <!date>", current_time(), "<!/date>", "<br />\n</div>\n"], - CSSFile = xhtml(fun() -> "" end, - fun() -> make_relative(locate_default_css_file(), Cwd) end), + CSSFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?css_default), + Cwd) + end), + JQueryFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?jquery_script), + Cwd) + end), + TableSorterFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?tablesorter_script), + Cwd) + end), + TableSorterScript = + xhtml(fun() -> "" end, + fun() -> insert_javascript({tablesorter, + ?sortable_table_name, + TableCols}) end), {xhtml, ["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n", "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n", "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n", "<head>\n<title>", TestName1, "</title>\n", "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", - "<link rel=\"stylesheet\" href=\"", CSSFile, "\" type=\"text/css\">", - "</head>\n","<body>\n", - LabelStr, "\n"], + "<link rel=\"stylesheet\" href=\"", CSSFile, "\" type=\"text/css\">\n", + "<script type=\"text/javascript\" src=\"", JQueryFile, "\"></script>\n", + "<script type=\"text/javascript\" src=\"", TableSorterFile, "\"></script>\n"] ++ + TableSorterScript ++ ["</head>\n","<body>\n", LabelStr, "\n"], ["<center>\n<br /><hr /><p>\n", "<a href=\"", AllRuns, "\">Test run history\n</a> | ", @@ -2222,3 +2374,89 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd) -> "\">Top level test index\n</a>\n</p>\n", Copyright,"</center>\n</body>\n</html>\n"]} end. + +insert_javascript({tablesorter,_TableName,undefined}) -> + []; + +insert_javascript({tablesorter,TableName, + {DateCols,TextCols,ValCols}}) -> + Headers = + lists:flatten( + lists:sort( + lists:flatmap(fun({Sorter,Cols}) -> + [lists:flatten( + io_lib:format(" ~w: " + "{ sorter: '~s' },\n", + [Col-1,Sorter])) || Col<-Cols] + end, [{"CTDateSorter",DateCols}, + {"CTTextSorter",TextCols}, + {"CTValSorter",ValCols}]))), + Headers1 = string:substr(Headers, 1, length(Headers)-2), + + ["<script type=\"text/javascript\">\n", + "// Parser for date format, e.g: Wed Jul 4 2012 11:24:15\n", + "var monthNames = {};\n", + "monthNames[\"Jan\"] = \"01\"; monthNames[\"Feb\"] = \"02\";\n", + "monthNames[\"Mar\"] = \"03\"; monthNames[\"Apr\"] = \"04\";\n", + "monthNames[\"May\"] = \"05\"; monthNames[\"Jun\"] = \"06\";\n", + "monthNames[\"Jul\"] = \"07\"; monthNames[\"Aug\"] = \"08\";\n", + "monthNames[\"Sep\"] = \"09\"; monthNames[\"Oct\"] = \"10\";\n", + "monthNames[\"Nov\"] = \"11\"; monthNames[\"Dec\"] = \"12\";\n", + "$.tablesorter.addParser({\n", + " id: 'CTDateSorter',\n", + " is: function(s) {\n", + " return false; },\n", + " format: function(s) {\n", + %% place empty cells, "-" and "?" at the bottom + " if (s.length < 2) return 999999999;\n", + " else {\n", + %% match out each date element + " var date = s.match(/(\\w{3})\\s(\\w{3})\\s(\\d{2})\\s(\\d{4})\\s(\\d{2}):(\\d{2}):(\\d{2})/);\n", + " var y = date[4]; var mo = monthNames[date[2]]; var d = String(date[3]);\n", + " var h = String(date[5]); var mi = String(date[6]); var sec = String(date[7]);\n", + " return (parseInt('' + y + mo + d + h + mi + sec)); }},\n", + " type: 'numeric' });\n", + + "// Parser for general text format\n", + "$.tablesorter.addParser({\n", + " id: 'CTTextSorter',\n", + " is: function(s) {\n", + " return false; },\n", + " format: function(s) {\n", + %% place empty cells, "?" and "-" at the bottom + " if (s.length < 1) return 'zzzzzzzz';\n", + " else if (s == \"?\") return 'zzzzzzz';\n", + " else if (s == \"-\") return 'zzzzzz';\n", + " else if (s == \"FAILED\") return 'A';\n", + " else if (s == \"SKIPPED\") return 'B';\n", + " else if (s == \"OK\") return 'C';\n", + " else return '' + s; },\n", + " type: 'text' });\n", + + "// Parser for numerical values\n", + "$.tablesorter.addParser({\n", + " id: 'CTValSorter',\n", + " is: function(s) {\n", + " return false; },\n", + " format: function(s) {\n" + %% place empty cells and "?" at the bottom + " if (s.length < 1) return '-2';\n", + " else if (s == \"?\") return '-1';\n", + %% look for skip value, eg "3 (2/1)" + " else if ((s.search(/(\\d{1,})\\s/)) >= 0) {\n", + " var num = s.match(/(\\d{1,})\\s/);\n", + %% return only the total skip value for sorting + " return (parseInt('' + num[1])); }\n", + " else if ((s.search(/(\\d{1,})\\.(\\d{3})s/)) >= 0) {\n", + " var num = s.match(/(\\d{1,})\\.(\\d{3})/);\n", + " if (num[1] == \"0\") return (parseInt('' + num[2]));\n", + " else return (parseInt('' + num[1] + num[2])); }\n", + " else return '' + s; },\n", + " type: 'numeric' });\n", + + "$(document).ready(function() {\n", + " $(\"#",TableName,"\").tablesorter({\n", + " headers: { \n", Headers1, "\n }\n });\n", + " $(\"#",TableName,"\").trigger(\"update\");\n", + " $(\"#",TableName,"\").trigger(\"appendCache\");\n", + "});\n</script>\n"]. diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl index 2a951bc5cf..9e61d5b16f 100644 --- a/lib/common_test/src/ct_master_logs.erl +++ b/lib/common_test/src/ct_master_logs.erl @@ -26,6 +26,8 @@ -export([start/2, make_all_runs_index/0, log/3, nodedir/2, stop/0]). +-include("ct_util.hrl"). + -record(state, {log_fd, start_time, logdir, rundir, nodedir_ix_fd, nodes, nodedirs=[]}). @@ -33,7 +35,6 @@ -define(all_runs_name, "master_runs.html"). -define(nodedir_index_name, "index.html"). -define(details_file_name,"details.info"). --define(css_default, "ct_default.css"). -define(table_color,"lightblue"). %%%-------------------------------------------------------------------- @@ -95,29 +96,30 @@ init(Parent,LogDir,Nodes) -> put(basic_html, true); BasicHtml -> put(basic_html, BasicHtml), - %% copy stylesheet to log dir (both top dir and test run + %% copy priv files to log dir (both top dir and test run %% dir) so logs are independent of Common Test installation CTPath = code:lib_dir(common_test), - CSSFileSrc = filename:join(filename:join(CTPath, "priv"), - ?css_default), - CSSFileDestTop = filename:join(LogDir, ?css_default), - CSSFileDestRun = filename:join(RunDirAbs, ?css_default), - case file:copy(CSSFileSrc, CSSFileDestTop) of - {error,Reason0} -> + PrivFiles = [?css_default,?jquery_script,?tablesorter_script], + PrivFilesSrc = [filename:join(filename:join(CTPath, "priv"), F) || + F <- PrivFiles], + PrivFilesDestTop = [filename:join(LogDir, F) || F <- PrivFiles], + PrivFilesDestRun = [filename:join(RunDirAbs, F) || F <- PrivFiles], + case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of + {error,Src1,Dest1,Reason1} -> io:format(user, "ERROR! "++ - "CSS file ~p could not be copied to ~p. "++ + "Priv file ~p could not be copied to ~p. "++ "Reason: ~p~n", - [CSSFileSrc,CSSFileDestTop,Reason0]), - exit({css_file_error,CSSFileDestTop}); - _ -> - case file:copy(CSSFileSrc, CSSFileDestRun) of - {error,Reason1} -> + [Src1,Dest1,Reason1]), + exit({priv_file_error,Dest1}); + ok -> + case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of + {error,Src2,Dest2,Reason2} -> io:format(user, "ERROR! "++ - "CSS file ~p could not be copied to ~p. "++ + "Priv file ~p could not be copied to ~p. "++ "Reason: ~p~n", - [CSSFileSrc,CSSFileDestRun,Reason1]), - exit({css_file_error,CSSFileDestRun}); - _ -> + [Src2,Dest2,Reason2]), + exit({priv_file_error,Dest2}); + ok -> ok end end @@ -146,6 +148,16 @@ init(Parent,LogDir,Nodes) -> {N,""} end,Nodes)}). +copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) -> + case file:copy(SrcF, DestF) of + {error,Reason} -> + {error,SrcF,DestF,Reason}; + _ -> + copy_priv_files(SrcFs, DestFs) + end; +copy_priv_files([], []) -> + ok. + loop(State) -> receive {log,_From,List} -> @@ -190,7 +202,7 @@ loop(State) -> open_ct_master_log(Dir) -> FullName = filename:join(Dir,?ct_master_log_name), {ok,Fd} = file:open(FullName,[write]), - io:format(Fd,header("Common Test Master Log"),[]), + io:format(Fd,header("Common Test Master Log", {[],[1,2],[]}),[]), %% maybe add config info here later io:format(Fd, config_table([]), []), io:format(Fd, @@ -216,11 +228,14 @@ config_table(Vars) -> config_table_header() -> ["<h2>Configuration</h2>\n", xhtml(["<table border=\"3\" cellpadding=\"5\" " - "bgcolor=\"",?table_color,"\"\n"], "<table>\n"), - "<tr><th>Key</th><th>Value</th></tr>\n"]. + "bgcolor=\"",?table_color,"\"\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n"]), + "<tr><th>Key</th><th>Value</th></tr>\n", + xhtml("", "</thead>\n<tbody>\n")]. config_table1([]) -> - ["</table>\n"]. + ["</tbody>\n</table>\n"]. int_header() -> "<div class=\"ct_internal\"><b>*** CT MASTER ~s *** ~s</b>". @@ -250,14 +265,16 @@ close_nodedir_index(Fd) -> file:close(Fd). nodedir_index_header(StartTime) -> - [header("Log Files " ++ format_time(StartTime)) | + [header("Log Files " ++ format_time(StartTime), {[],[1,2],[]}) | ["<center>\n", "<p><a href=\"",?ct_master_log_name,"\">Common Test Master Log</a></p>", xhtml(["<table border=\"3\" cellpadding=\"5\" " - "bgcolor=\"",?table_color,"\">\n"], "<table>\n"), + "bgcolor=\"",?table_color,"\">\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n<tr>\n"]), "<th><b>Node</b></th>\n", "<th><b>Log</b></th>\n", - "\n"]]. + xhtml("", "</tr>\n</thead>\n<tbody>\n")]]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% All Run Index functions %%% @@ -315,14 +332,16 @@ runentry(Dir) -> "</tr>\n"]. all_runs_header() -> - [header("Master Test Runs") | + [header("Master Test Runs", {[1],[2,3],[]}) | ["<center>\n", xhtml(["<table border=\"3\" cellpadding=\"5\" " - "bgcolor=\"",?table_color,"\">\n"], "<table>\n"), + "bgcolor=\"",?table_color,"\">\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n<tr>\n"]), "<th><b>History</b></th>\n" "<th><b>Master Host</b></th>\n" - "<th><b>Test Nodes</b></th>\n" - "\n"]]. + "<th><b>Test Nodes</b></th>\n", + xhtml("", "</tr></thead>\n<tbody>\n")]]. timestamp(Dir) -> [S,Min,H,D,M,Y|_] = lists:reverse(string:tokens(Dir,".-_")), @@ -346,9 +365,16 @@ read_details_file(Dir) -> %%% Internal functions %%%-------------------------------------------------------------------- -header(Title) -> +header(Title, TableCols) -> CSSFile = xhtml(fun() -> "" end, - fun() -> make_relative(locate_default_css_file()) end), + fun() -> make_relative(locate_priv_file(?css_default)) end), + JQueryFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?jquery_script)) end), + TableSorterFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?tablesorter_script)) end), + [xhtml(["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", "<html>\n"], ["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n", @@ -360,6 +386,16 @@ header(Title) -> "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", xhtml("", ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]), + xhtml("", + ["<script type=\"text/javascript\" src=\"",JQueryFile, + "\"></script>\n"]), + xhtml("", + ["<script type=\"text/javascript\" src=\"",TableSorterFile, + "\"></script>\n"]), + xhtml(fun() -> "" end, + fun() -> ct_logs:insert_javascript({tablesorter, + ?sortable_table_name, + TableCols}) end), "</head>\n", body_tag(), "<center>\n", @@ -367,7 +403,7 @@ header(Title) -> "</center>\n"]. index_footer() -> - ["</table>\n" + ["</tbody>\n</table>\n" "</center>\n" | footer()]. footer() -> @@ -393,7 +429,7 @@ current_time() -> format_time({{Y, Mon, D}, {H, Min, S}}) -> Weekday = weekday(calendar:day_of_the_week(Y, Mon, D)), - lists:flatten(io_lib:format("~s ~s ~p ~w ~2.2.0w:~2.2.0w:~2.2.0w", + lists:flatten(io_lib:format("~s ~s ~2.2.0w ~w ~2.2.0w:~2.2.0w:~2.2.0w", [Weekday, month(Mon), D, Y, H, Min, S])). weekday(1) -> "Mon"; @@ -446,8 +482,8 @@ basic_html() -> xhtml(HTML, XHTML) -> ct_logs:xhtml(HTML, XHTML). -locate_default_css_file() -> - ct_logs:locate_default_css_file(). +locate_priv_file(File) -> + ct_logs:locate_priv_file(File). make_relative(Dir) -> ct_logs:make_relative(Dir). diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl new file mode 100644 index 0000000000..52fe9599ce --- /dev/null +++ b/lib/common_test/src/ct_netconfc.erl @@ -0,0 +1,1835 @@ +%%---------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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% +%% +%%---------------------------------------------------------------------- +%% File: ct_netconfc.erl +%% +%% Description: +%% This file contains the Netconf client interface +%% +%% @author Support +%% +%% @doc Netconf client module. +%% +%% <p>The Netconf client is compliant with RFC4741 and RFC4742.</p> +%% +%% <p> For each server to test against, the following entry can be +%% added to a configuration file:</p> +%% +%% <p>`{server_id(),options()}.'</p> +%% +%% <p> The `server_id()' or an associated `target_name()' (see +%% {@link ct}) shall then be used in calls to {@link open/2}.</p> +%% +%% <p>If no configuration exists for a server, a session can still be +%% opened by calling {@link open/2} with all necessary options given +%% in the call. The first argument to {@link open/2} can then be any +%% atom.</p> +%% +%% == Logging == +%% +%% The netconf server uses the `error_logger' for logging of netconf +%% traffic. A special purpose error handler is implemented in +%% `ct_conn_log_h'. To use this error handler, add the `cth_conn_log' +%% hook in your test suite, e.g. +%% +%% ``` +%% suite() -> +%% [{ct_hooks, [{cth_conn_log, [{conn_mod(),hook_options()}]}]}]. +%%''' +%% +%% The `conn_mod()' is the name of the common_test module implementing +%% the connection protocol, e.g. `ct_netconfc'. +%% +%% The hook option `log_type' specifies the type of logging: +%% +%% <dl> +%% <dt>`raw'</dt> +%% <dd>The sent and received netconf data is logged to a separate +%% text file as is without any formatting. A link to the file is +%% added to the test case HTML log.</dd> +%% +%% <dt>`pretty'</dt> +%% <dd>The sent and received netconf data is logged to a separate +%% text file with XML data nicely indented. A link to the file is +%% added to the test case HTML log.</dd> +%% +%% <dt>`html (default)'</dt> +%% <dd>The sent and received netconf traffic is pretty printed +%% directly in the test case HTML log.</dd> +%% +%% <dt>`silent'</dt> +%% <dd>Netconf traffic is not logged.</dd> +%% </dl> +%% +%% By default, all netconf traffic is logged in one single log +%% file. However, it is possible to have different connections logged +%% in separate files. To do this, use the hook option `hosts' and +%% list the names of the servers/connections that will be used in the +%% suite. Note that the connections must be named for this to work, +%% i.e. they must be opened with {@link open/2}. +%% +%% The `hosts' option has no effect if `log_type' is set to `html' or +%% `silent'. +%% +%% The hook options can also be specified in a configuration file with +%% the configuration variable `ct_conn_log': +%% +%% ``` +%% {ct_conn_log,[{conn_mod(),hook_options()}]}. +%% ''' +%% +%% For example: +%% +%% ``` +%% {ct_conn_log,[{ct_netconfc,[{log_type,pretty}, +%% {hosts,[key_or_name()]}]}]} +%% ''' +%% +%% <b>Note</b> that hook options specified in a configuration file +%% will overwrite the hardcoded hook options in the test suite. +%% +%% === Logging example 1 === +%% +%% The following `ct_hooks' statement will cause pretty printing of +%% netconf traffic to separate logs for the connections named +%% `nc_server1' and `nc_server2'. Any other connections will be logged +%% to default netconf log. +%% +%% ``` +%% suite() -> +%% [{ct_hooks, [{cth_conn_log, [{ct_netconfc,[{log_type,pretty}}, +%% {hosts,[nc_server1,nc_server2]}]} +%% ]}]}]. +%%''' +%% +%% Connections must be opened like this: +%% +%% ``` +%% open(nc_server1,[...]), +%% open(nc_server2,[...]). +%% ''' +%% +%% === Logging example 2 === +%% +%% The following configuration file will cause raw logging of all +%% netconf traffic into one single text file. +%% +%% ``` +%% {ct_conn_log,[{ct_netconfc,[{log_type,raw}]}]}. +%% ''' +%% +%% The `ct_hooks' statement must look like this: +%% +%% ``` +%% suite() -> +%% [{ct_hooks, [{cth_conn_log, []}]}]. +%% ''' +%% +%% The same `ct_hooks' statement without the configuration file would +%% cause HTML logging of all netconf connections into the test case +%% HTML log. +%% +%% == Notifications == +%% +%% The netconf client is also compliant with RFC5277 NETCONF Event +%% Notifications, which defines a mechanism for an asynchronous +%% message notification delivery service for the netconf protocol. +%% +%% Specific functions to support this are {@link +%% create_subscription/6} and {@link get_event_streams/3}. (The +%% functions also exist with other arities.) +%% +%% @end +%%---------------------------------------------------------------------- +-module(ct_netconfc). + +-include("ct_netconfc.hrl"). +-include("ct_util.hrl"). +-include_lib("xmerl/include/xmerl.hrl"). + +%%---------------------------------------------------------------------- +%% External exports +%%---------------------------------------------------------------------- +-export([open/1, + open/2, + only_open/1, + only_open/2, + hello/1, + hello/2, + close_session/1, + close_session/2, + kill_session/2, + kill_session/3, + send/2, + send/3, + send_rpc/2, + send_rpc/3, + lock/2, + lock/3, + unlock/2, + unlock/3, + get/2, + get/3, + get_config/3, + get_config/4, + edit_config/3, + edit_config/4, + delete_config/2, + delete_config/3, + copy_config/3, + copy_config/4, + action/2, + action/3, + create_subscription/1, + create_subscription/2, + create_subscription/3, + create_subscription/4, + create_subscription/5, + create_subscription/6, + get_event_streams/2, + get_event_streams/3, + get_capabilities/1, + get_capabilities/2, + get_session_id/1, + get_session_id/2]). + +%%---------------------------------------------------------------------- +%% Exported types +%%---------------------------------------------------------------------- +-export_type([hook_options/0, + conn_mod/0, + log_type/0, + key_or_name/0, + notification/0]). + +%%---------------------------------------------------------------------- +%% Internal exports +%%---------------------------------------------------------------------- +%% ct_gen_conn callbacks +-export([init/3, + handle_msg/3, + handle_msg/2, + terminate/2, + close/1]). + +%% ct_conn_log callback +-export([format_data/2]). + +%%---------------------------------------------------------------------- +%% Internal defines +%%---------------------------------------------------------------------- +-define(APPLICATION,?MODULE). +-define(VALID_SSH_OPTS,[user, password, user_dir]). +-define(DEFAULT_STREAM,"NETCONF"). + +-define(error(ConnName,Report), + error_logger:error_report([{ct_connection,ConnName}, + {client,self()}, + {module,?MODULE}, + {line,?LINE} | + Report])). + +-define(is_timeout(T), (is_integer(T) orelse T==infinity)). +-define(is_filter(F), + (is_atom(F) orelse (is_tuple(F) andalso is_atom(element(1,F))))). +-define(is_string(S), (is_list(S) andalso is_integer(hd(S)))). + +%%---------------------------------------------------------------------- +%% Records +%%---------------------------------------------------------------------- +%% Client state +-record(state, {host, + port, + connection, % #connection + capabilities, + session_id, + msg_id = 1, + hello_status, + buff = <<>>, + pending = [], % [#pending] + event_receiver}).% pid + +%% Run-time client options. +-record(options, {ssh = [], % Options for the ssh application + host, + port = ?DEFAULT_PORT, + timeout = ?DEFAULT_TIMEOUT, + name}). + +%% Connection reference +-record(connection, {reference, % {CM,Ch} + host, + port, + name}). + +%% Pending replies from server +-record(pending, {tref, % timer ref (returned from timer:xxx) + ref, % pending ref + msg_id, + op, + caller}).% pid which sent the request + +%%---------------------------------------------------------------------- +%% Type declarations +%%---------------------------------------------------------------------- +-type client() :: handle() | server_id() | target_name(). +-type handle() :: term(). +%% An opaque reference for a connection (netconf session). See {@link +%% ct} for more information. + +-type server_id() :: atom(). +%% A `ServerId' which exists in a configuration file. +-type target_name() :: atom(). +%% A name which is associated to a `server_id()' via a +%% `require' statement or a call to {@link ct:require/2} in the +%% test suite. +-type key_or_name() :: server_id() | target_name(). + +-type options() :: [option()]. +%% Options used for setting up ssh connection to a netconf server. + +-type option() :: {ssh,host()} | {port,inet:port_number()} | {user,string()} | + {password,string()} | {user_dir,string()} | + {timeout,timeout()}. +-type host() :: inet:host_name() | inet:ip_address(). + +-type notification() :: {notification, xml_attributes(), notification_content()}. +-type notification_content() :: [event_time()|simple_xml()]. +-type event_time() :: {eventTime,xml_attributes(),[xs_datetime()]}. + +-type stream_name() :: string(). +-type streams() :: [{stream_name(),[stream_data()]}]. +-type stream_data() :: {description,string()} | + {replaySupport,string()} | + {replayLogCreationTime,string()} | + {replayLogAgedTime,string()}. +%% See XML Schema for Event Notifications found in RFC5277 for further +%% detail about the data format for the string values. + +-type hook_options() :: [hook_option()]. +%% Options that can be given to `cth_conn_log' in the `ct_hook' statement. +-type hook_option() :: {log_type,log_type()} | + {hosts,[key_or_name()]}. +-type log_type() :: raw | pretty | html | silent. +%-type error_handler() :: module(). +-type conn_mod() :: ct_netconfc. + +-type error_reason() :: term(). + +-type simple_xml() :: {xml_tag(), xml_attributes(), xml_content()} | + {xml_tag(), xml_content()} | + xml_tag(). +%% <p>This type is further described in the documentation for the +%% <tt>Xmerl</tt> application.</p> +-type xml_tag() :: atom(). +-type xml_attributes() :: [{xml_attribute_tag(),xml_attribute_value()}]. +-type xml_attribute_tag() :: atom(). +-type xml_attribute_value() :: string(). +-type xml_content() :: [simple_xml() | iolist()]. +-type xpath() :: {xpath,string()}. + +-type netconf_db() :: running | startup | candidate. +-type xs_datetime() :: string(). +%% This date and time identifyer has the same format as the XML type +%% dateTime and compliant to RFC3339. The format is +%% ```[-]CCYY-MM-DDThh:mm:ss[.s][Z|(+|-)hh:mm]''' + +%%---------------------------------------------------------------------- +%% External interface functions +%%---------------------------------------------------------------------- + +%%---------------------------------------------------------------------- +-spec open(Options) -> Result when + Options :: options(), + Result :: {ok,handle()} | {error,error_reason()}. +%% @doc Open a netconf session and exchange `hello' messages. +%% +%% If the server options are specified in a configuration file, or if +%% a named client is needed for logging purposes (see {@section +%% Logging}) use {@link open/2} instead. +%% +%% The opaque `handler()' reference which is returned from this +%% function is required as client identifier when calling any other +%% function in this module. +%% +%% The `timeout' option (milli seconds) is used when setting up +%% the ssh connection and when waiting for the hello message from the +%% server. It is not used for any other purposes during the lifetime +%% of the connection. +%% +%% @end +%%---------------------------------------------------------------------- +open(Options) -> + open(Options,#options{},[],true). + +%%---------------------------------------------------------------------- +-spec open(KeyOrName, ExtraOptions) -> Result when + KeyOrName :: key_or_name(), + ExtraOptions :: options(), + Result :: {ok,handle()} | {error,error_reason()}. +%% @doc Open a named netconf session and exchange `hello' messages. +%% +%% If `KeyOrName' is a configured `server_id()' or a +%% `target_name()' associated with such an ID, then the options +%% for this server will be fetched from the configuration file. +% +%% The `ExtraOptions' argument will be added to the options found in +%% the configuration file. If the same options are given, the values +%% from the configuration file will overwrite `ExtraOptions'. +%% +%% If the server is not specified in a configuration file, use {@link +%% open/1} instead. +%% +%% The opaque `handle()' reference which is returned from this +%% function can be used as client identifier when calling any other +%% function in this module. However, if `KeyOrName' is a +%% `target_name()', i.e. if the server is named via a call to +%% `ct:require/2' or a `require' statement in the test +%% suite, then this name may be used instead of the `handle()'. +%% +%% The `timeout' option (milli seconds) is used when setting up +%% the ssh connection and when waiting for the hello message from the +%% server. It is not used for any other purposes during the lifetime +%% of the connection. +%% +%% @see ct:require/2 +%% @end +%%---------------------------------------------------------------------- +open(KeyOrName, ExtraOpts) -> + open(KeyOrName, ExtraOpts, true). + +open(KeyOrName, ExtraOpts, Hello) -> + SortedExtra = lists:keysort(1,ExtraOpts), + SortedConfig = lists:keysort(1,ct:get_config(KeyOrName,[])), + AllOpts = lists:ukeymerge(1,SortedConfig,SortedExtra), + open(AllOpts,#options{name=KeyOrName},[{name,KeyOrName}],Hello). + +open(OptList,InitOptRec,NameOpt,Hello) -> + case check_options(OptList,undefined,undefined,InitOptRec) of + {Host,Port,Options} -> + case ct_gen_conn:start({Host,Port},Options,?MODULE, + NameOpt ++ [{reconnect,false}, + {use_existing_connection,false}, + {forward_messages,true}]) of + {ok,Client} when Hello==true -> + case hello(Client,Options#options.timeout) of + ok -> + {ok,Client}; + Error -> + Error + end; + Other -> + Other + end; + Error -> + Error + end. + + +%%---------------------------------------------------------------------- +-spec only_open(Options) -> Result when + Options :: options(), + Result :: {ok,handle()} | {error,error_reason()}. +%% @doc Open a netconf session, but don't send `hello'. +%% +%% As {@link open/1} but does not send a `hello' message. +%% +%% @end +%%---------------------------------------------------------------------- +only_open(Options) -> + open(Options,#options{},[],false). + +%%---------------------------------------------------------------------- +-spec only_open(KeyOrName,ExtraOptions) -> Result when + KeyOrName :: key_or_name(), + ExtraOptions :: options(), + Result :: {ok,handle()} | {error,error_reason()}. +%% @doc Open a name netconf session, but don't send `hello'. +%% +%% As {@link open/2} but does not send a `hello' message. +%% +%% @end +%%---------------------------------------------------------------------- +only_open(KeyOrName, ExtraOpts) -> + open(KeyOrName, ExtraOpts, false). + +%%---------------------------------------------------------------------- +%% @spec hello(Client) -> Result +%% @equiv hello(Client, infinity) +hello(Client) -> + hello(Client,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec hello(Client,Timeout) -> Result when + Client :: handle(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Exchange `hello' messages with the server. +%% +%% Sends a `hello' message to the server and waits for the return. +%% +%% @end +%%---------------------------------------------------------------------- +hello(Client,Timeout) -> + call(Client, {hello, Timeout}). + +%%---------------------------------------------------------------------- +%% @spec get_session_id(Client) -> Result +%% @equiv get_session_id(Client, infinity) +get_session_id(Client) -> + get_session_id(Client, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec get_session_id(Client, Timeout) -> Result when + Client :: client(), + Timeout :: timeout(), + Result :: pos_integer() | {error,error_reason()}. +%% @doc Returns the session id associated with the given client. +%% +%% @end +%%---------------------------------------------------------------------- +get_session_id(Client, Timeout) -> + call(Client, get_session_id, Timeout). + +%%---------------------------------------------------------------------- +%% @spec get_capabilities(Client) -> Result +%% @equiv get_capabilities(Client, infinity) +get_capabilities(Client) -> + get_capabilities(Client, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec get_capabilities(Client, Timeout) -> Result when + Client :: client(), + Timeout :: timeout(), + Result :: [string()] | {error,error_reason()}. +%% @doc Returns the server side capabilities +%% +%% The following capability identifiers, defined in RFC 4741, can be returned: +%% +%% <ul> +%% <li>`"urn:ietf:params:netconf:base:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:writable-running:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:candidate:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:confirmed-commit:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:rollback-on-error:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:startup:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:url:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:xpath:1.0"'</li> +%% </ul> +%% +%% Note, additional identifiers may exist, e.g. server side namespace. +%% +%% @end +%%---------------------------------------------------------------------- +get_capabilities(Client, Timeout) -> + call(Client, get_capabilities, Timeout). + +%% @private +send(Client, SimpleXml) -> + send(Client, SimpleXml, ?DEFAULT_TIMEOUT). +%% @private +send(Client, SimpleXml, Timeout) -> + call(Client,{send, Timeout, SimpleXml}). + +%% @private +send_rpc(Client, SimpleXml) -> + send_rpc(Client, SimpleXml, ?DEFAULT_TIMEOUT). +%% @private +send_rpc(Client, SimpleXml, Timeout) -> + call(Client,{send_rpc, SimpleXml, Timeout}). + + + +%%---------------------------------------------------------------------- +%% @spec lock(Client, Target) -> Result +%% @equiv lock(Client, Target, infinity) +lock(Client, Target) -> + lock(Client, Target,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec lock(Client, Target, Timeout) -> Result when + Client :: client(), + Target :: netconf_db(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Unlock configuration target. +%% +%% Which target parameters that can be used depends on if +%% `:candidate' and/or `:startup' are supported by the +%% server. If successfull, the configuration system of the device is +%% not available to other clients (Netconf, CORBA, SNMP etc). Locks +%% are intended to be short-lived. +%% +%% The operations {@link kill_session/2} or {@link kill_session/3} can +%% be used to force the release of a lock owned by another Netconf +%% session. How this is achieved by the server side is implementation +%% specific. +%% +%% @end +%%---------------------------------------------------------------------- +lock(Client, Target, Timeout) -> + call(Client,{send_rpc_op,lock,[Target],Timeout}). + +%%---------------------------------------------------------------------- +%% @spec unlock(Client, Target) -> Result +%% @equiv unlock(Client, Target, infinity) +unlock(Client, Target) -> + unlock(Client, Target,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec unlock(Client, Target, Timeout) -> Result when + Client :: client(), + Target :: netconf_db(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Unlock configuration target. +%% +%% If the client earlier has aquired a lock, via {@link lock/2} or +%% {@link lock/3}, this operation release the associated lock. To be +%% able to access another target than `running', the server must +%% support `:candidate' and/or `:startup'. +%% +%% @end +%%---------------------------------------------------------------------- +unlock(Client, Target, Timeout) -> + call(Client, {send_rpc_op, unlock, [Target], Timeout}). + +%%---------------------------------------------------------------------- +%% @spec get(Client, Filter) -> Result +%% @equiv get(Client, Filter, infinity) +get(Client, Filter) -> + get(Client, Filter, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec get(Client, Filter, Timeout) -> Result when + Client :: client(), + Filter :: simple_xml() | xpath(), + Timeout :: timeout(), + Result :: {ok,simple_xml()} | {error,error_reason()}. +%% @doc Get data. +%% +%% This operation returns both configuration and state data from the +%% server. +%% +%% Filter type `xpath' can only be used if the server supports +%% `:xpath'. +%% +%% @end +%%---------------------------------------------------------------------- +get(Client, Filter, Timeout) -> + call(Client,{send_rpc_op, get, [Filter], Timeout}). + +%%---------------------------------------------------------------------- +%% @spec get_config(Client, Source, Filter) -> Result +%% @equiv get_config(Client, Source, Filter, infinity) +get_config(Client, Source, Filter) -> + get_config(Client, Source, Filter, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec get_config(Client, Source, Filter, Timeout) -> Result when + Client :: client(), + Source :: netconf_db(), + Filter :: simple_xml() | xpath(), + Timeout :: timeout(), + Result :: {ok,simple_xml()} | {error,error_reason()}. +%% @doc Get configuration data. +%% +%% To be able to access another source than `running', the server +%% must advertise `:candidate' and/or `:startup'. +%% +%% Filter type `xpath' can only be used if the server supports +%% `:xpath'. +%% +%% +%% @end +%%---------------------------------------------------------------------- +get_config(Client, Source, Filter, Timeout) -> + call(Client, {send_rpc_op, get_config, [Source, Filter], Timeout}). + +%%---------------------------------------------------------------------- +%% @spec edit_config(Client, Target, Config) -> Result +%% @equiv edit_config(Client, Target, Config, infinity) +edit_config(Client, Target, Config) -> + edit_config(Client, Target, Config, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec edit_config(Client, Target, Config, Timeout) -> Result when + Client :: client(), + Target :: netconf_db(), + Config :: simple_xml(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Edit configuration data. +%% +%% Per default only the running target is available, unless the server +%% include `:candidate' or `:startup' in its list of +%% capabilities. +%% +%% @end +%%---------------------------------------------------------------------- +edit_config(Client, Target, Config, Timeout) -> + call(Client, {send_rpc_op, edit_config, [Target,Config], Timeout}). + + +%%---------------------------------------------------------------------- +%% @spec delete_config(Client, Target) -> Result +%% @equiv delete_config(Client, Target, infinity) +delete_config(Client, Target) -> + delete_config(Client, Target, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec delete_config(Client, Target, Timeout) -> Result when + Client :: client(), + Target :: startup | candidate, + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Delete configuration data. +%% +%% The running configuration cannot be deleted and `:candidate' +%% or `:startup' must be advertised by the server. +%% +%% @end +%%---------------------------------------------------------------------- +delete_config(Client, Target, Timeout) when Target == startup; + Target == candidate -> + call(Client,{send_rpc_op, delete_config, [Target], Timeout}). + +%%---------------------------------------------------------------------- +%% @spec copy_config(Client, Source, Target) -> Result +%% @equiv copy_config(Client, Source, Target, infinity) +copy_config(Client, Source, Target) -> + copy_config(Client, Source, Target, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec copy_config(Client, Target, Source, Timeout) -> Result when + Client :: client(), + Target :: netconf_db(), + Source :: netconf_db(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Copy configuration data. +%% +%% Which source and target options that can be issued depends on the +%% capabilities supported by the server. I.e. `:candidate' and/or +%% `:startup' are required. +%% +%% @end +%%---------------------------------------------------------------------- +copy_config(Client, Target, Source, Timeout) -> + call(Client,{send_rpc_op, copy_config, [Target, Source], Timeout}). + +%%---------------------------------------------------------------------- +%% @spec action(Client, Action) -> Result +%% @equiv action(Client, Action, infinity) +action(Client,Action) -> + action(Client,Action,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec action(Client, Action, Timeout) -> Result when + Client :: client(), + Action :: simple_xml(), + Timeout :: timeout(), + Result :: {ok,simple_xml()} | {error,error_reason()}. +%% @doc Execute an action. +%% +%% @end +%%---------------------------------------------------------------------- +action(Client,Action,Timeout) -> + call(Client,{send_rpc_op, action, [Action], Timeout}). + +%%---------------------------------------------------------------------- +create_subscription(Client) -> + create_subscription(Client,?DEFAULT_STREAM,?DEFAULT_TIMEOUT). + +create_subscription(Client,Timeout) + when ?is_timeout(Timeout) -> + create_subscription(Client,?DEFAULT_STREAM,Timeout); +create_subscription(Client,Stream) + when is_list(Stream) -> + create_subscription(Client,Stream,?DEFAULT_TIMEOUT); +create_subscription(Client,Filter) + when ?is_filter(Filter) -> + create_subscription(Client,?DEFAULT_STREAM,Filter, + ?DEFAULT_TIMEOUT). + +create_subscription(Client,Stream,Timeout) + when is_list(Stream) andalso + ?is_timeout(Timeout) -> + call(Client,{send_rpc_op,{create_subscription,self()}, + [Stream,undefined,undefined,undefined], + Timeout}); +create_subscription(Client,StartTime,StopTime) + when is_list(StartTime) andalso + is_list(StopTime) -> + create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime, + ?DEFAULT_TIMEOUT); +create_subscription(Client,Filter,Timeout) + when ?is_filter(Filter) andalso + ?is_timeout(Timeout) -> + create_subscription(Client,?DEFAULT_STREAM,Filter,Timeout); +create_subscription(Client,Stream,Filter) + when is_list(Stream) andalso + ?is_filter(Filter) -> + create_subscription(Client,Stream,Filter,?DEFAULT_TIMEOUT). + +create_subscription(Client,StartTime,StopTime,Timeout) + when is_list(StartTime) andalso + is_list(StopTime) andalso + ?is_timeout(Timeout) -> + create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime,Timeout); +create_subscription(Client,Stream,StartTime,StopTime) + when is_list(Stream) andalso + is_list(StartTime) andalso + is_list(StopTime) -> + create_subscription(Client,Stream,StartTime,StopTime,?DEFAULT_TIMEOUT); +create_subscription(Client,Filter,StartTime,StopTime) + when ?is_filter(Filter) andalso + is_list(StartTime) andalso + is_list(StopTime) -> + create_subscription(Client,?DEFAULT_STREAM,Filter, + StartTime,StopTime,?DEFAULT_TIMEOUT); +create_subscription(Client,Stream,Filter,Timeout) + when is_list(Stream) andalso + ?is_filter(Filter) andalso + ?is_timeout(Timeout) -> + call(Client,{send_rpc_op,{create_subscription,self()}, + [Stream,Filter,undefined,undefined], + Timeout}). + +create_subscription(Client,Stream,StartTime,StopTime,Timeout) + when is_list(Stream) andalso + is_list(StartTime) andalso + is_list(StopTime) andalso + ?is_timeout(Timeout) -> + call(Client,{send_rpc_op,{create_subscription,self()}, + [Stream,undefined,StartTime,StopTime], + Timeout}); +create_subscription(Client,Stream,Filter,StartTime,StopTime) + when is_list(Stream) andalso + ?is_filter(Filter) andalso + is_list(StartTime) andalso + is_list(StopTime) -> + create_subscription(Client,Stream,Filter,StartTime,StopTime,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec create_subscription(Client, Stream, Filter,StartTime, StopTime, Timeout) -> + Result when + Client :: client(), + Stream :: stream_name(), + Filter :: simple_xml(), + StartTime :: xs_datetime(), + StopTime :: xs_datetime(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Create a subscription for event notifications. +%% +%% This function sets up a subscription for netconf event +%% notifications of the given stream type, matching the given +%% filter. The calling process will receive notifications as messages +%% of type `notification()'. +%% +%% <dl> +%% <dt>Stream:</dt> +%% <dd> An optional parameter that indicates which stream of events +%% is of interest. If not present, events in the default NETCONF +%% stream will be sent.</dd> +%% +%% <dt>Filter:</dt> +%% <dd>An optional parameter that indicates which subset of all +%% possible events is of interest. The format of this parameter is +%% the same as that of the filter parameter in the NETCONF protocol +%% operations. If not present, all events not precluded by other +%% parameters will be sent. See section 3.6 for more information on +%% filters.</dd> +%% +%% <dt>StartTime:</dt> +%% <dd>An optional parameter used to trigger the replay feature and +%% indicate that the replay should start at the time specified. If +%% `StartTime' is not present, this is not a replay subscription. +%% It is not valid to specify start times that are later than the +%% current time. If the `StartTime' specified is earlier than the +%% log can support, the replay will begin with the earliest +%% available notification. This parameter is of type dateTime and +%% compliant to [RFC3339]. Implementations must support time +%% zones.</dd> +%% +%% <dt>StopTime:</dt> +%% <dd>An optional parameter used with the optional replay feature +%% to indicate the newest notifications of interest. If `StopTime' +%% is not present, the notifications will continue until the +%% subscription is terminated. Must be used with and be later than +%% `StartTime'. Values of `StopTime' in the future are valid. This +%% parameter is of type dateTime and compliant to [RFC3339]. +%% Implementations must support time zones.</dd> +%% </dl> +%% +%% See RFC5277 for further details about the event notification +%% mechanism. +%% +%% @end +%%---------------------------------------------------------------------- +create_subscription(Client,Stream,Filter,StartTime,StopTime,Timeout) -> + call(Client,{send_rpc_op,{create_subscription, self()}, + [Stream,Filter,StartTime,StopTime], + Timeout}). + +%%---------------------------------------------------------------------- +%% @spec get_event_streams(Client, Timeout) -> Result +%% @equiv get_event_streams(Client, [], Timeout) +get_event_streams(Client,Timeout) when is_integer(Timeout); Timeout==infinity -> + get_event_streams(Client,[],Timeout); + +%%---------------------------------------------------------------------- +%% @spec get_event_streams(Client, Streams) -> Result +%% @equiv get_event_streams(Client, Streams, infinity) +get_event_streams(Client,Streams) when is_list(Streams) -> + get_event_streams(Client,Streams,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec get_event_streams(Client, Streams, Timeout) + -> Result when + Client :: client(), + Streams :: [stream_name()], + Timeout :: timeout(), + Result :: {ok,streams()} | {error,error_reason()}. +%% @doc Send a request to get the given event streams. +%% +%% `Streams' is a list of stream names. The following filter will +%% be sent to the netconf server in a `get' request: +%% +%% ``` +%% <netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"> +%% <streams> +%% <stream> +%% <name>StreamName1</name> +%% </stream> +%% <stream> +%% <name>StreamName2</name> +%% </stream> +%% ... +%% </streams> +%% </netconf> +%% ''' +%% +%% If `Streams' is an empty list, ALL streams will be requested +%% by sending the following filter: +%% +%% ``` +%% <netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"> +%% <streams/> +%% </netconf> +%% ''' +%% +%% If more complex filtering is needed, a use {@link get/2} or {@link +%% get/3} and specify the exact filter according to XML Schema for +%% Event Notifications found in RFC5277. +%% +%% @end +%%---------------------------------------------------------------------- +get_event_streams(Client,Streams,Timeout) -> + call(Client,{get_event_streams,Streams,Timeout}). + + +%%---------------------------------------------------------------------- +%% @spec close_session(Client) -> Result +%% @equiv close_session(Client, infinity) +close_session(Client) -> + close_session(Client, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec close_session(Client, Timeout) -> Result when + Client :: client(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Request graceful termination of the session associated with the client. +%% +%% When a netconf server receives a `close-session' request, it +%% will gracefully close the session. The server will release any +%% locks and resources associated with the session and gracefully +%% close any associated connections. Any NETCONF requests received +%% after a `close-session' request will be ignored. +%% +%% @end +%%---------------------------------------------------------------------- +close_session(Client, Timeout) -> + call(Client,{send_rpc_op, close_session, [], Timeout}). + + +%%---------------------------------------------------------------------- +%% @spec kill_session(Client, SessionId) -> Result +%% @equiv kill_session(Client, SessionId, infinity) +kill_session(Client, SessionId) -> + kill_session(Client, SessionId, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec kill_session(Client, SessionId, Timeout) -> Result when + Client :: client(), + SessionId :: pos_integer(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Force termination of the session associated with the supplied +%% session id. +%% +%% The server side shall abort any operations currently in process, +%% release any locks and resources associated with the session, and +%% close any associated connections. +%% +%% Only if the server is in the confirmed commit phase, the +%% configuration will be restored to its state before entering the +%% confirmed commit phase. Otherwise, no configuration roll back will +%% be performed. +%% +%% If the given `SessionId' is equal to the current session id, +%% an error will be returned. +%% +%% @end +%% ---------------------------------------------------------------------- +kill_session(Client, SessionId, Timeout) -> + call(Client,{send_rpc_op, kill_session, [SessionId], Timeout}). + + +%%---------------------------------------------------------------------- +%% Callback functions +%%---------------------------------------------------------------------- + +%% @private +init(_KeyOrName,{_Host,_Port},Options) -> + case ssh_open(Options) of + {ok, Connection} -> + log(Connection,open), + {ConnPid,_} = Connection#connection.reference, + {ok, ConnPid, #state{connection = Connection}}; + {error,Reason}-> + {error,Reason} + end. + +%% @private +terminate(_, #state{connection=Connection}) -> + ssh_close(Connection), + log(Connection,close), + ok. + +%% @private +handle_msg({hello,Timeout}, From, + #state{connection=Connection,hello_status=HelloStatus} = State) -> + case do_send(Connection, client_hello()) of + ok -> + case HelloStatus of + undefined -> + {Ref,TRef} = set_request_timer(Timeout), + {noreply, State#state{hello_status=#pending{tref=TRef, + ref=Ref, + caller=From}}}; + received -> + {reply, ok, State#state{hello_status=done}}; + {error,Reason} -> + {stop, {error,Reason}, State} + end; + Error -> + {stop, Error, State} + end; +handle_msg(_, _From, #state{session_id=undefined} = State) -> + %% Hello is not yet excanged - this shall never happen + {reply,{error,waiting_for_hello},State}; +handle_msg(get_capabilities, _From, #state{capabilities = Caps} = State) -> + {reply, Caps, State}; +handle_msg(get_session_id, _From, #state{session_id = Id} = State) -> + {reply, Id, State}; +handle_msg({send, Timeout, SimpleXml}, From, + #state{connection=Connection,pending=Pending} = State) -> + case do_send(Connection, SimpleXml) of + ok -> + {Ref,TRef} = set_request_timer(Timeout), + {noreply, State#state{pending=[#pending{tref=TRef, + ref=Ref, + caller=From} | Pending]}}; + Error -> + {reply, Error, State} + end; +handle_msg({send_rpc, SimpleXml, Timeout}, From, State) -> + do_send_rpc(undefined, SimpleXml, Timeout, From, State); +handle_msg({send_rpc_op, Op, Data, Timeout}, From, State) -> + SimpleXml = encode_rpc_operation(Op,Data), + do_send_rpc(Op, SimpleXml, Timeout, From, State); +handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) -> + Filter = {netconf,?NETMOD_NOTIF_NAMESPACE_ATTR, + [{streams,[{stream,[{name,[Name]}]} || Name <- Streams]}]}, + SimpleXml = encode_rpc_operation(get,[Filter]), + do_send_rpc(Op, SimpleXml, Timeout, From, State). + +handle_msg({ssh_cm, _CM, {data, _Ch, _Type, Data}}, State) -> + handle_data(Data, State); +handle_msg({ssh_cm, _CM, _SshCloseMsg}, State) -> + %% _SshCloseMsg can probably be one of + %% {eof,Ch} + %% {exit_status,Ch,Status} + %% {exit_signal,Ch,ExitSignal,ErrorMsg,LanguageString} + %% {signal,Ch,Signal} + + %% This might e.g. happen if the server terminates the connection, + %% as in kill-session (or if ssh:close is called from somewhere + %% unexpected). + + %%! Log this?? + %%! Currently the log will say that the client closed the + %%! connection - due to terminate/2 + + {stop, State}; +handle_msg({Ref,timeout}, + #state{hello_status=#pending{ref=Ref,caller=Caller}} = State) -> + ct_gen_conn:return(Caller,{error,{hello_session_failed,timeout}}), + {stop,State#state{hello_status={error,timeout}}}; +handle_msg({Ref,timeout},#state{pending=Pending} = State) -> + {value,#pending{caller=Caller},Pending1} = + lists:keytake(Ref,#pending.ref,Pending), + ct_gen_conn:return(Caller,{error,timeout}), + {noreply,State#state{pending=Pending1}}. + +%% @private +%% Called by ct_util_server to close registered connections before terminate. +close(Client) -> + case get_handle(Client) of + {ok,Pid} -> + case ct_gen_conn:stop(Pid) of + {error,{process_down,Pid,noproc}} -> + {error,already_closed}; + Result -> + Result + end; + Error -> + Error + end. + + +%%---------------------------------------------------------------------- +%% Internal functions +%%---------------------------------------------------------------------- +call(Client, Msg) -> + call(Client, Msg, infinity). +call(Client, Msg, Timeout) -> + case get_handle(Client) of + {ok,Pid} -> + case ct_gen_conn:call(Pid,Msg,Timeout) of + {error,{process_down,Client,noproc}} -> + {error,no_such_client}; + {error,{process_down,Client,normal}} -> + {error,closed}; + {error,{process_down,Client,Reason}} -> + {error,{closed,Reason}}; + Other -> + Other + end; + Error -> + Error + end. + +get_handle(Client) when is_pid(Client) -> + {ok,Client}; +get_handle(Client) -> + case ct_util:get_connections(Client, ?MODULE) of + {ok,[{Pid,_}]} -> + {ok,Pid}; + {ok,[]} -> + {error,{no_connection_found,Client}}; + {ok,Conns} -> + {error,{multiple_connections_found,Client,Conns}}; + Error -> + Error + end. + +check_options([], undefined, _Port, _Options) -> + {error, no_host_address}; +check_options([], _Host, undefined, _Options) -> + {error, no_port}; +check_options([], Host, Port, Options) -> + {Host,Port,Options}; +check_options([{ssh, Host}|T], _, Port, #options{} = Options) -> + check_options(T, Host, Port, Options#options{host=Host}); +check_options([{port,Port}|T], Host, _, #options{} = Options) -> + check_options(T, Host, Port, Options#options{port=Port}); +check_options([{timeout, Timeout}|T], Host, Port, Options) + when is_integer(Timeout); Timeout==infinity -> + check_options(T, Host, Port, Options#options{timeout = Timeout}); +check_options([{X,_}=Opt|T], Host, Port, #options{ssh=SshOpts}=Options) -> + case lists:member(X,?VALID_SSH_OPTS) of + true -> + check_options(T, Host, Port, Options#options{ssh=[Opt|SshOpts]}); + false -> + {error, {invalid_option, Opt}} + end. + +%%%----------------------------------------------------------------- +set_request_timer(infinity) -> + {undefined,undefined}; +set_request_timer(T) -> + Ref = make_ref(), + {ok,TRef} = timer:send_after(T,{Ref,timeout}), + {Ref,TRef}. + + +%%%----------------------------------------------------------------- +client_hello() -> + {hello, ?NETCONF_NAMESPACE_ATTR, + [{capabilities, + [{capability,[?NETCONF_BASE_CAP++?NETCONF_BASE_CAP_VSN]}]}]}. + +%%%----------------------------------------------------------------- + +encode_rpc_operation(Lock,[Target]) when Lock==lock; Lock==unlock -> + {Lock,[{target,[Target]}]}; +encode_rpc_operation(get,[Filter]) -> + {get,filter(Filter)}; +encode_rpc_operation(get_config,[Source,Filter]) -> + {'get-config',[{source,[Source]}] ++ filter(Filter)}; +encode_rpc_operation(edit_config,[Target,Config]) -> + {'edit-config',[{target,[Target]},{config,[Config]}]}; +encode_rpc_operation(delete_config,[Target]) -> + {'delete-config',[{target,[Target]}]}; +encode_rpc_operation(copy_config,[Target,Source]) -> + {'copy-config',[{target,[Target]},{source,[Source]}]}; +encode_rpc_operation(action,[Action]) -> + {action,?ACTION_NAMESPACE_ATTR,[{data,[Action]}]}; +encode_rpc_operation(kill_session,[SessionId]) -> + {'kill-session',[{'session-id',[integer_to_list(SessionId)]}]}; +encode_rpc_operation(close_session,[]) -> + 'close-session'; +encode_rpc_operation({create_subscription,_}, + [Stream,Filter,StartTime,StopTime]) -> + {'create-subscription',?NETCONF_NOTIF_NAMESPACE_ATTR, + [{stream,[Stream]}] ++ + filter(Filter) ++ + maybe_element(startTime,StartTime) ++ + maybe_element(stopTime,StopTime)}. + +filter(undefined) -> + []; +filter({xpath,Filter}) when ?is_string(Filter) -> + [{filter,[{type,"xpath"},{select, Filter}],[]}]; +filter(Filter) -> + [{filter,[{type,"subtree"}],[Filter]}]. + +maybe_element(_,undefined) -> + []; +maybe_element(Tag,Value) -> + [{Tag,[Value]}]. + +%%%----------------------------------------------------------------- +%%% Send XML data to server +do_send_rpc(PendingOp,SimpleXml,Timeout,Caller, + #state{connection=Connection,msg_id=MsgId,pending=Pending} = State) -> + case do_send_rpc(Connection, MsgId, SimpleXml) of + ok -> + {Ref,TRef} = set_request_timer(Timeout), + {noreply, State#state{msg_id=MsgId+1, + pending=[#pending{tref=TRef, + ref=Ref, + msg_id=MsgId, + op=PendingOp, + caller=Caller} | Pending]}}; + Error -> + {reply, Error, State#state{msg_id=MsgId+1}} + end. + +do_send_rpc(Connection, MsgId, SimpleXml) -> + do_send(Connection, + {rpc, + [{'message-id',MsgId} | ?NETCONF_NAMESPACE_ATTR], + [SimpleXml]}). + +do_send(Connection, SimpleXml) -> + Xml=to_xml_doc(SimpleXml), + log(Connection,send,Xml), + ssh_send(Connection, Xml). + +to_xml_doc(Simple) -> + Prolog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", + Xml = list_to_binary(xmerl:export_simple([Simple], + xmerl_xml, + [#xmlAttribute{name=prolog, + value=Prolog}])), + <<Xml/binary,?END_TAG/binary>>. + +%%%----------------------------------------------------------------- +%%% Parse and handle received XML data +handle_data(NewData,#state{connection=Connection,buff=Buff} = State) -> + log(Connection,recv,NewData), + Data = <<Buff/binary,NewData/binary>>, + case xmerl_sax_parser:stream(<<>>, + [{continuation_fun,fun sax_cont/1}, + {continuation_state,{Data,Connection,false}}, + {event_fun,fun sax_event/3}, + {event_state,[]}]) of + {ok, Simple, Rest} -> + decode(Simple,State#state{buff=Rest}); + {fatal_error,_Loc,Reason,_EndTags,_EventState} -> + ?error(Connection#connection.name,[{parse_error,Reason}, + {data,Data}]), + case Reason of + {could_not_fetch_data,Msg} -> + handle_msg(Msg,State#state{buff = <<>>}); + _Other -> + Pending1 = + case State#state.pending of + [] -> + []; + Pending -> + %% Assuming the first request gets the + %% first answer + P=#pending{tref=TRef,caller=Caller} = + lists:last(Pending), + timer:cancel(TRef), + Reason1 = {failed_to_parse_received_data,Reason}, + ct_gen_conn:return(Caller,{error,Reason1}), + lists:delete(P,Pending) + end, + {noreply,State#state{pending=Pending1,buff = <<>>}} + end + end. + +%%%----------------------------------------------------------------- +%%% Parsing of XML data +%% Contiuation function for the sax parser +sax_cont(done) -> + {<<>>,done}; +sax_cont({Data,Connection,false}) -> + case binary:split(Data,[?END_TAG],[]) of + [All] -> + %% No end tag found. Remove what could be a part + %% of an end tag from the data and save for next + %% iteration + SafeSize = size(All)-5, + <<New:SafeSize/binary,Save:5/binary>> = All, + {New,{Save,Connection,true}}; + [_Msg,_Rest]=Msgs -> + %% We have at least one full message. Any excess data will + %% be returned from xmerl_sax_parser:stream/2 in the Rest + %% parameter. + {list_to_binary(Msgs),done} + end; +sax_cont({Data,Connection,true}) -> + case ssh_receive_data() of + {ok,Bin} -> + log(Connection,recv,Bin), + sax_cont({<<Data/binary,Bin/binary>>,Connection,false}); + {error,Reason} -> + throw({could_not_fetch_data,Reason}) + end. + + + +%% Event function for the sax parser. It builds a simple XML structure. +%% Care is taken to keep namespace attributes and prefixes as in the original XML. +sax_event(Event,_Loc,State) -> + sax_event(Event,State). + +sax_event({startPrefixMapping, Prefix, Uri},Acc) -> + %% startPrefixMapping will always come immediately before the + %% startElement where the namespace is defined. + [{xmlns,{Prefix,Uri}}|Acc]; +sax_event({startElement,_Uri,_Name,QN,Attrs},Acc) -> + %% Pick out any namespace attributes inserted due to a + %% startPrefixMapping event.The rest of Acc will then be only + %% elements. + {NsAttrs,NewAcc} = split_attrs_and_elements(Acc,[]), + Tag = qn_to_tag(QN), + [{Tag,NsAttrs ++ parse_attrs(Attrs),[]}|NewAcc]; +sax_event({endElement,_Uri,_Name,_QN},[{Name,Attrs,Cont},{Parent,PA,PC}|Acc]) -> + [{Parent,PA,[{Name,Attrs,lists:reverse(Cont)}|PC]}|Acc]; +sax_event(endDocument,[{Tag,Attrs,Cont}]) -> + {Tag,Attrs,lists:reverse(Cont)}; +sax_event({characters,String},[{Name,Attrs,Cont}|Acc]) -> + [{Name,Attrs,[String|Cont]}|Acc]; +sax_event(_Event,State) -> + State. + +split_attrs_and_elements([{xmlns,{Prefix,Uri}}|Rest],Attrs) -> + split_attrs_and_elements(Rest,[{xmlnstag(Prefix),Uri}|Attrs]); +split_attrs_and_elements(Elements,Attrs) -> + {Attrs,Elements}. + +xmlnstag([]) -> + xmlns; +xmlnstag(Prefix) -> + list_to_atom("xmlns:"++Prefix). + +qn_to_tag({[],Name}) -> + list_to_atom(Name); +qn_to_tag({Prefix,Name}) -> + list_to_atom(Prefix ++ ":" ++ Name). + +parse_attrs([{_Uri, [], Name, Value}|Attrs]) -> + [{list_to_atom(Name),Value}|parse_attrs(Attrs)]; +parse_attrs([{_Uri, Prefix, Name, Value}|Attrs]) -> + [{list_to_atom(Prefix ++ ":" ++ Name),Value}|parse_attrs(Attrs)]; +parse_attrs([]) -> + []. + + +%%%----------------------------------------------------------------- +%%% Decoding of parsed XML data +decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) -> + ConnName = Connection#connection.name, + case get_local_name_atom(Tag) of + 'rpc-reply' -> + case get_msg_id(Attrs) of + undefined -> + case Pending of + [#pending{msg_id=MsgId}] -> + ?error(ConnName,[{warning,rpc_reply_missing_msg_id}, + {assuming,MsgId}]), + decode_rpc_reply(MsgId,E,State); + _ -> + ?error(ConnName,[{error,rpc_reply_missing_msg_id}]), + {noreply,State} + end; + MsgId -> + decode_rpc_reply(MsgId,E,State) + end; + hello -> + case State#state.hello_status of + undefined -> + case decode_hello(E) of + {ok,SessionId,Capabilities} -> + {noreply,State#state{session_id = SessionId, + capabilities = Capabilities, + hello_status = received}}; + {error,Reason} -> + {noreply,State#state{hello_status = {error,Reason}}} + end; + #pending{tref=TRef,caller=Caller} -> + timer:cancel(TRef), + case decode_hello(E) of + {ok,SessionId,Capabilities} -> + ct_gen_conn:return(Caller,ok), + {noreply,State#state{session_id = SessionId, + capabilities = Capabilities, + hello_status = done}}; + {error,Reason} -> + ct_gen_conn:return(Caller,{error,Reason}), + {stop,State#state{hello_status={error,Reason}}} + end; + Other -> + ?error(ConnName,[{got_unexpected_hello,E}, + {hello_status,Other}]), + {noreply,State} + end; + notification -> + EventReceiver = State#state.event_receiver, + EventReceiver ! E, + {noreply,State}; + Other -> + %% Result of send/2, when not sending an rpc request - or + %% if netconf server sends noise. Can handle this only if + %% there is just one pending that matches (i.e. has + %% undefined msg_id and op) + case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of + [#pending{tref=TRef, + caller=Caller}] -> + timer:cancel(TRef), + ct_gen_conn:return(Caller,E), + {noreply,State#state{pending=[]}}; + _ -> + ?error(ConnName,[{got_unexpected_msg,Other}, + {expecting,Pending}]), + {noreply,State} + end + + end. + +get_msg_id(Attrs) -> + case lists:keyfind('message-id',1,Attrs) of + {_,Str} -> + list_to_integer(Str); + false -> + undefined + end. + +decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) -> + case lists:keytake(MsgId,#pending.msg_id,Pending) of + {value, #pending{tref=TRef,op=Op,caller=Caller}, Pending1} -> + timer:cancel(TRef), + Content = forward_xmlns_attr(Attrs,Content0), + {CallerReply,{ServerReply,State2}} = + do_decode_rpc_reply(Op,Content,State#state{pending=Pending1}), + ct_gen_conn:return(Caller,CallerReply), + {ServerReply,State2}; + false -> + %% Result of send/2, when receiving a correct + %% rpc-reply. Can handle this only if there is just one + %% pending that matches (i.e. has undefined msg_id and op) + case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of + [#pending{tref=TRef, + msg_id=undefined, + op=undefined, + caller=Caller}] -> + timer:cancel(TRef), + ct_gen_conn:return(Caller,E), + {noreply,State#state{pending=[]}}; + _ -> + ConnName = (State#state.connection)#connection.name, + ?error(ConnName,[{got_unexpected_msg_id,MsgId}, + {expecting,Pending}]), + {noreply,State} + end + end. + +do_decode_rpc_reply(Op,Result,State) + when Op==lock; Op==unlock; Op==edit_config; Op==delete_config; + Op==copy_config; Op==kill_session -> + {decode_ok(Result),{noreply,State}}; +do_decode_rpc_reply(Op,Result,State) + when Op==get; Op==get_config; Op==action -> + {decode_data(Result),{noreply,State}}; +do_decode_rpc_reply(close_session,Result,State) -> + case decode_ok(Result) of + ok -> {ok,{stop,State}}; + Other -> {Other,{noreply,State}} + end; +do_decode_rpc_reply({create_subscription,Caller},Result,State) -> + case decode_ok(Result) of + ok -> + {ok,{noreply,State#state{event_receiver=Caller}}}; + Other -> + {Other,{noreply,State}} + end; +do_decode_rpc_reply(get_event_streams,Result,State) -> + {decode_streams(decode_data(Result)),{noreply,State}}; +do_decode_rpc_reply(undefined,Result,State) -> + {Result,{noreply,State}}. + + + +decode_ok([{Tag,Attrs,Content}]) -> + case get_local_name_atom(Tag) of + ok -> + ok; + 'rpc-error' -> + {error,forward_xmlns_attr(Attrs,Content)}; + _Other -> + {error,{unexpected_rpc_reply,[{Tag,Attrs,Content}]}} + end; +decode_ok(Other) -> + {error,{unexpected_rpc_reply,Other}}. + +decode_data([{Tag,Attrs,Content}]) -> + case get_local_name_atom(Tag) of + data -> + %% Since content of data has nothing from the netconf + %% namespace, we remove the parent's xmlns attribute here + %% - just to make the result cleaner + {ok,forward_xmlns_attr(remove_xmlnsattr_for_tag(Tag,Attrs),Content)}; + 'rpc-error' -> + {error,forward_xmlns_attr(Attrs,Content)}; + _Other -> + {error,{unexpected_rpc_reply,[{Tag,Attrs,Content}]}} + end; +decode_data(Other) -> + {error,{unexpected_rpc_reply,Other}}. + +get_qualified_name(Tag) -> + case string:tokens(atom_to_list(Tag),":") of + [TagStr] -> {[],TagStr}; + [PrefixStr,TagStr] -> {PrefixStr,TagStr} + end. + +get_local_name_atom(Tag) -> + {_,TagStr} = get_qualified_name(Tag), + list_to_atom(TagStr). + + +%% Remove the xmlns attr that points to the tag. I.e. if the tag has a +%% prefix, remove {'xmlns:prefix',_}, else remove default {xmlns,_}. +remove_xmlnsattr_for_tag(Tag,Attrs) -> + {Prefix,_TagStr} = get_qualified_name(Tag), + XmlnsTag = xmlnstag(Prefix), + case lists:keytake(XmlnsTag,1,Attrs) of + {value,_,NoNsAttrs} -> + NoNsAttrs; + false -> + Attrs + end. + +%% Take all xmlns attributes from the parent's attribute list and +%% forward into all childrens' attribute lists. But do not overwrite +%% any. +forward_xmlns_attr(ParentAttrs,Children) -> + do_forward_xmlns_attr(get_all_xmlns_attrs(ParentAttrs,[]),Children). + +do_forward_xmlns_attr(XmlnsAttrs,[{ChT,ChA,ChC}|Children]) -> + ChA1 = add_xmlns_attrs(XmlnsAttrs,ChA), + [{ChT,ChA1,ChC} | do_forward_xmlns_attr(XmlnsAttrs,Children)]; +do_forward_xmlns_attr(_XmlnsAttrs,[]) -> + []. + +add_xmlns_attrs([{Key,_}=A|XmlnsAttrs],ChA) -> + case lists:keymember(Key,1,ChA) of + true -> + add_xmlns_attrs(XmlnsAttrs,ChA); + false -> + add_xmlns_attrs(XmlnsAttrs,[A|ChA]) + end; +add_xmlns_attrs([],ChA) -> + ChA. + +get_all_xmlns_attrs([{xmlns,_}=Default|Attrs],XmlnsAttrs) -> + get_all_xmlns_attrs(Attrs,[Default|XmlnsAttrs]); +get_all_xmlns_attrs([{Key,_}=Attr|Attrs],XmlnsAttrs) -> + case atom_to_list(Key) of + "xmlns:"++_Prefix -> + get_all_xmlns_attrs(Attrs,[Attr|XmlnsAttrs]); + _ -> + get_all_xmlns_attrs(Attrs,XmlnsAttrs) + end; +get_all_xmlns_attrs([],XmlnsAttrs) -> + XmlnsAttrs. + + +%% Decode server hello to pick out session id and capabilities +decode_hello({hello,_Attrs,Hello}) -> + case lists:keyfind('session-id',1,Hello) of + {'session-id',_,[SessionId]} -> + case lists:keyfind(capabilities,1,Hello) of + {capabilities,_,Capabilities} -> + case decode_caps(Capabilities,[],false) of + {ok,Caps} -> + {ok,list_to_integer(SessionId),Caps}; + Error -> + Error + end; + false -> + {error,{incorrect_hello,capabilities_not_found}} + end; + false -> + {error,{incorrect_hello,no_session_id_found}} + end. + +decode_caps([{capability,[],[?NETCONF_BASE_CAP++Vsn=Cap]} |Caps], Acc, _) -> + case Vsn of + ?NETCONF_BASE_CAP_VSN -> + decode_caps(Caps, [Cap|Acc], true); + _ -> + {error,{incompatible_base_capability_vsn,Vsn}} + end; +decode_caps([{capability,[],[Cap]}|Caps],Acc,Base) -> + decode_caps(Caps,[Cap|Acc],Base); +decode_caps([H|_T],_,_) -> + {error,{unexpected_capability_element,H}}; +decode_caps([],_,false) -> + {error,{incorrect_hello,no_base_capability_found}}; +decode_caps([],Acc,true) -> + {ok,lists:reverse(Acc)}. + + +%% Return a list of {Name,Data}, where data is a {Tag,Value} list for each stream +decode_streams({error,Reason}) -> + {error,Reason}; +decode_streams({ok,[{netconf,_,Streams}]}) -> + {ok,decode_streams(Streams)}; +decode_streams([{streams,_,Streams}]) -> + decode_streams(Streams); +decode_streams([{stream,_,Stream} | Streams]) -> + {name,_,[Name]} = lists:keyfind(name,1,Stream), + [{Name,[{Tag,Value} || {Tag,_,[Value]} <- Stream, Tag /= name]} + | decode_streams(Streams)]; +decode_streams([]) -> + []. + + +%%%----------------------------------------------------------------- +%%% Logging + +log(Connection,Action) -> + log(Connection,Action,<<>>). +log(#connection{host=Host,port=Port,name=Name},Action,Data) -> + error_logger:info_report(#conn_log{client=self(), + address={Host,Port}, + name=Name, + action=Action, + module=?MODULE}, + Data). + + +%% Log callback - called from the error handler process +format_data(raw,Data) -> + io_lib:format("~n~s~n",[hide_password(Data)]); +format_data(pretty,Data) -> + io_lib:format("~n~s~n",[indent(Data)]); +format_data(html,Data) -> + io_lib:format("~n~s~n",[html_format(Data)]). + +%%%----------------------------------------------------------------- +%%% Hide password elements from XML data +hide_password(Bin) -> + re:replace(Bin,<<"(<password[^>]*>)[^<]*(</password>)">>,<<"\\1*****\\2">>, + [global,{return,binary}]). + +%%%----------------------------------------------------------------- +%%% HTML formatting +html_format(Bin) -> + binary:replace(indent(Bin),<<"<">>,<<"<">>,[global]). + +%%%----------------------------------------------------------------- +%%% Indentation of XML code +indent(Bin) -> + String = normalize(hide_password(Bin)), + IndentedString = + case erase(part_of_line) of + undefined -> + indent1(String,[]); + Part -> + indent1(lists:reverse(Part)++String,erase(indent)) + end, + list_to_binary(IndentedString). + +%% Normalizes the XML document by removing all space and newline +%% between two XML tags. +%% Returns a list, no matter if the input was a list or a binary. +normalize(Str) -> + re:replace(Str,<<">[ \r\n\t]+<">>,<<"><">>,[global,{return,list}]). + + +indent1("<?"++Rest1,Indent1) -> + %% Prolog + {Line,Rest2,Indent2} = indent_line(Rest1,Indent1,[$?,$<]), + Line++indent1(Rest2,Indent2); +indent1("</"++Rest1,Indent1) -> + %% Stop tag + {Line,Rest2,Indent2} = indent_line1(Rest1,Indent1,[$/,$<]), + "\n"++Line++indent1(Rest2,Indent2); +indent1("<"++Rest1,Indent1) -> + %% Start- or empty tag + put(tag,get_tag(Rest1)), + {Line,Rest2,Indent2} = indent_line(Rest1,Indent1,[$<]), + "\n"++Line++indent1(Rest2,Indent2); +indent1([H|T],Indent) -> + [H|indent1(T,Indent)]; +indent1([],_Indent) -> + []. + +indent_line("?>"++Rest,Indent,Line) -> + %% Prolog + {lists:reverse(Line)++"?>",Rest,Indent}; +indent_line("/></"++Rest,Indent,Line) -> + %% Empty tag, and stop of parent tag -> one step out in indentation + {Indent++lists:reverse(Line)++"/>","</"++Rest,Indent--" "}; +indent_line("/>"++Rest,Indent,Line) -> + %% Empty tag, then probably next tag -> keep indentation + {Indent++lists:reverse(Line)++"/>",Rest,Indent}; +indent_line("></"++Rest,Indent,Line) -> + LastTag = erase(tag), + case get_tag(Rest) of + LastTag -> + %% Start and stop tag, but no content + indent_line1(Rest,Indent,[$/,$<,$>|Line]); + _ -> + %% Stop tag completed, and then stop tag of parent -> one step out + {Indent++lists:reverse(Line)++">","</"++Rest,Indent--" "} + end; +indent_line("><"++Rest,Indent,Line) -> + %% Stop tag completed, and new tag comming -> keep indentation + {Indent++lists:reverse(Line)++">","<"++Rest," "++Indent}; +indent_line("</"++Rest,Indent,Line) -> + %% Stop tag starting -> search for end of this tag + indent_line1(Rest,Indent,[$/,$<|Line]); +indent_line([H|T],Indent,Line) -> + indent_line(T,Indent,[H|Line]); +indent_line([],Indent,Line) -> + %% The line is not complete - will be continued later + put(part_of_line,Line), + put(indent,Indent), + {[],[],Indent}. + +indent_line1("></"++Rest,Indent,Line) -> + %% Stop tag completed, and then stop tag of parent -> one step out + {Indent++lists:reverse(Line)++">","</"++Rest,Indent--" "}; +indent_line1(">"++Rest,Indent,Line) -> + %% Stop tag completed -> keep indentation + {Indent++lists:reverse(Line)++">",Rest,Indent}; +indent_line1([H|T],Indent,Line) -> + indent_line1(T,Indent,[H|Line]); +indent_line1([],Indent,Line) -> + %% The line is not complete - will be continued later + put(part_of_line,Line), + put(indent,Indent), + {[],[],Indent}. + +get_tag("/>"++_) -> + []; +get_tag(">"++_) -> + []; +get_tag([H|T]) -> + [H|get_tag(T)]; +get_tag([]) -> + %% The line is not complete - will be continued later. + []. + + +%%%----------------------------------------------------------------- +%%% SSH stuff +ssh_receive_data() -> + receive + {ssh_cm, _CM, {data, _Ch, _Type, Data}} -> + {ok, Data}; + {ssh_cm, _CM, {Closed, _Ch}} = X when Closed == closed; Closed == eof -> + {error,X}; + {_Ref,timeout} = X -> + {error,X} + end. + +ssh_open(#options{host=Host,timeout=Timeout,port=Port,ssh=SshOpts,name=Name}) -> + case ssh:connect(Host, Port, + [{user_interaction,false}, + {silently_accept_hosts, true}|SshOpts]) of + {ok,CM} -> + case ssh_connection:session_channel(CM, Timeout) of + {ok,Ch} -> + case ssh_connection:subsystem(CM, Ch, "netconf", Timeout) of + success -> + {ok, #connection{reference = {CM,Ch}, + host = Host, + port = Port, + name = Name}}; + failure -> + ssh:close(CM), + {error,{ssh,could_not_execute_netconf_subsystem}} + end; + {error, Reason} -> + ssh:close(CM), + {error,{ssh,could_not_open_channel,Reason}}; + Other -> + %% Bug in ssh?? got {closed,0} here once... + {error,{ssh,unexpected_from_session_channel,Other}} + end; + {error,Reason} -> + {error,{ssh,could_not_connect_to_server,Reason}} + end. + +ssh_send(#connection{reference = {CM,Ch}}, Data) -> + case ssh_connection:send(CM, Ch, Data) of + ok -> ok; + {error,Reason} -> {error,{ssh,failed_to_send_data,Reason}} + end. + +ssh_close(#connection{reference = {CM,_Ch}}) -> + ssh:close(CM). + + +%%---------------------------------------------------------------------- +%% END OF MODULE +%%---------------------------------------------------------------------- diff --git a/lib/common_test/src/ct_netconfc.hrl b/lib/common_test/src/ct_netconfc.hrl new file mode 100644 index 0000000000..295a61a98b --- /dev/null +++ b/lib/common_test/src/ct_netconfc.hrl @@ -0,0 +1,58 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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% +%% +%%---------------------------------------------------------------------- +%% File: ct_netconfc.hrl +%% +%% Description: +%% This file defines constant values and records used by the +%% netconf client ct_netconfc. +%% +%% @author Support +%% @doc Netconf Client Interface. +%% @end +%%---------------------------------------------------------------------- +%%---------------------------------------------------------------------- + + +%% Default port number (RFC 4742/IANA). +-define(DEFAULT_PORT, 830). + +%% Default timeout to wait for netconf server to reply to a request +-define(DEFAULT_TIMEOUT, infinity). %% msec + +%% Namespaces +-define(NETCONF_NAMESPACE_ATTR,[{xmlns,?NETCONF_NAMESPACE}]). +-define(ACTION_NAMESPACE_ATTR,[{xmlns,?ACTION_NAMESPACE}]). +-define(NETCONF_NOTIF_NAMESPACE_ATTR,[{xmlns,?NETCONF_NOTIF_NAMESPACE}]). +-define(NETMOD_NOTIF_NAMESPACE_ATTR,[{xmlns,?NETMOD_NOTIF_NAMESPACE}]). + +-define(NETCONF_NAMESPACE,"urn:ietf:params:xml:ns:netconf:base:1.0"). +-define(ACTION_NAMESPACE,"urn:com:ericsson:ecim:1.0"). +-define(NETCONF_NOTIF_NAMESPACE, + "urn:ietf:params:xml:ns:netconf:notification:1.0"). +-define(NETMOD_NOTIF_NAMESPACE,"urn:ietf:params:xml:ns:netmod:notification"). + +%% Capabilities +-define(NETCONF_BASE_CAP,"urn:ietf:params:netconf:base:"). +-define(NETCONF_BASE_CAP_VSN,"1.0"). + +%% Misc +-define(END_TAG,<<"]]>]]>">>). + +-define(FORMAT(_F, _A), lists:flatten(io_lib:format(_F, _A))). diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl index 8ecd82f771..a47309c6ee 100644 --- a/lib/common_test/src/ct_repeat.erl +++ b/lib/common_test/src/ct_repeat.erl @@ -41,72 +41,86 @@ loop_test(If,Args) when is_list(Args) -> case get_loop_info(Args) of no_loop -> false; - {error,E} -> + E = {error,_} -> io:format("Common Test error: ~p\n\n",[E]), file:set_cwd(Cwd), E; {repeat,N} -> io:format("\nCommon Test: Will repeat tests ~w times.\n\n",[N]), Args1 = [{loop_info,[{repeat,1,N}]} | Args], - loop(If,repeat,0,N,undefined,Args1,undefined), - file:set_cwd(Cwd); + Result = loop(If,repeat,0,N,undefined,Args1,undefined,[]), + file:set_cwd(Cwd), + Result; {stop_time,StopTime} -> - case remaining_time(StopTime) of - 0 -> - io:format("\nCommon Test: No time left to run tests.\n\n",[]), - ok; - Secs -> - io:format("\nCommon Test: Will repeat tests for ~s.\n\n", - [ts(Secs)]), - TPid = - case lists:keymember(force_stop,1,Args) of - true -> - CtrlPid = self(), - spawn(fun() -> stop_after(CtrlPid,Secs) end); - false -> - undefined - end, - Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args], - loop(If,stop_time,0,Secs,StopTime,Args1,TPid) - end, - file:set_cwd(Cwd) + Result = + case remaining_time(StopTime) of + 0 -> + io:format("\nCommon Test: " + "No time left to run tests.\n\n",[]), + {error,not_enough_time}; + Secs -> + io:format("\nCommon Test: " + "Will repeat tests for ~s.\n\n",[ts(Secs)]), + TPid = + case lists:keymember(force_stop,1,Args) of + true -> + CtrlPid = self(), + spawn(fun() -> stop_after(CtrlPid,Secs) end); + false -> + undefined + end, + Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args], + loop(If,stop_time,0,Secs,StopTime,Args1,TPid,[]) + end, + file:set_cwd(Cwd), + Result end. -loop(_,repeat,N,N,_,_Args,_) -> - ok; +loop(_,repeat,N,N,_,_Args,_,AccResult) -> + lists:reverse(AccResult); -loop(If,Type,N,Data0,Data1,Args,TPid) -> +loop(If,Type,N,Data0,Data1,Args,TPid,AccResult) -> Pid = spawn_tester(If,self(),Args), receive {'EXIT',Pid,Reason} -> - io:format("Test run crashed! This could be an internal error " - "- please report!\n\n" - "~p\n\n",[Reason]), - cancel(TPid), - {error,Reason}; + case Reason of + {user_error,What} -> + io:format("\nTest run failed!\nReason: ~p\n\n\n", [What]), + cancel(TPid), + {error,What}; + _ -> + io:format("Test run crashed! This could be an internal error " + "- please report!\n\n" + "~p\n\n\n",[Reason]), + cancel(TPid), + {error,Reason} + end; {Pid,{error,Reason}} -> - io:format("\nTest run failed!\nReason: ~p\n\n",[Reason]), + io:format("\nTest run failed!\nReason: ~p\n\n\n",[Reason]), cancel(TPid), {error,Reason}; {Pid,Result} -> if Type == repeat -> - io:format("\nTest run ~w(~w) complete.\n\n",[N+1,Data0]), + io:format("\nTest run ~w(~w) complete.\n\n\n",[N+1,Data0]), lists:keydelete(loop_info,1,Args), Args1 = [{loop_info,[{repeat,N+2,Data0}]} | Args], - loop(If,repeat,N+1,Data0,Data1,Args1,TPid); + loop(If,repeat,N+1,Data0,Data1,Args1,TPid,[Result|AccResult]); Type == stop_time -> case remaining_time(Data1) of 0 -> - io:format("\nTest time (~s) has run out.\n\n",[ts(Data0)]), + io:format("\nTest time (~s) has run out.\n\n\n", + [ts(Data0)]), cancel(TPid), - Result; + lists:reverse([Result|AccResult]); Secs -> io:format("\n~s of test time remaining, " - "starting run #~w...\n\n",[ts(Secs),N+2]), + "starting run #~w...\n\n\n", + [ts(Secs),N+2]), lists:keydelete(loop_info,1,Args), ST = {stop_time,Data0,Data1,N+2}, Args1 = [{loop_info,[ST]} | Args], - loop(If,stop_time,N+1,Data0,Data1,Args1,TPid) + loop(If,stop_time,N+1,Data0,Data1,Args1,TPid, + [Result|AccResult]) end end end. diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 46aec04ec1..d80d216f9e 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -39,12 +39,20 @@ %% Misc internal functions -export([variables_file_name/1,script_start1/2,run_test2/1]). +-include("ct.hrl"). -include("ct_event.hrl"). -include("ct_util.hrl"). -define(abs(Name), filename:absname(Name)). -define(testdir(Name, Suite), ct_util:get_testdir(Name, Suite)). +-define(EXIT_STATUS_TEST_SUCCESSFUL, 0). +-define(EXIT_STATUS_TEST_CASE_FAILED, 1). +-define(EXIT_STATUS_TEST_RUN_FAILED, 2). + +-define(default_verbosity, [{default,?MAX_VERBOSITY}, + {'$unspecified',?MAX_VERBOSITY}]). + -record(opts, {label, profile, vts, @@ -54,18 +62,22 @@ step, logdir, logopts = [], + basic_html, + verbosity = [], config = [], event_handlers = [], ct_hooks = [], enable_builtin_hooks, include = [], - silent_connections, + auto_compile, + silent_connections = [], stylesheet, multiply_timetraps = 1, scale_timetraps = false, create_priv_dir, testspecs = [], - tests}). + tests, + starter}). %%%----------------------------------------------------------------- %%% @spec script_start() -> void() @@ -102,7 +114,8 @@ script_start() -> end, Flags) end, %% used for purpose of testing the run_test interface - io:format(user, "~n-------------------- START ARGS --------------------~n", []), + io:format(user, "~n-------------------- START ARGS " + "--------------------~n", []), io:format(user, "--- Init args:~n~p~n", [FlagFilter(Init)]), io:format(user, "--- CT args:~n~p~n", [FlagFilter(CtArgs)]), EnvArgs = opts2args(EnvStartOpts), @@ -110,7 +123,8 @@ script_start() -> [EnvStartOpts,EnvArgs]), Merged = merge_arguments(CtArgs ++ EnvArgs), io:format(user, "--- Merged args:~n~p~n", [FlagFilter(Merged)]), - io:format(user, "----------------------------------------------------~n~n", []), + io:format(user, "-----------------------------------" + "-----------------~n~n", []), Merged; _ -> merge_arguments(CtArgs) @@ -122,46 +136,100 @@ script_start() -> script_start(Args) -> Tracing = start_trace(Args), - Res = - case ct_repeat:loop_test(script, Args) of - false -> - {ok,Cwd} = file:get_cwd(), - CTVsn = - case filename:basename(code:lib_dir(common_test)) of - CTBase when is_list(CTBase) -> - case string:tokens(CTBase, "-") of - ["common_test",Vsn] -> " v"++Vsn; - _ -> "" - end - end, - io:format("~nCommon Test~s starting (cwd is ~s)~n~n", [CTVsn,Cwd]), - Self = self(), - Pid = spawn_link(fun() -> script_start1(Self, Args) end), - receive - {'EXIT',Pid,Reason} -> - case Reason of - {user_error,What} -> - io:format("\nTest run failed!\nReason: ~p\n\n", [What]), - {error,What}; - _ -> - io:format("Test run crashed! This could be an internal error " - "- please report!\n\n" - "~p\n\n", [Reason]), - {error,Reason} - end; - {Pid,{error,Reason}} -> - io:format("\nTest run failed! Reason:\n~p\n\n",[Reason]), - {error,Reason}; - {Pid,Result} -> - Result - end; - Result -> - Result - end, + case ct_repeat:loop_test(script, Args) of + false -> + {ok,Cwd} = file:get_cwd(), + CTVsn = + case filename:basename(code:lib_dir(common_test)) of + CTBase when is_list(CTBase) -> + case string:tokens(CTBase, "-") of + ["common_test",Vsn] -> " v"++Vsn; + _ -> "" + end + end, + io:format("~nCommon Test~s starting (cwd is ~s)~n~n", + [CTVsn,Cwd]), + Self = self(), + Pid = spawn_link(fun() -> script_start1(Self, Args) end), + receive + {'EXIT',Pid,Reason} -> + case Reason of + {user_error,What} -> + io:format("\nTest run failed!\nReason: ~p\n\n\n", + [What]), + finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args); + _ -> + io:format("Test run crashed! " + "This could be an internal error " + "- please report!\n\n" + "~p\n\n\n", [Reason]), + finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args) + end; + {Pid,{error,Reason}} -> + io:format("\nTest run failed! Reason:\n~p\n\n\n",[Reason]), + finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args); + {Pid,Result} -> + io:nl(), + finish(Tracing, analyze_test_result(Result, Args), Args) + end; + {error,_LoopReason} -> + finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args); + Result -> + io:nl(), + finish(Tracing, analyze_test_result(Result, Args), Args) + end. + +%% analyze the result of one test run, or many (in case of looped test) +analyze_test_result(ok, _) -> + ?EXIT_STATUS_TEST_SUCCESSFUL; +analyze_test_result({error,_Reason}, _) -> + ?EXIT_STATUS_TEST_RUN_FAILED; +analyze_test_result({_Ok,Failed,{_UserSkipped,AutoSkipped}}, Args) -> + if Failed > 0 -> + ?EXIT_STATUS_TEST_CASE_FAILED; + true -> + case AutoSkipped of + 0 -> + ?EXIT_STATUS_TEST_SUCCESSFUL; + _ -> + case get_start_opt(exit_status, + fun([ExitOpt]) -> ExitOpt end, + Args) of + undefined -> + ?EXIT_STATUS_TEST_CASE_FAILED; + "ignore_config" -> + ?EXIT_STATUS_TEST_SUCCESSFUL + end + end + end; +analyze_test_result([Result|Rs], Args) -> + case analyze_test_result(Result, Args) of + ?EXIT_STATUS_TEST_SUCCESSFUL -> + analyze_test_result(Rs, Args); + Other -> + Other + end; +analyze_test_result([], _) -> + ?EXIT_STATUS_TEST_SUCCESSFUL; +analyze_test_result(Unknown, _) -> + io:format("\nTest run failed! Reason:\n~p\n\n\n",[Unknown]), + ?EXIT_STATUS_TEST_RUN_FAILED. + +finish(Tracing, ExitStatus, Args) -> stop_trace(Tracing), timer:sleep(1000), - io:nl(), - Res. + %% it's possible to tell CT to finish execution with a call + %% to a different function than the normal halt/1 BIF + %% (meant to be used mainly for reading the CT exit status) + case get_start_opt(halt_with, + fun([HaltMod,HaltFunc]) -> {list_to_atom(HaltMod), + list_to_atom(HaltFunc)} end, + Args) of + undefined -> + halt(ExitStatus); + {M,F} -> + apply(M, F, [ExitStatus]) + end. script_start1(Parent, Args) -> %% read general start flags @@ -173,6 +241,7 @@ script_start1(Parent, Args) -> LogDir = get_start_opt(logdir, fun([LogD]) -> LogD end, Args), LogOpts = get_start_opt(logopts, fun(Os) -> [list_to_atom(O) || O <- Os] end, [], Args), + Verbosity = verbosity_args2opts(Args), MultTT = get_start_opt(multiply_timetraps, fun([MT]) -> list_to_integer(MT) end, 1, Args), ScaleTT = get_start_opt(scale_timetraps, @@ -206,7 +275,7 @@ script_start1(Parent, Args) -> end end, %% no_auto_compile + include - IncludeDirs = + {AutoCompile,IncludeDirs} = case proplists:get_value(no_auto_compile, Args) of undefined -> application:set_env(common_test, auto_compile, true), @@ -222,46 +291,52 @@ script_start1(Parent, Args) -> case os:getenv("CT_INCLUDE_PATH") of false -> application:set_env(common_test, include, InclDirs), - InclDirs; + {undefined,InclDirs}; CtInclPath -> AllInclDirs = string:tokens(CtInclPath,[$:,$ ,$,]) ++ InclDirs, application:set_env(common_test, include, AllInclDirs), - AllInclDirs + {undefined,AllInclDirs} end; _ -> application:set_env(common_test, auto_compile, false), - [] + {false,[]} end, %% silent connections SilentConns = get_start_opt(silent_connections, - fun(["all"]) -> []; + fun(["all"]) -> [all]; (Conns) -> [list_to_atom(Conn) || Conn <- Conns] - end, Args), + end, [], Args), %% stylesheet Stylesheet = get_start_opt(stylesheet, fun([SS]) -> ?abs(SS) end, Args), %% basic_html - used by ct_logs - case proplists:get_value(basic_html, Args) of - undefined -> - application:set_env(common_test, basic_html, false); - _ -> - application:set_env(common_test, basic_html, true) - end, + BasicHtml = case proplists:get_value(basic_html, Args) of + undefined -> + application:set_env(common_test, basic_html, false), + undefined; + _ -> + application:set_env(common_test, basic_html, true), + true + end, StartOpts = #opts{label = Label, profile = Profile, vts = Vts, shell = Shell, cover = Cover, logdir = LogDir, logopts = LogOpts, + basic_html = BasicHtml, + verbosity = Verbosity, event_handlers = EvHandlers, ct_hooks = CTHooks, enable_builtin_hooks = EnableBuiltinHooks, + auto_compile = AutoCompile, include = IncludeDirs, silent_connections = SilentConns, stylesheet = Stylesheet, multiply_timetraps = MultTT, scale_timetraps = ScaleTT, - create_priv_dir = CreatePrivDir}, + create_priv_dir = CreatePrivDir, + starter = script}, %% check if log files should be refreshed or go on to run tests... Result = run_or_refresh(StartOpts, Args), @@ -325,9 +400,15 @@ script_start2(StartOpts = #opts{vts = undefined, AllLogOpts = merge_vals([StartOpts#opts.logopts, SpecStartOpts#opts.logopts]), - - Cover = choose_val(StartOpts#opts.cover, - SpecStartOpts#opts.cover), + AllVerbosity = + merge_keyvals([StartOpts#opts.verbosity, + SpecStartOpts#opts.verbosity]), + AllSilentConns = + merge_vals([StartOpts#opts.silent_connections, + SpecStartOpts#opts.silent_connections]), + Cover = + choose_val(StartOpts#opts.cover, + SpecStartOpts#opts.cover), MultTT = choose_val(StartOpts#opts.multiply_timetraps, SpecStartOpts#opts.multiply_timetraps), @@ -352,9 +433,36 @@ script_start2(StartOpts = #opts{vts = undefined, StartOpts#opts.enable_builtin_hooks, SpecStartOpts#opts.enable_builtin_hooks), + Stylesheet = + choose_val(StartOpts#opts.stylesheet, + SpecStartOpts#opts.stylesheet), + AllInclude = merge_vals([StartOpts#opts.include, SpecStartOpts#opts.include]), application:set_env(common_test, include, AllInclude), + + AutoCompile = + case choose_val(StartOpts#opts.auto_compile, + SpecStartOpts#opts.auto_compile) of + undefined -> + true; + ACBool -> + application:set_env(common_test, + auto_compile, + ACBool), + ACBool + end, + + BasicHtml = + case choose_val(StartOpts#opts.basic_html, + SpecStartOpts#opts.basic_html) of + undefined -> + false; + BHBool -> + application:set_env(common_test, basic_html, + BHBool), + BHBool + end, {TS,StartOpts#opts{label = Label, profile = Profile, @@ -362,11 +470,16 @@ script_start2(StartOpts = #opts{vts = undefined, cover = Cover, logdir = LogDir, logopts = AllLogOpts, + basic_html = BasicHtml, + verbosity = AllVerbosity, + silent_connections = AllSilentConns, config = SpecStartOpts#opts.config, event_handlers = AllEvHs, ct_hooks = AllCTHooks, enable_builtin_hooks = EnableBuiltinHooks, + stylesheet = Stylesheet, + auto_compile = AutoCompile, include = AllInclude, multiply_timetraps = MultTT, scale_timetraps = ScaleTT, @@ -519,6 +632,7 @@ script_start4(#opts{label = Label, profile = Profile, event_handlers = EvHandlers, ct_hooks = CTHooks, logopts = LogOpts, + verbosity = Verbosity, enable_builtin_hooks = EnableBuiltinHooks, logdir = LogDir, testspecs = Specs}, _Args) -> %% label - used by ct_logs @@ -536,7 +650,8 @@ script_start4(#opts{label = Label, profile = Profile, {ct_hooks, CTHooks}, {enable_builtin_hooks,EnableBuiltinHooks}]) of ok -> - ct_util:start(interactive, LogDir), + ct_util:start(interactive, LogDir, + add_verbosity_defaults(Verbosity)), ct_util:set_testdata({logopts, LogOpts}), log_ts_names(Specs), io:nl(), @@ -553,7 +668,7 @@ script_start4(#opts{vts = true, cover = Cover}, _) -> %% Add support later (maybe). io:format("\nCan't run cover in vts mode.\n\n", []) end, - erlang:halt(); + {error,no_cover_in_vts_mode}; script_start4(#opts{shell = true, cover = Cover}, _) -> case Cover of @@ -562,7 +677,8 @@ script_start4(#opts{shell = true, cover = Cover}, _) -> _ -> %% Add support later (maybe). io:format("\nCan't run cover in interactive mode.\n\n", []) - end; + end, + {error,no_cover_in_interactive_mode}; script_start4(Opts = #opts{tests = Tests}, Args) -> do_run(Tests, [], Opts, Args). @@ -579,6 +695,7 @@ script_usage() -> "\n\t[-dir TestDir1 TestDir2 .. TestDirN] |" "\n\t[-suite Suite [-case Case]]" "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]" + "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]" "\n\t[-include InclDir1 InclDir2 .. InclDirN]" "\n\t[-no_auto_compile]" "\n\t[-multiply_timetraps N]" @@ -593,11 +710,12 @@ script_usage() -> "\n\t[-userconfig CallbackModule ConfigFile1 .. ConfigFileN]" "\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]" "\n\t[-logdir LogDir]" + "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]" + "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]" "\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]" - "\n\t[-stylesheet CSSFile]" + "\n\t[-stylesheet CSSFile]" "\n\t[-cover CoverCfgFile]" "\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]" - "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]" "\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]" "\n\t[-include InclDir1 InclDir2 .. InclDirN]" "\n\t[-no_auto_compile]" @@ -613,12 +731,13 @@ script_usage() -> "\n\t[-config ConfigFile1 ConfigFile2 .. ConfigFileN]" "\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]" "\n\t[-logdir LogDir]" + "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]" + "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]" "\n\t[-allow_user_terms]" "\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]" "\n\t[-stylesheet CSSFile]" "\n\t[-cover CoverCfgFile]" "\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]" - "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]" "\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]" "\n\t[-include InclDir1 InclDir2 .. InclDirN]" "\n\t[-no_auto_compile]" @@ -702,7 +821,7 @@ run_test(StartOpts) when is_list(StartOpts) -> Ref = monitor(process, CTPid), receive {'DOWN',Ref,process,CTPid,{user_error,Error}} -> - Error; + {error,Error}; {'DOWN',Ref,process,CTPid,Other} -> Other end. @@ -739,8 +858,10 @@ run_test2(StartOpts) -> (Lbl) when is_atom(Lbl) -> atom_to_list(Lbl) end, StartOpts), %% profile - Profile = get_start_opt(profile, fun(Prof) when is_list(Prof) -> Prof; - (Prof) when is_atom(Prof) -> atom_to_list(Prof) + Profile = get_start_opt(profile, fun(Prof) when is_list(Prof) -> + Prof; + (Prof) when is_atom(Prof) -> + atom_to_list(Prof) end, StartOpts), %% logdir LogDir = get_start_opt(logdir, fun(LD) when is_list(LD) -> LD end, @@ -748,6 +869,19 @@ run_test2(StartOpts) -> %% logopts LogOpts = get_start_opt(logopts, value, [], StartOpts), + %% verbosity + Verbosity = + get_start_opt(verbosity, + fun(VLvls) when is_list(VLvls) -> + lists:map(fun(VLvl = {_Cat,_Lvl}) -> + VLvl; + (Lvl) -> + {'$unspecified',Lvl} + end, VLvls); + (VLvl) when is_integer(VLvl) -> + [{'$unspecified',VLvl}] + end, [], StartOpts), + %% config & userconfig CfgFiles = ct_config:get_config_file_list(StartOpts), @@ -786,9 +920,9 @@ run_test2(StartOpts) -> %% silent connections SilentConns = get_start_opt(silent_connections, - fun(all) -> []; + fun(all) -> [all]; (Conns) -> Conns - end, StartOpts), + end, [], StartOpts), %% stylesheet Stylesheet = get_start_opt(stylesheet, fun(SS) -> ?abs(SS) end, @@ -805,7 +939,7 @@ run_test2(StartOpts) -> CreatePrivDir = get_start_opt(create_priv_dir, value, StartOpts), %% auto compile & include files - Include = + {AutoCompile,Include} = case proplists:get_value(auto_compile, StartOpts) of undefined -> application:set_env(common_test, auto_compile, true), @@ -821,16 +955,16 @@ run_test2(StartOpts) -> case os:getenv("CT_INCLUDE_PATH") of false -> application:set_env(common_test, include, InclDirs), - InclDirs; + {undefined,InclDirs}; CtInclPath -> InclDirs1 = string:tokens(CtInclPath, [$:,$ ,$,]), AllInclDirs = InclDirs1++InclDirs, application:set_env(common_test, include, AllInclDirs), - AllInclDirs + {undefined,AllInclDirs} end; ACBool -> application:set_env(common_test, auto_compile, ACBool), - [] + {ACBool,[]} end, %% decrypt config file @@ -844,11 +978,14 @@ run_test2(StartOpts) -> end, %% basic html - used by ct_logs - case proplists:get_value(basic_html, StartOpts) of - undefined -> - application:set_env(common_test, basic_html, false); - BasicHtmlBool -> - application:set_env(common_test, basic_html, BasicHtmlBool) + BasicHtml = + case proplists:get_value(basic_html, StartOpts) of + undefined -> + application:set_env(common_test, basic_html, false), + undefined; + BasicHtmlBool -> + application:set_env(common_test, basic_html, BasicHtmlBool), + BasicHtmlBool end, %% stepped execution @@ -856,16 +993,20 @@ run_test2(StartOpts) -> Opts = #opts{label = Label, profile = Profile, cover = Cover, step = Step, logdir = LogDir, - logopts = LogOpts, config = CfgFiles, + logopts = LogOpts, basic_html = BasicHtml, + config = CfgFiles, + verbosity = Verbosity, event_handlers = EvHandlers, ct_hooks = CTHooks, enable_builtin_hooks = EnableBuiltinHooks, + auto_compile = AutoCompile, include = Include, silent_connections = SilentConns, stylesheet = Stylesheet, multiply_timetraps = MultiplyTT, scale_timetraps = ScaleTT, - create_priv_dir = CreatePrivDir}, + create_priv_dir = CreatePrivDir, + starter = ct}, %% test specification case proplists:get_value(spec, StartOpts) of @@ -894,7 +1035,7 @@ run_spec_file(Relaxed, log_ts_names(AbsSpecs), case catch ct_testspec:collect_tests_from_file(AbsSpecs, Relaxed) of {Error,CTReason} when Error == error ; Error == 'EXIT' -> - exit(CTReason); + exit({error,CTReason}); TS -> SpecOpts = get_data_for_node(TS, node()), Label = choose_val(Opts#opts.label, @@ -905,6 +1046,12 @@ run_spec_file(Relaxed, SpecOpts#opts.logdir), AllLogOpts = merge_vals([Opts#opts.logopts, SpecOpts#opts.logopts]), + Stylesheet = choose_val(Opts#opts.stylesheet, + SpecOpts#opts.stylesheet), + AllVerbosity = merge_keyvals([Opts#opts.verbosity, + SpecOpts#opts.verbosity]), + AllSilentConns = merge_vals([Opts#opts.silent_connections, + SpecOpts#opts.silent_connections]), AllConfig = merge_vals([CfgFiles, SpecOpts#opts.config]), Cover = choose_val(Opts#opts.cover, SpecOpts#opts.cover), @@ -918,21 +1065,45 @@ run_spec_file(Relaxed, SpecOpts#opts.event_handlers]), AllInclude = merge_vals([Opts#opts.include, SpecOpts#opts.include]), - AllCTHooks = merge_vals([Opts#opts.ct_hooks, - SpecOpts#opts.ct_hooks]), + SpecOpts#opts.ct_hooks]), EnableBuiltinHooks = choose_val(Opts#opts.enable_builtin_hooks, SpecOpts#opts.enable_builtin_hooks), application:set_env(common_test, include, AllInclude), + AutoCompile = case choose_val(Opts#opts.auto_compile, + SpecOpts#opts.auto_compile) of + undefined -> + true; + ACBool -> + application:set_env(common_test, auto_compile, + ACBool), + ACBool + end, + + BasicHtml = case choose_val(Opts#opts.basic_html, + SpecOpts#opts.basic_html) of + undefined -> + false; + BHBool -> + application:set_env(common_test, basic_html, + BHBool), + BHBool + end, + Opts1 = Opts#opts{label = Label, profile = Profile, cover = Cover, logdir = which(logdir, LogDir), logopts = AllLogOpts, + stylesheet = Stylesheet, + basic_html = BasicHtml, + verbosity = AllVerbosity, + silent_connections = AllSilentConns, config = AllConfig, event_handlers = AllEvHs, + auto_compile = AutoCompile, include = AllInclude, testspecs = AbsSpecs, multiply_timetraps = MultTT, @@ -948,20 +1119,20 @@ run_spec_file(Relaxed, {Run,Skip} = ct_testspec:prepare_tests(TS, node()), reformat_result(catch do_run(Run, Skip, Opts1, StartOpts)); {error,GCFReason} -> - exit(GCFReason) + exit({error,GCFReason}) end end. run_prepared(Run, Skip, Opts = #opts{logdir = LogDir, - config = CfgFiles }, + config = CfgFiles}, StartOpts) -> LogDir1 = which(logdir, LogDir), case check_and_install_configfiles(CfgFiles, LogDir1, Opts) of ok -> reformat_result(catch do_run(Run, Skip, Opts#opts{logdir = LogDir1}, StartOpts)); - {error,Reason} -> - exit(Reason) + {error,_Reason} = Error -> + exit(Error) end. check_config_file(Callback, File)-> @@ -969,7 +1140,7 @@ check_config_file(Callback, File)-> false -> case code:load_file(Callback) of {module,_} -> ok; - {error,Why} -> exit({cant_load_callback_module,Why}) + {error,Why} -> exit({error,{cant_load_callback_module,Why}}) end; _ -> ok @@ -980,16 +1151,17 @@ check_config_file(Callback, File)-> {ok,{config,_}}-> File; {error,{wrong_config,Message}}-> - exit({wrong_config,{Callback,Message}}); + exit({error,{wrong_config,{Callback,Message}}}); {error,{nofile,File}}-> - exit({no_such_file,?abs(File)}) + exit({error,{no_such_file,?abs(File)}}) end. run_dir(Opts = #opts{logdir = LogDir, config = CfgFiles, event_handlers = EvHandlers, ct_hooks = CTHook, - enable_builtin_hooks = EnableBuiltinHooks }, StartOpts) -> + enable_builtin_hooks = EnableBuiltinHooks}, + StartOpts) -> LogDir1 = which(logdir, LogDir), Opts1 = Opts#opts{logdir = LogDir1}, AbsCfgFiles = @@ -1002,7 +1174,8 @@ run_dir(Opts = #opts{logdir = LogDir, {module,Callback}-> ok; {error,_}-> - exit({no_such_module,Callback}) + exit({error,{no_such_module, + Callback}}) end end, {Callback, @@ -1015,7 +1188,7 @@ run_dir(Opts = #opts{logdir = LogDir, {ct_hooks, CTHook}, {enable_builtin_hooks,EnableBuiltinHooks}], LogDir1) of ok -> ok; - {error,IReason} -> exit(IReason) + {error,_IReason} = IError -> exit(IError) end, case {proplists:get_value(dir, StartOpts), proplists:get_value(suite, StartOpts), @@ -1057,7 +1230,7 @@ run_dir(Opts = #opts{logdir = LogDir, [], Opts1, StartOpts)); {undefined,[Hd,_|_],_GsAndCs} when not is_integer(Hd) -> - exit(multiple_suites_and_cases); + exit({error,multiple_suites_and_cases}); {undefined,Suite=[Hd|Tl],GsAndCs} when is_integer(Hd) ; (is_list(Hd) and (Tl == [])) ; @@ -1067,10 +1240,10 @@ run_dir(Opts = #opts{logdir = LogDir, [], Opts1, StartOpts)); {[Hd,_|_],_Suites,[]} when is_list(Hd) ; not is_integer(Hd) -> - exit(multiple_dirs_and_suites); + exit({error,multiple_dirs_and_suites}); {undefined,undefined,GsAndCs} when GsAndCs /= [] -> - exit(incorrect_start_options); + exit({error,incorrect_start_options}); {Dir,Suite,GsAndCs} when is_integer(hd(Dir)) ; (is_atom(Dir) and (Dir /= undefined)) ; @@ -1079,7 +1252,7 @@ run_dir(Opts = #opts{logdir = LogDir, Dir1 = if is_atom(Dir) -> atom_to_list(Dir); true -> Dir end, if Suite == undefined -> - exit(incorrect_start_options); + exit({error,incorrect_start_options}); is_integer(hd(Suite)) ; (is_atom(Suite) and (Suite /= undefined)) ; @@ -1098,7 +1271,7 @@ run_dir(Opts = #opts{logdir = LogDir, is_list(Suite) -> % multiple suites case [suite_to_test(Dir1, S) || S <- Suite] of [_,_|_] when GsAndCs /= [] -> - exit(multiple_suites_and_cases); + exit({error,multiple_suites_and_cases}); [{Dir2,Mod}] when GsAndCs /= [] -> reformat_result(catch do_run(tests(Dir2, Mod, GsAndCs), [], Opts1, StartOpts)); @@ -1109,10 +1282,10 @@ run_dir(Opts = #opts{logdir = LogDir, end; {undefined,undefined,[]} -> - exit(no_test_specified); + exit({error,no_test_specified}); {Dir,Suite,GsAndCs} -> - exit({incorrect_start_options,{Dir,Suite,GsAndCs}}) + exit({error,{incorrect_start_options,{Dir,Suite,GsAndCs}}}) end. %%%----------------------------------------------------------------- @@ -1157,7 +1330,7 @@ run_testspec2(File) when is_list(File), is_integer(hd(File)) -> run_testspec2(TestSpec) -> case catch ct_testspec:collect_tests_from_list(TestSpec, false) of {E,CTReason} when E == error ; E == 'EXIT' -> - exit(CTReason); + exit({error,CTReason}); TS -> Opts = get_data_for_node(TS, node()), @@ -1179,8 +1352,8 @@ run_testspec2(TestSpec) -> include = AllInclude}, {Run,Skip} = ct_testspec:prepare_tests(TS, node()), reformat_result(catch do_run(Run, Skip, Opts1, [])); - {error,GCFReason} -> - exit(GCFReason) + {error,_GCFReason} = GCFError -> + exit(GCFError) end end. @@ -1188,12 +1361,17 @@ get_data_for_node(#testspec{label = Labels, profile = Profiles, logdir = LogDirs, logopts = LogOptsList, + basic_html = BHs, + stylesheet = SSs, + verbosity = VLvls, + silent_connections = SilentConnsList, cover = CoverFs, config = Cfgs, userconfig = UsrCfgs, event_handler = EvHs, ct_hooks = CTHooks, enable_builtin_hooks = EnableBuiltinHooks, + auto_compile = ACs, include = Incl, multiply_timetraps = MTs, scale_timetraps = STs, @@ -1208,6 +1386,16 @@ get_data_for_node(#testspec{label = Labels, undefined -> []; LOs -> LOs end, + BasicHtml = proplists:get_value(Node, BHs), + Stylesheet = proplists:get_value(Node, SSs), + Verbosity = case proplists:get_value(Node, VLvls) of + undefined -> []; + Lvls -> Lvls + end, + SilentConns = case proplists:get_value(Node, SilentConnsList) of + undefined -> []; + SCs -> SCs + end, Cover = proplists:get_value(Node, CoverFs), MT = proplists:get_value(Node, MTs), ST = proplists:get_value(Node, STs), @@ -1216,16 +1404,22 @@ get_data_for_node(#testspec{label = Labels, [CBF || {N,CBF} <- UsrCfgs, N==Node], EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node], FiltCTHooks = [Hook || {N,Hook} <- CTHooks, N==Node], + AutoCompile = proplists:get_value(Node, ACs), Include = [I || {N,I} <- Incl, N==Node], #opts{label = Label, profile = Profile, logdir = LogDir, logopts = LogOpts, + basic_html = BasicHtml, + stylesheet = Stylesheet, + verbosity = Verbosity, + silent_connections = SilentConns, cover = Cover, config = ConfigFiles, event_handlers = EvHandlers, ct_hooks = FiltCTHooks, enable_builtin_hooks = EnableBuiltinHooks, + auto_compile = AutoCompile, include = Include, multiply_timetraps = MT, scale_timetraps = ST, @@ -1267,6 +1461,14 @@ choose_val(V0, _V1) -> merge_vals(Vs) -> lists:append(Vs). +merge_keyvals(Vs) -> + make_unique(lists:append(Vs)). + +make_unique([Elem={Key,_} | Elems]) -> + [Elem | make_unique(proplists:delete(Key, Elems))]; +make_unique([]) -> + []. + listify([C|_]=Str) when is_integer(C) -> [Str]; listify(L) when is_list(L) -> L; listify(E) -> [E]. @@ -1376,7 +1578,8 @@ do_run(Tests, Misc, LogDir, LogOpts) when is_list(Misc), do_run(Tests, [], Opts1#opts{logdir = LogDir}, []); do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) -> - #opts{label = Label, profile = Profile, cover = Cover} = Opts, + #opts{label = Label, profile = Profile, cover = Cover, + verbosity = VLvls} = Opts, %% label - used by ct_logs TestLabel = if Label == undefined -> undefined; @@ -1397,7 +1600,7 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) -> case code:which(test_server) of non_existing -> - exit({error,no_path_to_test_server}); + {error,no_path_to_test_server}; _ -> Opts1 = if Cover == undefined -> Opts; @@ -1418,77 +1621,131 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) -> "ct_framework" -> ok; Other -> - erlang:display(list_to_atom("Note: TEST_SERVER_FRAMEWORK = " ++ Other)) + erlang:display( + list_to_atom( + "Note: TEST_SERVER_FRAMEWORK = " ++ Other)) end, - case ct_util:start(Opts#opts.logdir) of + Verbosity = add_verbosity_defaults(VLvls), + case ct_util:start(Opts#opts.logdir, Verbosity) of {error,interactive_mode} -> io:format("CT is started in interactive mode. " - "To exit this mode, run ct:stop_interactive().\n" + "To exit this mode, " + "run ct:stop_interactive().\n" "To enter the interactive mode again, " "run ct:start_interactive()\n\n",[]), {error,interactive_mode}; _Pid -> - %% save stylesheet info - ct_util:set_testdata({stylesheet,Opts#opts.stylesheet}), - %% save logopts - ct_util:set_testdata({logopts,Opts#opts.logopts}), - %% enable silent connections - case Opts#opts.silent_connections of - [] -> - Conns = ct_util:override_silence_all_connections(), - ct_logs:log("Silent connections", "~p", [Conns]); - Conns when is_list(Conns) -> - ct_util:override_silence_connections(Conns), - ct_logs:log("Silent connections", "~p", [Conns]); - _ -> - ok - end, - log_ts_names(Opts1#opts.testspecs), - TestSuites = suite_tuples(Tests), - - {_TestSuites1,SuiteMakeErrors,AllMakeErrors} = - case application:get_env(common_test, auto_compile) of - {ok,false} -> - {TestSuites1,SuitesNotFound} = - verify_suites(TestSuites), - {TestSuites1,SuitesNotFound,SuitesNotFound}; - _ -> - {SuiteErrs,HelpErrs} = auto_compile(TestSuites), - {TestSuites,SuiteErrs,SuiteErrs++HelpErrs} - end, + ct_util:set_testdata({starter,Opts#opts.starter}), + compile_and_run(Tests, Skip, + Opts1#opts{verbosity=Verbosity}, Args) + end + end. - case continue(AllMakeErrors) of - true -> - SavedErrors = save_make_errors(SuiteMakeErrors), - ct_repeat:log_loop_info(Args), +compile_and_run(Tests, Skip, Opts, Args) -> + %% save stylesheet info + ct_util:set_testdata({stylesheet,Opts#opts.stylesheet}), + %% save logopts + ct_util:set_testdata({logopts,Opts#opts.logopts}), + %% enable silent connections + case Opts#opts.silent_connections of + [] -> + ok; + Conns -> + case lists:member(all, Conns) of + true -> + Conns1 = ct_util:override_silence_all_connections(), + ct_logs:log("Silent connections", "~p", [Conns1]); + false -> + ct_util:override_silence_connections(Conns), + ct_logs:log("Silent connections", "~p", [Conns]) + end + end, + log_ts_names(Opts#opts.testspecs), + TestSuites = suite_tuples(Tests), + + {_TestSuites1,SuiteMakeErrors,AllMakeErrors} = + case application:get_env(common_test, auto_compile) of + {ok,false} -> + {TestSuites1,SuitesNotFound} = + verify_suites(TestSuites), + {TestSuites1,SuitesNotFound,SuitesNotFound}; + _ -> + {SuiteErrs,HelpErrs} = auto_compile(TestSuites), + {TestSuites,SuiteErrs,SuiteErrs++HelpErrs} + end, + + case continue(AllMakeErrors) of + true -> + SavedErrors = save_make_errors(SuiteMakeErrors), + ct_repeat:log_loop_info(Args), + + {Tests1,Skip1} = final_tests(Tests,Skip,SavedErrors), + + ReleaseSh = proplists:get_value(release_shell, Args), + ct_util:set_testdata({release_shell,ReleaseSh}), + possibly_spawn(ReleaseSh == true, Tests1, Skip1, Opts); + false -> + io:nl(), + ct_util:stop(clean), + BadMods = + lists:foldr( + fun({{_,_},Ms}, Acc) -> + Ms ++ lists:foldl( + fun(M, Acc1) -> + lists:delete(M, Acc1) + end, Acc, Ms) + end, [], AllMakeErrors), + {error,{make_failed,BadMods}} + end. - {Tests1,Skip1} = final_tests(Tests,Skip,SavedErrors), +%% keep the shell as the top controlling process +possibly_spawn(false, Tests, Skip, Opts) -> + TestResult = (catch do_run_test(Tests, Skip, Opts)), + case TestResult of + {EType,_} = Error when EType == user_error; + EType == error -> + ct_util:stop(clean), + exit(Error); + _ -> + ct_util:stop(normal), + TestResult + end; - R = (catch do_run_test(Tests1, Skip1, Opts1)), - case R of - {EType,_} = Error when EType == user_error ; +%% we must return control to the shell now, so we spawn +%% a test supervisor process to keep an eye on the test run +possibly_spawn(true, Tests, Skip, Opts) -> + CTUtilSrv = whereis(ct_util_server), + Supervisor = + fun() -> + process_flag(trap_exit, true), + link(CTUtilSrv), + TestRun = + fun() -> + TestResult = (catch do_run_test(Tests, Skip, Opts)), + case TestResult of + {EType,_} = Error when EType == user_error; EType == error -> ct_util:stop(clean), exit(Error); _ -> ct_util:stop(normal), - R - end; - false -> - io:nl(), - ct_util:stop(clean), - BadMods = - lists:foldr( - fun({{_,_},Ms}, Acc) -> - Ms ++ lists:foldl( - fun(M, Acc1) -> - lists:delete(M, Acc1) - end, Acc, Ms) - end, [], AllMakeErrors), - {error,{make_failed,BadMods}} - end - end - end. + exit({ok,TestResult}) + end + end, + TestRunPid = spawn_link(TestRun), + receive + {'EXIT',TestRunPid,{ok,TestResult}} -> + io:format(user, "~nCommon Test returned ~p~n~n", + [TestResult]); + {'EXIT',TestRunPid,Error} -> + exit(Error) + end + end, + unlink(CTUtilSrv), + SupPid = spawn(Supervisor), + io:format(user, "~nTest control handed over to process ~p~n~n", + [SupPid]), + SupPid. %% attempt to compile the modules specified in TestSuites auto_compile(TestSuites) -> @@ -1504,11 +1761,13 @@ auto_compile(TestSuites) -> end, SuiteMakeErrors = lists:flatmap(fun({TestDir,Suite} = TS) -> - case run_make(suites, TestDir, Suite, UserInclude) of + case run_make(suites, TestDir, + Suite, UserInclude) of {error,{make_failed,Bad}} -> [{TS,Bad}]; {error,_} -> - [{TS,[filename:join(TestDir,"*_SUITE")]}]; + [{TS,[filename:join(TestDir, + "*_SUITE")]}]; _ -> [] end @@ -1547,23 +1806,29 @@ verify_suites(TestSuites) -> {[DS|Found],NotFound}; true -> Beam = filename:join(TestDir, - atom_to_list(Suite)++".beam"), + atom_to_list(Suite)++ + ".beam"), case filelib:is_regular(Beam) of true -> {[DS|Found],NotFound}; false -> case code:is_loaded(Suite) of {file,SuiteFile} -> - %% test suite is already loaded and - %% since auto_compile == false, + %% test suite is already + %% loaded and since + %% auto_compile == false, %% let's assume the user has - %% loaded the beam file explicitly - ActualDir = filename:dirname(SuiteFile), - {[{ActualDir,Suite}|Found],NotFound}; + %% loaded the beam file + %% explicitly + ActualDir = + filename:dirname(SuiteFile), + {[{ActualDir,Suite}|Found], + NotFound}; false -> Name = filename:join(TestDir, - atom_to_list(Suite)), + atom_to_list( + Suite)), io:format(user, "Suite ~w not found" "in directory ~s~n", @@ -1581,7 +1846,8 @@ verify_suites(TestSuites) -> ActualDir = filename:dirname(SuiteFile), {[{ActualDir,Suite}|Found],NotFound}; false -> - io:format(user, "Directory ~s is invalid~n", [Dir]), + io:format(user, "Directory ~s is " + "invalid~n", [Dir]), Name = filename:join(Dir, atom_to_list(Suite)), {Found,[{DS,[Name]}|NotFound]} end @@ -1595,7 +1861,8 @@ save_make_errors([]) -> save_make_errors(Errors) -> Suites = get_bad_suites(Errors,[]), ct_logs:log("MAKE RESULTS", - "Error compiling or locating the following suites: ~n~p",[Suites]), + "Error compiling or locating the " + "following suites: ~n~p",[Suites]), %% save the info for logger file:write_file(?missing_suites_info,term_to_binary(Errors)), Errors. @@ -1616,8 +1883,9 @@ step(TestDir, Suite, Case) -> %%%----------------------------------------------------------------- %%% @hidden %%% @equiv ct:step/4 -step(TestDir, Suite, Case, Opts) when is_list(TestDir), is_atom(Suite), is_atom(Case), - Suite =/= all, Case =/= all -> +step(TestDir, Suite, Case, Opts) when is_list(TestDir), + is_atom(Suite), is_atom(Case), + Suite =/= all, Case =/= all -> do_run([{TestDir,Suite,Case}], [{step,Opts}]). @@ -1735,9 +2003,11 @@ continue(_MakeErrors) -> case set_group_leader_same_as_shell() of true -> S = self(), - io:format("Failed to compile or locate one or more test suites\n" + io:format("Failed to compile or locate one " + "or more test suites\n" "Press \'c\' to continue or \'a\' to abort.\n" - "Will continue in 15 seconds if no answer is given!\n"), + "Will continue in 15 seconds if no " + "answer is given!\n"), Pid = spawn(fun() -> case io:get_line('(c/a) ') of "c\n" -> @@ -1769,7 +2039,8 @@ set_group_leader_same_as_shell() -> end end, case [P || P <- processes(), GS2or3(P), - true == lists:keymember(shell,1,element(2,process_info(P,dictionary)))] of + true == lists:keymember(shell,1, + element(2,process_info(P,dictionary)))] of [GL|_] -> group_leader(GL, self()); [] -> @@ -1815,12 +2086,14 @@ do_run_test(Tests, Skip, Opts) -> incl_mods = CovIncl, cross = CovCross, src = _CovSrc}} -> - ct_logs:log("COVER INFO","Using cover specification file: ~s~n" + ct_logs:log("COVER INFO", + "Using cover specification file: ~s~n" "App: ~w~n" "Cross cover: ~w~n" "Including ~w modules~n" "Excluding ~w modules", - [CovFile,CovApp,CovCross,length(CovIncl),length(CovExcl)]), + [CovFile,CovApp,CovCross, + length(CovIncl),length(CovExcl)]), %% cover export file will be used for export and import %% between tests so make sure it doesn't exist initially @@ -1828,7 +2101,8 @@ do_run_test(Tests, Skip, Opts) -> true -> DelResult = file:delete(CovExport), ct_logs:log("COVER INFO", - "Warning! Export file ~s already exists. " + "Warning! " + "Export file ~s already exists. " "Deleting with result: ~p", [CovExport,DelResult]); false -> @@ -1844,7 +2118,8 @@ do_run_test(Tests, Skip, Opts) -> %% start cover on specified nodes if (CovNodes /= []) and (CovNodes /= undefined) -> ct_logs:log("COVER INFO", - "Nodes included in cover session: ~w", + "Nodes included in cover " + "session: ~w", [CovNodes]), cover:start(CovNodes); true -> @@ -1869,17 +2144,27 @@ do_run_test(Tests, Skip, Opts) -> ct_logs:log("TEST INFO","~w test(s), ~w suite(s)", [NoOfTests,NoOfSuites]); true -> - io:format("~nTEST INFO: ~w test(s), ~w case(s) in ~w suite(s)~n~n", + io:format("~nTEST INFO: ~w test(s), ~w case(s) " + "in ~w suite(s)~n~n", [NoOfTests,NoOfCases,NoOfSuites]), - ct_logs:log("TEST INFO","~w test(s), ~w case(s) in ~w suite(s)", + ct_logs:log("TEST INFO","~w test(s), ~w case(s) " + "in ~w suite(s)", [NoOfTests,NoOfCases,NoOfSuites]) end, - + %% if the verbosity level is set lower than ?STD_IMPORTANCE, tell + %% test_server to ignore stdout printouts to the test case log file + case proplists:get_value(default, Opts#opts.verbosity) of + VLvl when is_integer(VLvl), (?STD_IMPORTANCE < (100-VLvl)) -> + test_server_ctrl:reject_io_reqs(true); + _Lower -> + ok + end, test_server_ctrl:multiply_timetraps(Opts#opts.multiply_timetraps), test_server_ctrl:scale_timetraps(Opts#opts.scale_timetraps), - test_server_ctrl:create_priv_dir(choose_val(Opts#opts.create_priv_dir, - auto_per_run)), + test_server_ctrl:create_priv_dir(choose_val( + Opts#opts.create_priv_dir, + auto_per_run)), ct_event:notify(#event{name=start_info, node=node(), data={NoOfTests,NoOfSuites,NoOfCases}}), @@ -1898,9 +2183,15 @@ do_run_test(Tests, Skip, Opts) -> maybe_cleanup_interpret(Suite, Opts#opts.step) end, CleanUp), [code:del_path(Dir) || Dir <- AddedToPath], - ok; + + case ct_util:get_testdata(stats) of + Stats = {_Ok,_Failed,{_UserSkipped,_AutoSkipped}} -> + Stats; + _ -> + {error,test_result_unknown} + end; Error -> - Error + exit(Error) end. delete_dups([S | Suites]) -> @@ -2357,7 +2648,6 @@ parse_cth_args(String) -> String end. - event_handler_args2opts(Args) -> case proplists:get_value(event_handler, Args) of undefined -> @@ -2380,6 +2670,42 @@ event_handler_init_args2opts([EH, Arg]) -> event_handler_init_args2opts([]) -> []. +verbosity_args2opts(Args) -> + case proplists:get_value(verbosity, Args) of + undefined -> + []; + VArgs -> + GetVLvls = + fun("and", {new,SoFar}) when is_list(SoFar) -> + {new,SoFar}; + ("and", {Lvl,SoFar}) when is_list(SoFar) -> + {new,[{'$unspecified',list_to_integer(Lvl)} | SoFar]}; + (CatOrLvl, {new,SoFar}) when is_list(SoFar) -> + {CatOrLvl,SoFar}; + (Lvl, {Cat,SoFar}) -> + {new,[{list_to_atom(Cat),list_to_integer(Lvl)} | SoFar]} + end, + case lists:foldl(GetVLvls, {new,[]}, VArgs) of + {new,Parsed} -> + Parsed; + {Lvl,Parsed} -> + [{'$unspecified',list_to_integer(Lvl)} | Parsed] + end + end. + +add_verbosity_defaults(VLvls) -> + case {proplists:get_value('$unspecified', VLvls), + proplists:get_value(default, VLvls)} of + {undefined,undefined} -> + ?default_verbosity ++ VLvls; + {Lvl,undefined} -> + [{default,Lvl} | VLvls]; + {undefined,_Lvl} -> + [{'$unspecified',?MAX_VERBOSITY} | VLvls]; + _ -> + VLvls + end. + %% This function reads pa and pz arguments, converts dirs from relative %% to absolute, and re-inserts them in the code path. The order of the %% dirs in the code path remain the same. Note however that since this @@ -2446,7 +2772,11 @@ make_abs1([], Path) -> %% to ct_run start arguments (on the init arguments format) - %% this is useful mainly for testing the ct_run start functions. opts2args(EnvStartOpts) -> - lists:flatmap(fun({config,CfgFiles}) -> + lists:flatmap(fun({exit_status,ExitStatusOpt}) when is_atom(ExitStatusOpt) -> + [{exit_status,[atom_to_list(ExitStatusOpt)]}]; + ({halt_with,{HaltM,HaltF}}) -> + [{halt_with,[atom_to_list(HaltM),atom_to_list(HaltF)]}]; + ({config,CfgFiles}) -> [{ct_config,[CfgFiles]}]; ({userconfig,{CBM,CfgStr=[X|_]}}) when is_integer(X) -> [{userconfig,[atom_to_list(CBM),CfgStr]}]; @@ -2454,10 +2784,14 @@ opts2args(EnvStartOpts) -> [{userconfig,[atom_to_list(CBM) | CfgStrs]}]; ({userconfig,UserCfg}) when is_list(UserCfg) -> Strs = - lists:map(fun({CBM,CfgStr=[X|_]}) when is_integer(X) -> - [atom_to_list(CBM),CfgStr,"and"]; - ({CBM,CfgStrs}) when is_list(CfgStrs) -> - [atom_to_list(CBM) | CfgStrs] ++ ["and"] + lists:map(fun({CBM,CfgStr=[X|_]}) + when is_integer(X) -> + [atom_to_list(CBM), + CfgStr,"and"]; + ({CBM,CfgStrs}) + when is_list(CfgStrs) -> + [atom_to_list(CBM) | CfgStrs] ++ + ["and"] end, UserCfg), [_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)), [{userconfig,lists:reverse(StrsR)}]; @@ -2492,7 +2826,7 @@ opts2args(EnvStartOpts) -> ({decrypt,{file,File}}) -> [{ct_decrypt_file,[File]}]; ({basic_html,true}) -> - ({basic_html,[]}); + [{basic_html,[]}]; ({basic_html,false}) -> []; ({event_handler,EH}) when is_atom(EH) -> @@ -2505,12 +2839,32 @@ opts2args(EnvStartOpts) -> ({event_handler,{EHs,Arg}}) when is_list(EHs) -> ArgStr = lists:flatten(io_lib:format("~p", [Arg])), Strs = lists:map(fun(EH) -> - [atom_to_list(EH),ArgStr,"and"] + [atom_to_list(EH), + ArgStr,"and"] end, EHs), [_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)), [{event_handler_init,lists:reverse(StrsR)}]; ({logopts,LOs}) when is_list(LOs) -> [{logopts,[atom_to_list(LO) || LO <- LOs]}]; + ({verbosity,?default_verbosity}) -> + []; + ({verbosity,VLvl}) when is_integer(VLvl) -> + [{verbosity,[integer_to_list(VLvl)]}]; + ({verbosity,VLvls}) when is_list(VLvls) -> + VLvlArgs = + lists:flatmap(fun({'$unspecified',Lvl}) -> + [integer_to_list(Lvl), + "and"]; + ({Cat,Lvl}) -> + [atom_to_list(Cat), + integer_to_list(Lvl), + "and"]; + (Lvl) -> + [integer_to_list(Lvl), + "and"] + end, VLvls), + [_LastAnd|VLvlArgsR] = lists:reverse(VLvlArgs), + [{verbosity,lists:reverse(VLvlArgsR)}]; ({ct_hooks,[]}) -> []; ({ct_hooks,CTHs}) when is_list(CTHs) -> diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl index aebb28bc42..d0d94e1e6e 100644 --- a/lib/common_test/src/ct_ssh.erl +++ b/lib/common_test/src/ct_ssh.erl @@ -133,10 +133,11 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) -> %%% is used to identify the connection, this name may %%% be used as connection reference for subsequent calls. %%% It's only possible to have one open connection at a time -%%% associated with <code>Name</code>. If <code>Key</code> is +%%% associated with <code>Name</code>. If <code>Key</code> is %%% used, the returned handle must be used for subsequent calls %%% (multiple connections may be opened using the config -%%% data specified by <code>Key</code>).</p> +%%% data specified by <code>Key</code>). See <c>ct:require/2</c> +%%% for how to create a new <c>Name</c></p> %%% %%% <p><code>ConnType</code> will always override the type %%% specified in the address tuple in the configuration data (and @@ -152,6 +153,8 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) -> %%% The extra options will override any existing options with the %%% same key in the config data. For details on valid SSH %%% options, see the documentation for the OTP ssh application.</p> +%%% +%%% @see ct:require/2 connect(KeyOrName, ConnType, ExtraOpts) -> case ct:get_config(KeyOrName) of undefined -> @@ -182,19 +185,22 @@ connect(KeyOrName, ConnType, ExtraOpts) -> undefined -> {ssh,undefined,AllOpts}; SFTPAddr -> - log(heading(connect,KeyOrName), - "Note: Opening ssh connection to sftp host.\n", + try_log(heading(connect,KeyOrName), + "Note: Opening ssh connection " + "to sftp host.\n", []), {ssh,SFTPAddr, - [{ssh,SFTPAddr}|proplists:delete(sftp, AllOpts)]} + [{ssh,SFTPAddr} | + proplists:delete(sftp, AllOpts)]} end; undefined when ConnType == sftp -> case proplists:get_value(ssh, AllOpts) of undefined -> {sftp,undefined,AllOpts}; SSHAddr -> - log(heading(connect,KeyOrName), - "Note: Opening sftp connection to ssh host.\n", + try_log(heading(connect,KeyOrName), + "Note: Opening sftp connection " + "to ssh host.\n", []), {sftp,SSHAddr, [{sftp,SSHAddr}|proplists:delete(ssh, AllOpts)]} @@ -209,15 +215,15 @@ connect(KeyOrName, ConnType, ExtraOpts) -> [{not_available,{KeyOrName,ConnType1}}]), {error,{not_available,{KeyOrName,ConnType1}}}; {_,undefined} -> - log(heading(connect,KeyOrName), - "Opening ~w connection to ~p:22\n", - [ConnType1,Addr]), + try_log(heading(connect,KeyOrName), + "Opening ~w connection to ~p:22\n", + [ConnType1,Addr]), ct_gen_conn:start(KeyOrName, {ConnType1,Addr,22}, AllOpts1, ?MODULE); {_,Port} -> - log(heading(connect,KeyOrName), - "Opening ~w connection to ~p:~w\n", - [ConnType1,Addr,Port]), + try_log(heading(connect,KeyOrName), + "Opening ~w connection to ~p:~w\n", + [ConnType1,Addr,Port]), ct_gen_conn:start(KeyOrName, {ConnType1,Addr,Port}, AllOpts1, ?MODULE) end @@ -232,7 +238,7 @@ connect(KeyOrName, ConnType, ExtraOpts) -> disconnect(SSH) -> case get_handle(SSH) of {ok,Pid} -> - log(heading(disconnect,SSH), "Handle: ~p", [Pid]), + try_log(heading(disconnect,SSH), "Handle: ~p", [Pid], 5000), case ct_gen_conn:stop(Pid) of {error,{process_down,Pid,noproc}} -> {error,already_closed}; @@ -968,8 +974,9 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) -> Error; Ok -> SSHRef = element(2, Ok), - log(heading(init,KeyOrName), - "Opened ~w connection:\nHost: ~p (~p)\nUser: ~p\nPassword: ~p\n", + try_log(heading(init,KeyOrName), + "Opened ~w connection:\n" + "Host: ~p (~p)\nUser: ~p\nPassword: ~p\n", [ConnType,Addr,Port,User,lists:duplicate(length(Password),$*)]), {ok,SSHRef,#state{ssh_ref=SSHRef, conn_type=ConnType, target=KeyOrName}} @@ -978,25 +985,26 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) -> %% @hidden handle_msg(sftp_connect, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(sftp_connect,Target), "SSH Ref: ~p", [SSHRef]), + try_log(heading(sftp_connect,Target), "SSH Ref: ~p", [SSHRef]), {ssh_sftp:start_channel(SSHRef),State}; handle_msg({session_open,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(session_open,Target), "SSH Ref: ~p, Timeout: ~p", [SSHRef,TO]), + try_log(heading(session_open,Target), "SSH Ref: ~p, Timeout: ~p", + [SSHRef,TO]), {ssh_connection:session_channel(SSHRef, TO),State}; handle_msg({session_close,Chn}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(session_close,Target), "SSH Ref: ~p, Chn: ~p", [SSHRef,Chn]), + try_log(heading(session_close,Target), "SSH Ref: ~p, Chn: ~p", [SSHRef,Chn]), {ssh_connection:close(SSHRef, Chn),State}; handle_msg({exec,Chn,Command,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, Chn1 = if Chn == undefined -> - log(heading(exec,Target), - "Opening channel for exec, SSH Ref: ~p", [SSHRef]), + try_log(heading(exec,Target), + "Opening channel for exec, SSH Ref: ~p", [SSHRef]), case ssh_connection:session_channel(SSHRef, TO) of {ok,C} -> C; CErr -> CErr @@ -1009,9 +1017,9 @@ handle_msg({exec,Chn,Command,TO}, State) -> log(heading(exec,Target), "Opening channel failed: ~p", [ChnError]), {ChnError,State}; _ -> - log(heading(exec,Target), - "SSH Ref: ~p, Chn: ~p, Command: ~p, Timeout: ~p", - [SSHRef,Chn1,Command,TO]), + try_log(heading(exec,Target), + "SSH Ref: ~p, Chn: ~p, Command: ~p, Timeout: ~p", + [SSHRef,Chn1,Command,TO]), case ssh_connection:exec(SSHRef, Chn1, Command, TO) of success -> Result = do_recv_response(SSHRef, Chn1, [], close, TO), @@ -1024,24 +1032,24 @@ handle_msg({exec,Chn,Command,TO}, State) -> handle_msg({receive_response,Chn,End,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(receive_response,Target), - "SSH Ref: ~p, Chn: ~p, Timeout: ~p", [SSHRef,Chn,TO]), + try_log(heading(receive_response,Target), + "SSH Ref: ~p, Chn: ~p, Timeout: ~p", [SSHRef,Chn,TO]), Result = do_recv_response(SSHRef, Chn, [], End, TO), {Result,State}; handle_msg({send,Chn,Type,Data,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(send,Target), - "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n" - "Data: ~p", [SSHRef,Chn,Type,TO,Data]), + try_log(heading(send,Target), + "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n" + "Data: ~p", [SSHRef,Chn,Type,TO,Data]), Result = ssh_connection:send(SSHRef, Chn, Type, Data, TO), {Result,State}; handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(send_and_receive,Target), - "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n" - "Data: ~p", [SSHRef,Chn,Type,TO,Data]), + try_log(heading(send_and_receive,Target), + "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n" + "Data: ~p", [SSHRef,Chn,Type,TO,Data]), case ssh_connection:send(SSHRef, Chn, Type, Data, TO) of ok -> Result = do_recv_response(SSHRef, Chn, [], End, TO), @@ -1052,137 +1060,162 @@ handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) -> handle_msg({subsystem,Chn,Subsystem,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(subsystem,Target), - "SSH Ref: ~p, Chn: ~p, Subsys: ~p, Timeout: ~p", - [SSHRef,Chn,Subsystem,TO]), + try_log(heading(subsystem,Target), + "SSH Ref: ~p, Chn: ~p, Subsys: ~p, Timeout: ~p", + [SSHRef,Chn,Subsystem,TO]), Result = ssh_connection:subsystem(SSHRef, Chn, Subsystem, TO), {Result,State}; %% --- SFTP Commands --- handle_msg({read_file,Srv,File}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:read_file(ref(Srv,SSHRef), File),S}; handle_msg({write_file,Srv,File,Iolist}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:write_file(ref(Srv,SSHRef), File, Iolist),S}; handle_msg({list_dir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:list_dir(ref(Srv,SSHRef), Path),S}; handle_msg({open,Srv,File,Mode}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:open(ref(Srv,SSHRef), File, Mode),S}; handle_msg({opendir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:opendir(ref(Srv,SSHRef), Path),S}; handle_msg({close,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:close(ref(Srv,SSHRef), Handle),S}; handle_msg({read,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:read(ref(Srv,SSHRef), Handle, Len),S}; handle_msg({pread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:pread(ref(Srv,SSHRef),Handle,Position,Length),S}; handle_msg({aread,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:aread(ref(Srv,SSHRef), Handle, Len),S}; handle_msg({apread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:apread(ref(Srv,SSHRef), Handle, Position, Length),S}; handle_msg({write,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:write(ref(Srv,SSHRef), Handle, Data),S}; handle_msg({pwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:pwrite(ref(Srv,SSHRef), Handle, Position, Data),S}; handle_msg({awrite,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:awrite(ref(Srv,SSHRef), Handle, Data),S}; handle_msg({apwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:apwrite(ref(Srv,SSHRef), Handle, Position, Data),S}; handle_msg({position,Srv,Handle,Location}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:position(ref(Srv,SSHRef), Handle, Location),S}; handle_msg({read_file_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:read_file_info(ref(Srv,SSHRef), Name),S}; handle_msg({get_file_info,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:get_file_info(ref(Srv,SSHRef), Handle),S}; handle_msg({read_link_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:read_link_info(ref(Srv,SSHRef), Name),S}; handle_msg({write_file_info,Srv,Name,Info}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:write_file_info(ref(Srv,SSHRef), Name, Info),S}; handle_msg({read_link,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:read_link(ref(Srv,SSHRef), Name),S}; handle_msg({make_symlink,Srv,Name,Target}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:make_symlink(ref(Srv,SSHRef), Name, Target),S}; handle_msg({rename,Srv,OldName,NewName}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:rename(ref(Srv,SSHRef), OldName, NewName),S}; handle_msg({delete,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:delete(ref(Srv,SSHRef), Name),S}; handle_msg({make_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:make_dir(ref(Srv,SSHRef), Name),S}; handle_msg({del_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:del_dir(ref(Srv,SSHRef), Name),S}. %% @hidden @@ -1197,12 +1230,12 @@ close(SSHRef) -> terminate(SSHRef, State) -> case State#state.conn_type of ssh -> - log(heading(disconnect_ssh,State#state.target), - "SSH Ref: ~p",[SSHRef]), + try_log(heading(disconnect_ssh,State#state.target), + "SSH Ref: ~p",[SSHRef], 5000), ssh:close(SSHRef); sftp -> - log(heading(disconnect_sftp,State#state.target), - "SFTP Ref: ~p",[SSHRef]), + try_log(heading(disconnect_sftp,State#state.target), + "SFTP Ref: ~p",[SSHRef], 5000), ssh_sftp:stop_channel(SSHRef) end. @@ -1217,7 +1250,7 @@ do_recv_response(SSH, Chn, Data, End, Timeout) -> {ssh_cm, SSH, {open,Chn,RemoteChn,{session}}} -> debug("RECVD open"), {ok,{open,Chn,RemoteChn,{session}}}; - + {ssh_cm, SSH, {closed,Chn}} -> ssh_connection:close(SSH, Chn), debug("CLSD~n~p ~p", [SSH,Chn]), @@ -1245,38 +1278,38 @@ do_recv_response(SSH, Chn, Data, End, Timeout) -> {ssh_cm, SSH, {exit_signal,Chn,Signal,Err,_Lang}} -> debug("RECVD exit_signal~n~p ~p~n~p ~p", [SSH,Chn,Signal,Err]), do_recv_response(SSH, Chn, Data, End, Timeout); -%% {ok,{exit_signal,Chn,Signal,Err,_Lang}}; + %% {ok,{exit_signal,Chn,Signal,Err,_Lang}}; {ssh_cm, SSH, {exit_status,Chn,Status}} -> debug("RECVD exit_status~n~p ~p~n~p", [SSH,Chn,Status]), do_recv_response(SSH, Chn, Data, End, Timeout); -%% {ok,{exit_status,Chn,_Status}}; + %% {ok,{exit_status,Chn,_Status}}; -%% --- INTERACTIVE MESSAGES - NOT HANDLED --- -%% -%% {ssh_cm, SSH, {subsystem,Chn,WantReply,Name}} -> -%% debug("RECVD SUBS WNTRPLY~n~p ~p~n~p~n~p", -%% [SSH,Chn,WantReply]), -%% ssh_connection:reply_request(SSH, WantReply, success, Chn), -%% do_recv_response(SSH, Chn, Data, End, Timeout); - -%% {ssh_cm, SSH, {shell,WantReply}} -> -%% debug("RECVD SHELL WNTRPLY~n~p ~p~n~p~n~p", -%% [SSH,Chn,WantReply]), -%% ssh_connection:reply_request(SSH, WantReply, success, Chn), -%% do_recv_response(SSH,Chn,Data,End,Timeout); - -%% {ssh_cm, SSH, {pty,Chn,WantReply,Pty}} -> -%% debug("RECVD PTY WNTRPLY~n~p ~p~n~p~n~p", -%% [SSH,Chn,WantReply,Pty]), -%% ssh_connection:reply_request(SSH, WantReply, success, Chn), -%% do_recv_response(SSH, Chn, Data, End, Timeout); - -%% {ssh_cm, SSH, WCh={window_change,_Chn,_Width,_Height,_PixWidth,_PixHeight}} -> -%% debug("RECVD WINCH"), -%% {ok,WCh}; - + %% --- INTERACTIVE MESSAGES - NOT HANDLED --- + %% + %% {ssh_cm, SSH, {subsystem,Chn,WantReply,Name}} -> + %% debug("RECVD SUBS WNTRPLY~n~p ~p~n~p~n~p", + %% [SSH,Chn,WantReply]), + %% ssh_connection:reply_request(SSH, WantReply, success, Chn), + %% do_recv_response(SSH, Chn, Data, End, Timeout); + + %% {ssh_cm, SSH, {shell,WantReply}} -> + %% debug("RECVD SHELL WNTRPLY~n~p ~p~n~p~n~p", + %% [SSH,Chn,WantReply]), + %% ssh_connection:reply_request(SSH, WantReply, success, Chn), + %% do_recv_response(SSH,Chn,Data,End,Timeout); + + %% {ssh_cm, SSH, {pty,Chn,WantReply,Pty}} -> + %% debug("RECVD PTY WNTRPLY~n~p ~p~n~p~n~p", + %% [SSH,Chn,WantReply,Pty]), + %% ssh_connection:reply_request(SSH, WantReply, success, Chn), + %% do_recv_response(SSH, Chn, Data, End, Timeout); + + %% {ssh_cm, SSH, WCh={window_change,_Chn,_Width,_Height,_PixWidth,_PixHeight}} -> + %% debug("RECVD WINCH"), + %% {ok,WCh}; + Other -> debug("UNEXPECTED MESSAGE~n~p ~p~n~p", [SSH,Chn,Other]), do_recv_response(SSH, Chn, Data, End, Timeout) @@ -1307,9 +1340,12 @@ get_handle(SSH) -> %%%----------------------------------------------------------------- %%% call(SSH, Msg) -> + call(SSH, Msg, infinity). + +call(SSH, Msg, Timeout) -> case get_handle(SSH) of {ok,Pid} -> - ct_gen_conn:call(Pid, Msg); + ct_gen_conn:call(Pid, Msg, Timeout); Error -> Error end. @@ -1318,13 +1354,13 @@ call(SSH, Msg) -> %%% ref(sftp, SSHRef) -> SSHRef; ref(Server, _) -> Server. - + %%%----------------------------------------------------------------- %%% mod(Cmd) -> [Op,_Server|Args] = tuple_to_list(Cmd), list_to_tuple([Op|Args]). - + %%%----------------------------------------------------------------- %%% heading(Function, Ref) -> @@ -1335,6 +1371,20 @@ heading(Function, Ref) -> log(Heading, Str, Args) -> ct_gen_conn:log(Heading, Str, Args). +%%%----------------------------------------------------------------- +%%% +try_log(Heading, Str, Args) -> + try_log(Heading, Str, Args, infinity). + +try_log(Heading, Str, Args, Timeout) -> + case ct_util:is_silenced(ssh, Timeout) of + true -> + ok; + false -> + ct_gen_conn:log(Heading, Str, Args); + _Error -> + ok + end. %%%----------------------------------------------------------------- %%% @@ -1342,5 +1392,5 @@ debug(Str) -> debug(Str, []). debug(_Str, _Args) -> -%% io:format("~n--- ct_ssh debug ---~n" ++ _Str ++ "~n", _Args), + %% io:format("~n--- ct_ssh debug ---~n" ++ _Str ++ "~n", _Args), ok. diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl index f4a551e3ff..e37a657617 100644 --- a/lib/common_test/src/ct_telnet.erl +++ b/lib/common_test/src/ct_telnet.erl @@ -155,6 +155,8 @@ open(KeyOrName,ConnType,TargetMod) -> %%% <p><code>TargetMod</code> is a module which exports the functions %%% <code>connect(Ip,Port,KeepAlive,Extra)</code> and <code>get_prompt_regexp()</code> %%% for the given <code>TargetType</code> (e.g. <code>unix_telnet</code>).</p> +%%% +%%% @see ct:require/2 open(KeyOrName,ConnType,TargetMod,Extra) -> case ct:get_config({KeyOrName,ConnType}) of undefined -> diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl index 4c05f57520..a8b67d0329 100644 --- a/lib/common_test/src/ct_testspec.erl +++ b/lib/common_test/src/ct_testspec.erl @@ -29,6 +29,8 @@ -include("ct_util.hrl"). +-define(testspec_fields, record_info(fields, testspec)). + %%%------------------------------------------------------------------ %%% NOTE: %%% Multiple testspecs may be used as input with the result that @@ -46,7 +48,8 @@ %%% Version 1 - extract and return all tests and skips for Node %%% (incl all_nodes) %%%------------------------------------------------------------------- -prepare_tests(TestSpec,Node) when is_record(TestSpec,testspec), is_atom(Node) -> +prepare_tests(TestSpec,Node) when is_record(TestSpec,testspec), + is_atom(Node) -> case lists:keysearch(Node,1,prepare_tests(TestSpec)) of {value,{Node,Run,Skip}} -> {Run,Skip}; @@ -249,22 +252,23 @@ collect_tests_from_file1([Spec|Specs],TestSpec,Relaxed) -> SpecDir = filename:dirname(filename:absname(Spec)), case file:consult(Spec) of {ok,Terms} -> - TestSpec1 = collect_tests(Terms, - TestSpec#testspec{spec_dir=SpecDir}, - Relaxed), - collect_tests_from_file1(Specs,TestSpec1,Relaxed); + case collect_tests(Terms, + TestSpec#testspec{spec_dir=SpecDir}, + Relaxed) of + TS = #testspec{tests=Tests, logdir=LogDirs} when Specs == [] -> + LogDirs1 = lists:delete(".",LogDirs) ++ ["."], + TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1}; + TS = #testspec{alias = As, nodes = Ns} -> + TS1 = TS#testspec{alias = lists:reverse(As), + nodes = lists:reverse(Ns)}, + collect_tests_from_file1(Specs,TS1,Relaxed) + end; {error,Reason} -> ReasonStr = lists:flatten(io_lib:format("~s", [file:format_error(Reason)])), throw({error,{Spec,ReasonStr}}) - end; -collect_tests_from_file1([],TS=#testspec{config=Cfgs,event_handler=EvHs, - include=Incl,tests=Tests},_) -> - TS#testspec{config=lists:reverse(Cfgs), - event_handler=lists:reverse(EvHs), - include=lists:reverse(Incl), - tests=lists:flatten(Tests)}. + end. collect_tests_from_list(Terms,Relaxed) -> collect_tests_from_list(Terms,[node()],Relaxed). @@ -278,30 +282,163 @@ collect_tests_from_list(Terms,Nodes,Relaxed) when is_list(Nodes) -> E = {error,_} -> E; TS -> - #testspec{config=Cfgs,event_handler=EvHs,include=Incl,tests=Tests} = TS, - TS#testspec{config=lists:reverse(Cfgs), - event_handler=lists:reverse(EvHs), - include=lists:reverse(Incl), - tests=lists:flatten(Tests)} + #testspec{tests=Tests, logdir=LogDirs} = TS, + LogDirs1 = lists:delete(".",LogDirs) ++ ["."], + TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1} end. collect_tests(Terms,TestSpec,Relaxed) -> put(relaxed,Relaxed), - TestSpec1 = get_global(Terms,TestSpec), - TestSpec2 = get_all_nodes(Terms,TestSpec1), - {Terms2, TestSpec3} = filter_init_terms(Terms, [], TestSpec2), + Terms1 = replace_names(Terms), + TestSpec1 = get_global(Terms1,TestSpec), + TestSpec2 = get_all_nodes(Terms1,TestSpec1), + {Terms2, TestSpec3} = filter_init_terms(Terms1, [], TestSpec2), add_tests(Terms2,TestSpec3). -get_global([{merge_tests, Bool} | Ts], Spec) -> - get_global(Ts,Spec#testspec{ merge_tests = Bool }); +%% replace names (atoms) in the testspec matching those in 'define' terms by +%% searching recursively through tuples and lists +replace_names(Terms) -> + Defs = + lists:flatmap(fun(Def={define,Name,_Replacement}) -> + %% check that name follows convention + if not is_atom(Name) -> + throw({illegal_name_in_testspec,Name}); + true -> + [First|_] = atom_to_list(Name), + if ((First == $?) or (First == $$) + or (First == $_) + or ((First >= $A) + and (First =< $Z))) -> + [Def]; + true -> + throw({illegal_name_in_testspec, + Name}) + end + end; + (_) -> [] + end, Terms), + DefProps = replace_names_in_defs(Defs,[]), + replace_names(Terms,[],DefProps). + +replace_names_in_defs([Def|Left],ModDefs) -> + [{define,Name,Replacement}] = replace_names([Def],[],ModDefs), + replace_names_in_defs(Left,[{Name,Replacement}|ModDefs]); +replace_names_in_defs([],ModDefs) -> + ModDefs. + +replace_names([Term|Ts],Modified,Defs) when is_tuple(Term) -> + [TypeTag|Data] = tuple_to_list(Term), + Term1 = list_to_tuple([TypeTag|replace_names_in_elems(Data,[],Defs)]), + replace_names(Ts,[Term1|Modified],Defs); +replace_names([Term|Ts],Modified,Defs) when is_atom(Term) -> + case proplists:get_value(Term,Defs) of + undefined -> + replace_names(Ts,[Term|Modified],Defs); + Replacement -> + replace_names(Ts,[Replacement|Modified],Defs) + end; +replace_names([Term=[Ch|_]|Ts],Modified,Defs) when is_integer(Ch) -> + %% Term *could* be a string, attempt to search through it + Term1 = replace_names_in_string(Term,Defs), + replace_names(Ts,[Term1|Modified],Defs); +replace_names([Term|Ts],Modified,Defs) -> + replace_names(Ts,[Term|Modified],Defs); +replace_names([],Modified,_Defs) -> + lists:reverse(Modified). + +replace_names_in_elems([Elem|Es],Modified,Defs) when is_tuple(Elem) -> + Elem1 = list_to_tuple(replace_names_in_elems(tuple_to_list(Elem),[],Defs)), + replace_names_in_elems(Es,[Elem1|Modified],Defs); +replace_names_in_elems([Elem|Es],Modified,Defs) when is_atom(Elem) -> + case proplists:get_value(Elem,Defs) of + undefined -> + %% if Term is a node name, check it for replacements as well + Elem1 = replace_names_in_node(Elem,Defs), + replace_names_in_elems(Es,[Elem1|Modified],Defs); + Replacement -> + replace_names_in_elems(Es,[Replacement|Modified],Defs) + end; +replace_names_in_elems([Elem=[Ch|_]|Es],Modified,Defs) when is_integer(Ch) -> + %% Term *could* be a string, attempt to search through it + case replace_names_in_string(Elem,Defs) of + Elem -> + List = replace_names_in_elems(Elem,[],Defs), + replace_names_in_elems(Es,[List|Modified],Defs); + Elem1 -> + replace_names_in_elems(Es,[Elem1|Modified],Defs) + end; +replace_names_in_elems([Elem|Es],Modified,Defs) when is_list(Elem) -> + List = replace_names_in_elems(Elem,[],Defs), + replace_names_in_elems(Es,[List|Modified],Defs); +replace_names_in_elems([Elem|Es],Modified,Defs) -> + replace_names_in_elems(Es,[Elem|Modified],Defs); +replace_names_in_elems([],Modified,_Defs) -> + lists:reverse(Modified). + +replace_names_in_string(Term,Defs=[{Name,Replacement=[Ch|_]}|Ds]) + when is_integer(Ch) -> + try re:replace(Term,[$'|atom_to_list(Name)]++"'", + Replacement,[{return,list}]) of + Term -> % no match, proceed + replace_names_in_string(Term,Ds); + Term1 -> + replace_names_in_string(Term1,Defs) + catch + _:_ -> Term % Term is not a string + end; +replace_names_in_string(Term,[_|Ds]) -> + replace_names_in_string(Term,Ds); +replace_names_in_string(Term,[]) -> + Term. + +replace_names_in_node(Node,Defs) -> + String = atom_to_list(Node), + case lists:member($@,String) of + true -> + list_to_atom(replace_names_in_node1(String,Defs)); + false -> + Node + end. + +replace_names_in_node1(NodeStr,Defs=[{Name,Replacement}|Ds]) -> + ReplStr = case Replacement of + [Ch|_] when is_integer(Ch) -> Replacement; + _ when is_atom(Replacement) -> atom_to_list(Replacement); + _ -> false + end, + if ReplStr == false -> + replace_names_in_node1(NodeStr,Ds); + true -> + case re:replace(NodeStr,atom_to_list(Name), + ReplStr,[{return,list}]) of + NodeStr -> % no match, proceed + replace_names_in_node1(NodeStr,Ds); + NodeStr1 -> + replace_names_in_node1(NodeStr1,Defs) + end + end; +replace_names_in_node1(NodeStr,[]) -> + NodeStr. + + +%% global terms that will be used for analysing all other terms in the spec +get_global([{merge_tests,Bool} | Ts], Spec) -> + get_global(Ts,Spec#testspec{merge_tests=Bool}); + +%% the 'define' term replaces the 'alias' and 'node' terms, but we need to keep +%% the latter two for backwards compatibility... get_global([{alias,Ref,Dir}|Ts],Spec=#testspec{alias=Refs}) -> get_global(Ts,Spec#testspec{alias=[{Ref,get_absdir(Dir,Spec)}|Refs]}); get_global([{node,Ref,Node}|Ts],Spec=#testspec{nodes=Refs}) -> - get_global(Ts,Spec#testspec{nodes=[{Ref,Node}|lists:keydelete(Node,2,Refs)]}); -get_global([_|Ts],Spec) -> get_global(Ts,Spec); -get_global([],Spec) -> Spec. + get_global(Ts,Spec#testspec{nodes=[{Ref,Node} | + lists:keydelete(Node,2,Refs)]}); -get_absfile(Callback, FullName,#testspec{spec_dir=SpecDir}) -> +get_global([_|Ts],Spec) -> + get_global(Ts,Spec); +get_global([],Spec=#testspec{nodes=Ns, alias=As}) -> + Spec#testspec{nodes=lists:reverse(Ns), alias=lists:reverse(As)}. + +get_absfile(Callback,FullName,#testspec{spec_dir=SpecDir}) -> % we need to temporary switch to new cwd here, because % otherwise config files cannot be found {ok, OldWd} = file:get_cwd(), @@ -329,29 +466,45 @@ get_absfile(FullName,#testspec{spec_dir=SpecDir}) -> get_absdir(Dir,#testspec{spec_dir=SpecDir}) -> get_absname(Dir,SpecDir). -get_absname(TestDir,SpecDir) -> - AbsName = filename:absname(TestDir,SpecDir), - TestDirName = filename:basename(AbsName), - Path = filename:dirname(AbsName), - TopDir = filename:basename(Path), - Path1 = - case TopDir of - "." -> - [_|Rev] = lists:reverse(filename:split(Path)), - filename:join(lists:reverse(Rev)); - ".." -> - [_,_|Rev] = lists:reverse(filename:split(Path)), - filename:join(lists:reverse(Rev)); - _ -> - Path - end, - filename:join(Path1,TestDirName). +get_absname(Dir,SpecDir) -> + AbsName = filename:absname(Dir,SpecDir), + shorten_path(AbsName,SpecDir). + +shorten_path(Path,SpecDir) -> + case shorten_split_path(filename:split(Path),[]) of + [] -> + [Root|_] = filename:split(SpecDir), + Root; + Short -> + filename:join(Short) + end. + +shorten_split_path([".."|Path],SoFar) -> + shorten_split_path(Path,tl(SoFar)); +shorten_split_path(["."|Path],SoFar) -> + shorten_split_path(Path,SoFar); +shorten_split_path([Dir|Path],SoFar) -> + shorten_split_path(Path,[Dir|SoFar]); +shorten_split_path([],SoFar) -> + lists:reverse(SoFar). %% go through all tests and register all nodes found get_all_nodes([{suites,Nodes,_,_}|Ts],Spec) when is_list(Nodes) -> get_all_nodes(Ts,save_nodes(Nodes,Spec)); get_all_nodes([{suites,Node,_,_}|Ts],Spec) -> get_all_nodes(Ts,save_nodes([Node],Spec)); +get_all_nodes([{groups,[Char|_],_,_,_}|Ts],Spec) when is_integer(Char) -> + get_all_nodes(Ts,Spec); +get_all_nodes([{groups,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) -> + get_all_nodes(Ts,save_nodes(Nodes,Spec)); +get_all_nodes([{groups,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) -> + get_all_nodes(Ts,save_nodes(Nodes,Spec)); +get_all_nodes([{groups,_,_,_,{cases,_}}|Ts],Spec) -> + get_all_nodes(Ts,Spec); +get_all_nodes([{groups,Node,_,_,_}|Ts],Spec) -> + get_all_nodes(Ts,save_nodes([Node],Spec)); +get_all_nodes([{groups,Node,_,_,_,_}|Ts],Spec) -> + get_all_nodes(Ts,save_nodes([Node],Spec)); get_all_nodes([{cases,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) -> get_all_nodes(Ts,save_nodes(Nodes,Spec)); get_all_nodes([{cases,Node,_,_,_}|Ts],Spec) -> @@ -360,74 +513,93 @@ get_all_nodes([{skip_suites,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) -> get_all_nodes(Ts,save_nodes(Nodes,Spec)); get_all_nodes([{skip_suites,Node,_,_,_}|Ts],Spec) -> get_all_nodes(Ts,save_nodes([Node],Spec)); +get_all_nodes([{skip_groups,[Char|_],_,_,_,_}|Ts],Spec) when is_integer(Char) -> + get_all_nodes(Ts,Spec); +get_all_nodes([{skip_groups,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) -> + get_all_nodes(Ts,save_nodes(Nodes,Spec)); +get_all_nodes([{skip_groups,Node,_,_,_,_}|Ts],Spec) -> + get_all_nodes(Ts,save_nodes([Node],Spec)); +get_all_nodes([{skip_groups,Nodes,_,_,_,_,_}|Ts],Spec) when is_list(Nodes) -> + get_all_nodes(Ts,save_nodes(Nodes,Spec)); +get_all_nodes([{skip_groups,Node,_,_,_,_,_}|Ts],Spec) -> + get_all_nodes(Ts,save_nodes([Node],Spec)); get_all_nodes([{skip_cases,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) -> get_all_nodes(Ts,save_nodes(Nodes,Spec)); get_all_nodes([{skip_cases,Node,_,_,_,_}|Ts],Spec) -> get_all_nodes(Ts,save_nodes([Node],Spec)); -get_all_nodes([_|Ts],Spec) -> +get_all_nodes([_Other|Ts],Spec) -> get_all_nodes(Ts,Spec); get_all_nodes([],Spec) -> Spec. -filter_init_terms([{init, InitOptions}|Ts], NewTerms, Spec)-> - filter_init_terms([{init, list_nodes(Spec), InitOptions}|Ts], NewTerms, Spec); -filter_init_terms([{init, NodeRef, InitOptions}|Ts], NewTerms, Spec) - when is_atom(NodeRef)-> - filter_init_terms([{init, [NodeRef], InitOptions}|Ts], NewTerms, Spec); -filter_init_terms([{init, NodeRefs, InitOption}|Ts], NewTerms, Spec) when is_tuple(InitOption) -> - filter_init_terms([{init, NodeRefs, [InitOption]}|Ts], NewTerms, Spec); -filter_init_terms([{init, [NodeRef|NodeRefs], InitOptions}|Ts], NewTerms, Spec=#testspec{init=InitData})-> - NodeStartOptions = case lists:keyfind(node_start, 1, InitOptions) of - {node_start, NSOptions}-> - case lists:keyfind(callback_module, 1, NSOptions) of - {callback_module, _Callback}-> - NSOptions; - false-> - [{callback_module, ct_slave}|NSOptions] - end; - false-> - [] - end, - EvalTerms = case lists:keyfind(eval, 1, InitOptions) of - {eval, MFA} when is_tuple(MFA)-> - [MFA]; - {eval, MFAs} when is_list(MFAs)-> - MFAs; - false-> - [] - end, +filter_init_terms([{init,InitOptions}|Ts],NewTerms,Spec) -> + filter_init_terms([{init,list_nodes(Spec),InitOptions}|Ts], + NewTerms,Spec); +filter_init_terms([{init,all_nodes,InitOptions}|Ts],NewTerms,Spec) -> + filter_init_terms([{init,list_nodes(Spec),InitOptions}|Ts], + NewTerms,Spec); +filter_init_terms([{init,NodeRef,InitOptions}|Ts], + NewTerms,Spec) when is_atom(NodeRef) -> + filter_init_terms([{init,[NodeRef],InitOptions}|Ts],NewTerms,Spec); +filter_init_terms([{init,NodeRefs,InitOption}|Ts], + NewTerms,Spec) when is_tuple(InitOption) -> + filter_init_terms([{init,NodeRefs,[InitOption]}|Ts],NewTerms,Spec); +filter_init_terms([{init,[NodeRef|NodeRefs],InitOptions}|Ts], + NewTerms,Spec=#testspec{init=InitData}) -> + NodeStartOptions = + case lists:keyfind(node_start,1,InitOptions) of + {node_start,NSOptions}-> + case lists:keyfind(callback_module,1,NSOptions) of + {callback_module,_Callback}-> + NSOptions; + false-> + [{callback_module,ct_slave}|NSOptions] + end; + false-> + [] + end, + EvalTerms = case lists:keyfind(eval,1,InitOptions) of + {eval,MFA} when is_tuple(MFA) -> + [MFA]; + {eval,MFAs} when is_list(MFAs) -> + MFAs; + false-> + [] + end, Node = ref2node(NodeRef,Spec#testspec.nodes), - InitData2 = add_option({node_start, NodeStartOptions}, Node, InitData, true), - InitData3 = add_option({eval, EvalTerms}, Node, InitData2, false), - filter_init_terms([{init, NodeRefs, InitOptions}|Ts], NewTerms, Spec#testspec{init=InitData3}); -filter_init_terms([{init, [], _}|Ts], NewTerms, Spec)-> - filter_init_terms(Ts, NewTerms, Spec); -filter_init_terms([Term|Ts], NewTerms, Spec)-> - filter_init_terms(Ts, [Term|NewTerms], Spec); -filter_init_terms([], NewTerms, Spec)-> - {lists:reverse(NewTerms), Spec}. - -add_option({Key, Value}, Node, List, WarnIfExists) when is_list(Value)-> - OldOptions = case lists:keyfind(Node, 1, List) of - {Node, Options}-> + InitData2 = add_option({node_start,NodeStartOptions},Node,InitData,true), + InitData3 = add_option({eval,EvalTerms},Node,InitData2,false), + filter_init_terms([{init,NodeRefs,InitOptions}|Ts], + NewTerms,Spec#testspec{init=InitData3}); +filter_init_terms([{init,[],_}|Ts],NewTerms,Spec) -> + filter_init_terms(Ts,NewTerms,Spec); +filter_init_terms([Term|Ts],NewTerms,Spec) -> + filter_init_terms(Ts,[Term|NewTerms],Spec); +filter_init_terms([],NewTerms,Spec) -> + {lists:reverse(NewTerms),Spec}. + +add_option({Key,Value},Node,List,WarnIfExists) when is_list(Value) -> + OldOptions = case lists:keyfind(Node,1,List) of + {Node,Options}-> Options; false-> [] end, - NewOption = case lists:keyfind(Key, 1, OldOptions) of - {Key, OldOption} when WarnIfExists, OldOption/=[]-> - io:format("There is an option ~w=~w already defined for node ~p, skipping new ~w~n", - [Key, OldOption, Node, Value]), + NewOption = case lists:keyfind(Key,1,OldOptions) of + {Key,OldOption} when WarnIfExists,OldOption/=[]-> + io:format("There is an option ~w=~w already " + "defined for node ~p, skipping new ~w~n", + [Key,OldOption,Node,Value]), OldOption; - {Key, OldOption}-> + {Key,OldOption}-> OldOption ++ Value; false-> Value end, - lists:keystore(Node, 1, List, - {Node, lists:keystore(Key, 1, OldOptions, {Key, NewOption})}); -add_option({Key, Value}, Node, List, WarnIfExists)-> - add_option({Key, [Value]}, Node, List, WarnIfExists). + lists:keystore(Node,1,List, + {Node,lists:keystore(Key,1,OldOptions,{Key,NewOption})}); +add_option({Key,Value},Node,List,WarnIfExists) -> + add_option({Key,[Value]},Node,List,WarnIfExists). save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) -> NodeRefs1 = @@ -446,267 +618,18 @@ save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) -> end end end,NodeRefs,Nodes), - Spec#testspec{nodes=NodeRefs1}. + Spec#testspec{nodes=NodeRefs1}. list_nodes(#testspec{nodes=NodeRefs}) -> lists:map(fun({_Ref,Node}) -> Node end, NodeRefs). - -%% --------------------------------------------------------- -%% / \ -%% | When adding tests, remember to update valid_terms/0 also! | -%% \ / -%% --------------------------------------------------------- - - -%% Associate a "global" logdir with all nodes -%% except those with specific logdir, e.g: -%% ["/tmp/logdir",{ct1@finwe,"/tmp/logdir2"}] -%% means all nodes should write to /tmp/logdir -%% except ct1@finwe that should use /tmp/logdir2. - -%% --- logdir --- -add_tests([{logdir,all_nodes,Dir}|Ts],Spec) -> - Dirs = Spec#testspec.logdir, - Tests = [{logdir,N,get_absdir(Dir,Spec)} || - N <- list_nodes(Spec), - lists:keymember(ref2node(N,Spec#testspec.nodes), - 1,Dirs) == false], - add_tests(Tests++Ts,Spec); -add_tests([{logdir,Nodes,Dir}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,logdir,[Dir],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{logdir,Node,Dir}|Ts],Spec) -> - Dirs = Spec#testspec.logdir, - Dirs1 = [{ref2node(Node,Spec#testspec.nodes),get_absdir(Dir,Spec)} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Dirs)], - add_tests(Ts,Spec#testspec{logdir=Dirs1}); -add_tests([{logdir,Dir}|Ts],Spec) -> - add_tests([{logdir,all_nodes,Dir}|Ts],Spec); - -%% --- logopts --- -add_tests([{logopts,all_nodes,Opts}|Ts],Spec) -> - LogOpts = Spec#testspec.logopts, - Tests = [{logopts,N,Opts} || - N <- list_nodes(Spec), - lists:keymember(ref2node(N,Spec#testspec.nodes),1, - LogOpts) == false], - add_tests(Tests++Ts,Spec); -add_tests([{logopts,Nodes,Opts}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,logopts,[Opts],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{logopts,Node,Opts}|Ts],Spec) -> - LogOpts = Spec#testspec.logopts, - LogOpts1 = [{ref2node(Node,Spec#testspec.nodes),Opts} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes), - 1,LogOpts)], - add_tests(Ts,Spec#testspec{logopts=LogOpts1}); -add_tests([{logopts,Opts}|Ts],Spec) -> - add_tests([{logopts,all_nodes,Opts}|Ts],Spec); - -%% --- label --- -add_tests([{label,all_nodes,Lbl}|Ts],Spec) -> - Labels = Spec#testspec.label, - Tests = [{label,N,Lbl} || N <- list_nodes(Spec), - lists:keymember(ref2node(N,Spec#testspec.nodes), - 1,Labels) == false], - add_tests(Tests++Ts,Spec); -add_tests([{label,Nodes,Lbl}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,label,[Lbl],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{label,Node,Lbl}|Ts],Spec) -> - Labels = Spec#testspec.label, - Labels1 = [{ref2node(Node,Spec#testspec.nodes),Lbl} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Labels)], - add_tests(Ts,Spec#testspec{label=Labels1}); -add_tests([{label,Lbl}|Ts],Spec) -> - add_tests([{label,all_nodes,Lbl}|Ts],Spec); - -%% --- cover --- -add_tests([{cover,all_nodes,File}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {cover,N,File} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{cover,Nodes,File}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,cover,[File],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{cover,Node,File}|Ts],Spec) -> - CoverFs = Spec#testspec.cover, - CoverFs1 = [{ref2node(Node,Spec#testspec.nodes),get_absfile(File,Spec)} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,CoverFs)], - add_tests(Ts,Spec#testspec{cover=CoverFs1}); -add_tests([{cover,File}|Ts],Spec) -> - add_tests([{cover,all_nodes,File}|Ts],Spec); - -%% --- multiply_timetraps --- -add_tests([{multiply_timetraps,all_nodes,MT}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {multiply_timetraps,N,MT} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{multiply_timetraps,Nodes,MT}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,multiply_timetraps,[MT],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{multiply_timetraps,Node,MT}|Ts],Spec) -> - MTs = Spec#testspec.multiply_timetraps, - MTs1 = [{ref2node(Node,Spec#testspec.nodes),MT} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,MTs)], - add_tests(Ts,Spec#testspec{multiply_timetraps=MTs1}); -add_tests([{multiply_timetraps,MT}|Ts],Spec) -> - add_tests([{multiply_timetraps,all_nodes,MT}|Ts],Spec); - -%% --- scale_timetraps --- -add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {scale_timetraps,N,ST} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{scale_timetraps,Nodes,ST}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,scale_timetraps,[ST],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{scale_timetraps,Node,ST}|Ts],Spec) -> - STs = Spec#testspec.scale_timetraps, - STs1 = [{ref2node(Node,Spec#testspec.nodes),ST} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,STs)], - add_tests(Ts,Spec#testspec{scale_timetraps=STs1}); -add_tests([{scale_timetraps,ST}|Ts],Spec) -> - add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec); - -%% --- create_priv_dir --- -add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {create_priv_dir,N,PD} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{create_priv_dir,Nodes,PD}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,create_priv_dir,[PD],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{create_priv_dir,Node,PD}|Ts],Spec) -> - PDs = Spec#testspec.create_priv_dir, - PDs1 = [{ref2node(Node,Spec#testspec.nodes),PD} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,PDs)], - add_tests(Ts,Spec#testspec{create_priv_dir=PDs1}); -add_tests([{create_priv_dir,PD}|Ts],Spec) -> - add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec); - -%% --- config --- -add_tests([{config,all_nodes,Files}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {config,N,Files} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{config,Nodes,Files}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,config,[Files],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{config,Node,[F|Fs]}|Ts],Spec) when is_list(F) -> - Cfgs = Spec#testspec.config, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests([{config,Node,Fs}|Ts], - Spec#testspec{config=[{Node1,get_absfile(F,Spec)}|Cfgs]}); -add_tests([{config,_Node,[]}|Ts],Spec) -> - add_tests(Ts,Spec); -add_tests([{config,Node,F}|Ts],Spec) -> - add_tests([{config,Node,[F]}|Ts],Spec); -add_tests([{config,Files}|Ts],Spec) -> - add_tests([{config,all_nodes,Files}|Ts],Spec); - - -%% --- userconfig --- -add_tests([{userconfig,all_nodes,CBF}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {userconfig,N,CBF} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{userconfig,Nodes,CBF}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,userconfig,[CBF],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{userconfig,Node,[{Callback, Config}|CBF]}|Ts],Spec) -> - Cfgs = Spec#testspec.userconfig, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests([{userconfig,Node,CBF}|Ts], - Spec#testspec{userconfig=[{Node1,{Callback, - get_absfile(Callback, Config ,Spec)}}|Cfgs]}); -add_tests([{userconfig,_Node,[]}|Ts],Spec) -> - add_tests(Ts,Spec); -add_tests([{userconfig,Node,CBF}|Ts],Spec) -> - add_tests([{userconfig,Node,[CBF]}|Ts],Spec); -add_tests([{userconfig,CBF}|Ts],Spec) -> - add_tests([{userconfig,all_nodes,CBF}|Ts],Spec); - -%% --- event_handler --- -add_tests([{event_handler,all_nodes,Hs}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {event_handler,N,Hs,[]} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{event_handler,all_nodes,Hs,Args}|Ts],Spec) when is_list(Args) -> - Tests = lists:map(fun(N) -> {event_handler,N,Hs,Args} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{event_handler,Hs}|Ts],Spec) -> - add_tests([{event_handler,all_nodes,Hs,[]}|Ts],Spec); -add_tests([{event_handler,HsOrNodes,HsOrArgs}|Ts],Spec) -> - case is_noderef(HsOrNodes,Spec#testspec.nodes) of - true -> % HsOrNodes == Nodes, HsOrArgs == Hs - case {HsOrNodes,HsOrArgs} of - {Nodes,Hs} when is_list(Nodes) -> - Ts1 = separate(Nodes,event_handler,[Hs,[]],Ts, - Spec#testspec.nodes), - add_tests(Ts1,Spec); - {_Node,[]} -> - add_tests(Ts,Spec); - {Node,HOrHs} -> - EvHs = Spec#testspec.event_handler, - Node1 = ref2node(Node,Spec#testspec.nodes), - case HOrHs of - [H|Hs] when is_atom(H) -> - add_tests([{event_handler,Node,Hs}|Ts], - Spec#testspec{event_handler=[{Node1,H,[]}|EvHs]}); - H when is_atom(H) -> - add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,[]}|EvHs]}) - end - end; - false -> % HsOrNodes == Hs, HsOrArgs == Args - add_tests([{event_handler,all_nodes,HsOrNodes,HsOrArgs}|Ts],Spec) - end; -add_tests([{event_handler,Nodes,Hs,Args}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,event_handler,[Hs,Args],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{event_handler,Node,[H|Hs],Args}|Ts],Spec) when is_atom(H) -> - EvHs = Spec#testspec.event_handler, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests([{event_handler,Node,Hs,Args}|Ts], - Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]}); -add_tests([{event_handler,_Node,[],_Args}|Ts],Spec) -> - add_tests(Ts,Spec); -add_tests([{event_handler,Node,H,Args}|Ts],Spec) when is_atom(H) -> - EvHs = Spec#testspec.event_handler, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]}); - -%% --- ct_hooks -- -add_tests([{ct_hooks, all_nodes, Hooks} | Ts], Spec) -> - Tests = [{ct_hooks,N,Hooks} || N <- list_nodes(Spec)], - add_tests(Tests ++ Ts, Spec); -add_tests([{ct_hooks, Node, [Hook|Hooks]}|Ts], Spec) -> - SuiteCbs = Spec#testspec.ct_hooks, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests([{ct_hooks, Node, Hooks} | Ts], - Spec#testspec{ct_hooks = [{Node1,Hook} | SuiteCbs]}); -add_tests([{ct_hooks, _Node, []}|Ts], Spec) -> - add_tests(Ts, Spec); -add_tests([{ct_hooks, Hooks}|Ts], Spec) -> - add_tests([{ct_hooks, all_nodes, Hooks}|Ts], Spec); - -%% -- enable_builtin_hooks -- -add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) -> - add_tests(Ts, Spec#testspec{ enable_builtin_hooks = Bool }); - -%% --- include --- -add_tests([{include,all_nodes,InclDirs}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {include,N,InclDirs} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{include,Nodes,InclDirs}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,include,[InclDirs],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{include,Node,[D|Ds]}|Ts],Spec) when is_list(D) -> - Dirs = Spec#testspec.include, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests([{include,Node,Ds}|Ts], - Spec#testspec{include=[{Node1,get_absdir(D,Spec)}|Dirs]}); -add_tests([{include,_Node,[]}|Ts],Spec) -> - add_tests(Ts,Spec); -add_tests([{include,Node,D}|Ts],Spec) -> - add_tests([{include,Node,[D]}|Ts],Spec); -add_tests([{include,InclDirs}|Ts],Spec) -> - add_tests([{include,all_nodes,InclDirs}|Ts],Spec); +%% ----------------------------------------------------- +%% / \ +%% | When adding test/config terms, remember to update | +%% | valid_terms/0 also! | +%% \ / +%% ----------------------------------------------------- %% --- suites --- add_tests([{suites,all_nodes,Dir,Ss}|Ts],Spec) -> @@ -719,7 +642,7 @@ add_tests([{suites,Nodes,Dir,Ss}|Ts],Spec) when is_list(Nodes) -> add_tests([{suites,Node,Dir,Ss}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = insert_suites(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Ss,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); @@ -739,20 +662,22 @@ add_tests([{groups,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) -> add_tests([{groups,Nodes,Dir,Suite,Gs}|Ts],Spec) when is_list(Nodes) -> Ts1 = separate(Nodes,groups,[Dir,Suite,Gs],Ts,Spec#testspec.nodes), add_tests(Ts1,Spec); -add_tests([{groups,Nodes,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,groups,[Dir,Suite,Gs,{cases,TCs}],Ts,Spec#testspec.nodes), +add_tests([{groups,Nodes,Dir,Suite,Gs,{cases,TCs}}|Ts], + Spec) when is_list(Nodes) -> + Ts1 = separate(Nodes,groups,[Dir,Suite,Gs,{cases,TCs}],Ts, + Spec#testspec.nodes), add_tests(Ts1,Spec); add_tests([{groups,Node,Dir,Suite,Gs}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = insert_groups(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Gs,all,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); add_tests([{groups,Node,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = insert_groups(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Gs,TCs,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); @@ -768,7 +693,7 @@ add_tests([{cases,Nodes,Dir,Suite,Cs}|Ts],Spec) when is_list(Nodes) -> add_tests([{cases,Node,Dir,Suite,Cs}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = insert_cases(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Cs,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); @@ -783,7 +708,7 @@ add_tests([{skip_suites,Nodes,Dir,Ss,Cmt}|Ts],Spec) when is_list(Nodes) -> add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = skip_suites(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Ss,Cmt,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); @@ -792,7 +717,8 @@ add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) -> add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) -> add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,Cmt}|Ts],Spec); add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) -> - add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec); + add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,{cases,TCs},Cmt}|Ts], + Spec); add_tests([{skip_groups,Dir,Suite,Gs,Cmt}|Ts],Spec) -> add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,Cmt}|Ts],Spec); add_tests([{skip_groups,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) -> @@ -800,20 +726,22 @@ add_tests([{skip_groups,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) -> add_tests([{skip_groups,Nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) when is_list(Nodes) -> Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,Cmt],Ts,Spec#testspec.nodes), add_tests(Ts1,Spec); -add_tests([{skip_groups,Nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,{cases,TCs},Cmt],Ts,Spec#testspec.nodes), +add_tests([{skip_groups,Nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts], + Spec) when is_list(Nodes) -> + Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,{cases,TCs},Cmt],Ts, + Spec#testspec.nodes), add_tests(Ts1,Spec); add_tests([{skip_groups,Node,Dir,Suite,Gs,Cmt}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = skip_groups(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Gs,all,Cmt,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); add_tests([{skip_groups,Node,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = skip_groups(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Gs,TCs,Cmt,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); @@ -829,45 +757,101 @@ add_tests([{skip_cases,Nodes,Dir,Suite,Cs,Cmt}|Ts],Spec) when is_list(Nodes) -> add_tests([{skip_cases,Node,Dir,Suite,Cs,Cmt}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = skip_cases(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Cs,Cmt,Tests,Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); +%% --- various configuration terms --- +add_tests([{config,Nodes,CfgDir,Files}|Ts],Spec) when is_list(Nodes); + Nodes == all_nodes -> + add_tests([{config,Nodes,{CfgDir,Files}}|Ts],Spec); +add_tests([{config,Node,CfgDir,FileOrFiles}|Ts],Spec) -> + add_tests([{config,Node,{CfgDir,FileOrFiles}}|Ts],Spec); +add_tests([{config,CfgDir=[Ch|_],Files}|Ts],Spec) when is_integer(Ch) -> + add_tests([{config,all_nodes,{CfgDir,Files}}|Ts],Spec); + +add_tests([{event_handler,Nodes,Hs,Args}|Ts],Spec) when is_list(Nodes); + Nodes == all_nodes -> + add_tests([{event_handler,Nodes,{Hs,Args}}|Ts],Spec); +add_tests([{event_handler,Node,HOrHs,Args}|Ts],Spec) -> + add_tests([{event_handler,Node,{HOrHs,Args}}|Ts],Spec); + +add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) -> + add_tests(Ts, Spec#testspec{enable_builtin_hooks = Bool}); + +add_tests([{release_shell,Bool}|Ts],Spec) -> + add_tests(Ts, Spec#testspec{release_shell = Bool}); + %% --- handled/errors --- +add_tests([{define,_,_}|Ts],Spec) -> % handled + add_tests(Ts,Spec); + add_tests([{alias,_,_}|Ts],Spec) -> % handled add_tests(Ts,Spec); add_tests([{node,_,_}|Ts],Spec) -> % handled add_tests(Ts,Spec); -add_tests([{merge_tests, _} | Ts], Spec) -> % handled +add_tests([{merge_tests, _} | Ts], Spec) -> % handled add_tests(Ts,Spec); -%% check if it's a CT term that has bad format or if the user seems to -%% have added something of his/her own, which we'll let pass if relaxed -%% mode is enabled. -add_tests([Other|Ts],Spec) when is_tuple(Other) -> - [Name|_] = tuple_to_list(Other), - case lists:keymember(Name,1,valid_terms()) of - true -> % halt - throw({error,{bad_term_in_spec,Other}}); - false -> % ignore - case get(relaxed) of - true -> - %% warn if name resembles a CT term - case resembles_ct_term(Name,size(Other)) of - true -> - io:format("~nSuspicious term, please check:~n" - "~p~n", [Other]); - false -> - ok - end, - add_tests(Ts,Spec); - false -> - throw({error,{undefined_term_in_spec,Other}}) - end +%% -------------------------------------------------- +%% / \ +%% | General add_tests/2 clauses below will work for | +%% | most test spec configuration terms | +%% \ / +%% -------------------------------------------------- + +%% create one test entry per known node and reinsert +add_tests([Term={Tag,all_nodes,Data}|Ts],Spec) -> + case check_term(Term) of + valid -> + Tests = [{Tag,Node,Data} || Node <- list_nodes(Spec), + should_be_added(Tag,Node,Data,Spec)], + add_tests(Tests++Ts,Spec); + invalid -> % ignore term + add_tests(Ts,Spec) end; - +%% create one test entry per node in Nodes and reinsert +add_tests([{Tag,[],Data}|Ts],Spec) -> + add_tests([{Tag,all_nodes,Data}|Ts],Spec); +add_tests([{Tag,String=[Ch|_],Data}|Ts],Spec) when is_integer(Ch) -> + add_tests([{Tag,all_nodes,{String,Data}}|Ts],Spec); +add_tests([{Tag,NodesOrOther,Data}|Ts],Spec) when is_list(NodesOrOther) -> + case lists:all(fun(Test) -> is_node(Test,Spec#testspec.nodes) + end, NodesOrOther) of + true -> + Ts1 = separate(NodesOrOther,Tag,[Data],Ts,Spec#testspec.nodes), + add_tests(Ts1,Spec); + false -> + add_tests([{Tag,all_nodes,{NodesOrOther,Data}}|Ts],Spec) + end; +%% update data for testspec term of type Tag +add_tests([Term={Tag,NodeOrOther,Data}|Ts],Spec) -> + case is_node(NodeOrOther,Spec#testspec.nodes) of + true -> + case check_term(Term) of + valid -> + Node = ref2node(NodeOrOther,Spec#testspec.nodes), + NodeIxData = + update_recorded(Tag,Node,Spec) ++ + handle_data(Tag,Node,Data,Spec), + add_tests(Ts,mod_field(Spec,Tag,NodeIxData)); + invalid -> % ignore term + add_tests(Ts,Spec) + end; + false -> + add_tests([{Tag,all_nodes,{NodeOrOther,Data}}|Ts],Spec) + end; +%% this test should be added for all known nodes +add_tests([Term={Tag,Data}|Ts],Spec) -> + case check_term(Term) of + valid -> + add_tests([{Tag,all_nodes,Data}|Ts],Spec); + invalid -> + add_tests(Ts,Spec) + end; +%% some other data than a tuple add_tests([Other|Ts],Spec) -> case get(relaxed) of true -> @@ -879,6 +863,118 @@ add_tests([Other|Ts],Spec) -> add_tests([],Spec) -> % done Spec. +%% check if it's a CT term that has bad format or if the user seems to +%% have added something of his/her own, which we'll let pass if relaxed +%% mode is enabled. +check_term(Term) -> + Size = size(Term), + [Name|_] = tuple_to_list(Term), + Valid = valid_terms(), + case lists:member({Name,Size},Valid) of + true -> + valid; + false -> + case lists:keymember(Name,1,Valid) of + true -> % halt + throw({error,{bad_term_in_spec,Term}}); + false -> % ignore + case get(relaxed) of + true -> + %% warn if name resembles a CT term + case resembles_ct_term(Name,size(Term)) of + true -> + io:format("~nSuspicious term, " + "please check:~n" + "~p~n", [Term]), + invalid; + false -> + invalid + end; + false -> + throw({error,{undefined_term_in_spec,Term}}) + end + end + end. + +%% specific data handling before saving in testspec record, e.g. +%% converting relative paths to absolute for directories and files +%% (introduce a clause *only* if the data value needs processing) +handle_data(logdir,Node,Dir,Spec) -> + [{Node,ref2dir(Dir,Spec)}]; +handle_data(cover,Node,File,Spec) -> + [{Node,get_absfile(File,Spec)}]; +handle_data(include,Node,Dirs=[D|_],Spec) when is_list(D) -> + [{Node,ref2dir(Dir,Spec)} || Dir <- Dirs]; +handle_data(include,Node,Dir=[Ch|_],Spec) when is_integer(Ch) -> + handle_data(include,Node,[Dir],Spec); +handle_data(config,Node,File=[Ch|_],Spec) when is_integer(Ch) -> + handle_data(config,Node,[File],Spec); +handle_data(config,Node,{CfgDir,File=[Ch|_]},Spec) when is_integer(Ch) -> + handle_data(config,Node,{CfgDir,[File]},Spec); +handle_data(config,Node,Files=[F|_],Spec) when is_list(F) -> + [{Node,get_absfile(File,Spec)} || File <- Files]; +handle_data(config,Node,{CfgDir,Files=[F|_]},Spec) when is_list(F) -> + [{Node,filename:join(ref2dir(CfgDir,Spec),File)} || File <- Files]; +handle_data(userconfig,Node,CBs,Spec) when is_list(CBs) -> + [{Node,{Callback,get_absfile(Callback,Config,Spec)}} || + {Callback,Config} <- CBs]; +handle_data(userconfig,Node,CB,Spec) when is_tuple(CB) -> + handle_data(userconfig,Node,[CB],Spec); +handle_data(event_handler,Node,H,Spec) when is_atom(H) -> + handle_data(event_handler,Node,{[H],[]},Spec); +handle_data(event_handler,Node,{H,Args},Spec) when is_atom(H) -> + handle_data(event_handler,Node,{[H],Args},Spec); +handle_data(event_handler,Node,Hs,_Spec) when is_list(Hs) -> + [{Node,EvH,[]} || EvH <- Hs]; +handle_data(event_handler,Node,{Hs,Args},_Spec) when is_list(Hs) -> + [{Node,EvH,Args} || EvH <- Hs]; +handle_data(ct_hooks,Node,Hooks,_Spec) when is_list(Hooks) -> + [{Node,Hook} || Hook <- Hooks ]; +handle_data(ct_hooks,Node,Hook,_Spec) -> + [{Node,Hook}]; +handle_data(stylesheet,Node,CSSFile,Spec) -> + [{Node,get_absfile(CSSFile,Spec)}]; +handle_data(verbosity,Node,VLvls,_Spec) when is_integer(VLvls) -> + [{Node,[{'$unspecified',VLvls}]}]; +handle_data(verbosity,Node,VLvls,_Spec) when is_list(VLvls) -> + VLvls1 = lists:map(fun(VLvl = {_Cat,_Lvl}) -> VLvl; + (Lvl) -> {'$unspecified',Lvl} end, VLvls), + [{Node,VLvls1}]; +handle_data(silent_connections,Node,all,_Spec) -> + [{Node,[all]}]; +handle_data(silent_connections,Node,Conn,_Spec) when is_atom(Conn) -> + [{Node,[Conn]}]; +handle_data(silent_connections,Node,Conns,_Spec) -> + [{Node,Conns}]; +handle_data(_Tag,Node,Data,_Spec) -> + [{Node,Data}]. + +%% check if duplicates should be saved or not +should_be_added(Tag,Node,_Data,Spec) -> + if + %% list terms *without* possible duplicates here + Tag == logdir; Tag == logopts; + Tag == basic_html; Tag == label; + Tag == auto_compile; Tag == stylesheet; + Tag == verbosity; Tag == silent_connections -> + lists:keymember(ref2node(Node,Spec#testspec.nodes),1, + read_field(Spec,Tag)) == false; + %% for terms *with* possible duplicates + true -> + true + end. + +%% check if previous elements for Node should be deleted +update_recorded(Tag,Node,Spec) -> + if Tag == config; Tag == userconfig; Tag == event_handler; + Tag == ct_hooks; Tag == include -> + read_field(Spec,Tag); + true -> + %% delete previous value for Tag + lists:keydelete(Node,1,read_field(Spec,Tag)) + end. + +%% create one test term per node separate(Nodes,Tag,Data,Tests,Refs) -> Separated = separate(Nodes,Tag,Data,Refs), Separated ++ Tests. @@ -886,7 +982,25 @@ separate([N|Ns],Tag,Data,Refs) -> [list_to_tuple([Tag,ref2node(N,Refs)|Data])|separate(Ns,Tag,Data,Refs)]; separate([],_,_,_) -> []. - + +%% read the value for FieldName in record Rec#testspec +read_field(Rec, FieldName) -> + catch lists:foldl(fun(F, Pos) when F == FieldName -> + throw(element(Pos, Rec)); + (_,Pos) -> + Pos+1 + end,2,?testspec_fields). + +%% modify the value for FieldName in record Rec#testspec +mod_field(Rec, FieldName, NewVal) -> + [_testspec|RecList] = tuple_to_list(Rec), + RecList1 = + (catch lists:foldl(fun(F, {Prev,[_OldVal|Rest]}) when F == FieldName -> + throw(lists:reverse(Prev) ++ [NewVal|Rest]); + (_,{Prev,[Field|Rest]}) -> + {[Field|Prev],Rest} + end,{[],RecList},?testspec_fields)), + list_to_tuple([testspec|RecList1]). %% Representation: %% {{Node,Dir},[{Suite1,[GrOrCase11,GrOrCase12,...]}, @@ -1094,33 +1208,40 @@ ref2node(all_nodes,_Refs) -> ref2node(master,_Refs) -> master; ref2node(RefOrNode,Refs) -> - case string:chr(atom_to_list(RefOrNode),$@) of - 0 -> % a ref + case lists:member($@,atom_to_list(RefOrNode)) of + false -> % a ref case lists:keysearch(RefOrNode,1,Refs) of {value,{RefOrNode,Node}} -> Node; false -> throw({error,{noderef_missing,RefOrNode}}) end; - _ -> % a node + true -> % a node RefOrNode end. -ref2dir(Ref,Refs) when is_atom(Ref) -> +ref2dir(Ref,Spec) -> + ref2dir(Ref,Spec#testspec.alias,Spec). + +ref2dir(Ref,Refs,Spec) when is_atom(Ref) -> case lists:keysearch(Ref,1,Refs) of {value,{Ref,Dir}} -> - Dir; + get_absdir(Dir,Spec); false -> throw({error,{alias_missing,Ref}}) end; -ref2dir(Dir,_) when is_list(Dir) -> - Dir. - -is_noderef(What,Nodes) when is_atom(What) -> - is_noderef([What],Nodes); -is_noderef([master|_],_Nodes) -> +ref2dir(Dir,_,Spec) when is_list(Dir) -> + get_absdir(Dir,Spec); +ref2dir(What,_,_) -> + throw({error,{invalid_directory_name,What}}). + +is_node(What,Nodes) when is_atom(What) -> + is_node([What],Nodes); +is_node([master|_],_Nodes) -> true; -is_noderef([What|_],Nodes) -> +is_node(What={N,H},Nodes) when is_atom(N), is_atom(H) -> + is_node([What],Nodes); +is_node([What|_],Nodes) -> case lists:keymember(What,1,Nodes) or lists:keymember(What,2,Nodes) of true -> @@ -1128,24 +1249,32 @@ is_noderef([What|_],Nodes) -> false -> false end; -is_noderef([],_) -> +is_node([],_) -> false. valid_terms() -> [ + {define,3}, {node,3}, {cover,2}, {cover,3}, {config,2}, {config,3}, + {config,4}, {userconfig,2}, {userconfig,3}, {alias,3}, - {merge_tests,1}, + {merge_tests,2}, {logdir,2}, {logdir,3}, {logopts,2}, {logopts,3}, + {basic_html,2}, + {basic_html,3}, + {verbosity,2}, + {verbosity,3}, + {silent_connections,2}, + {silent_connections,3}, {label,2}, {label,3}, {event_handler,2}, @@ -1153,13 +1282,18 @@ valid_terms() -> {event_handler,4}, {ct_hooks,2}, {ct_hooks,3}, - {enable_builtin_hooks,1}, + {enable_builtin_hooks,2}, + {release_shell,2}, {multiply_timetraps,2}, {multiply_timetraps,3}, {scale_timetraps,2}, {scale_timetraps,3}, {include,2}, {include,3}, + {auto_compile,2}, + {auto_compile,3}, + {stylesheet,2}, + {stylesheet,3}, {suites,3}, {suites,4}, {groups,4}, @@ -1174,7 +1308,8 @@ valid_terms() -> {skip_groups,7}, {skip_cases,5}, {skip_cases,6}, - {create_priv_dir,2} + {create_priv_dir,2}, + {create_priv_dir,3} ]. %% this function "guesses" if the user has misspelled a term name diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index 9d6ee3c8b9..cf891ed043 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -25,7 +25,8 @@ %%% -module(ct_util). --export([start/0,start/1,start/2,stop/1,update_last_run_index/0]). +-export([start/0,start/1,start/2,start/3, + stop/1,update_last_run_index/0]). -export([register_connection/4,unregister_connection/1, does_connection_exist/3,get_key_from_name/1]). @@ -36,14 +37,15 @@ save_suite_data_async/3, save_suite_data_async/2, read_suite_data/1, delete_suite_data/0, delete_suite_data/1, match_delete_suite_data/1, - delete_testdata/0, delete_testdata/1, set_testdata/1, get_testdata/1, - set_testdata_async/1, update_testdata/2]). + delete_testdata/0, delete_testdata/1, + set_testdata/1, get_testdata/1, get_testdata/2, + set_testdata_async/1, update_testdata/2, update_testdata/3]). -export([override_silence_all_connections/0, override_silence_connections/1, get_overridden_silenced_connections/0, delete_overridden_silenced_connections/0, - silence_all_connections/0, silence_connections/1, is_silenced/1, - reset_silent_connections/0]). + silence_all_connections/0, silence_connections/1, + is_silenced/1, is_silenced/2, reset_silent_connections/0]). -export([get_mode/0, create_table/3, read_opts/0]). @@ -64,9 +66,13 @@ -export([get_profile_data/0, get_profile_data/1, get_profile_data/2, open_url/3]). +-include("ct.hrl"). -include("ct_event.hrl"). -include("ct_util.hrl"). +-define(default_verbosity, [{default,?MAX_VERBOSITY}, + {'$unspecified',?MAX_VERBOSITY}]). + -record(suite_data, {key,name,value}). %%%----------------------------------------------------------------- @@ -85,18 +91,21 @@ %%% %%% @see ct start() -> - start(normal,"."). + start(normal, ".", ?default_verbosity). start(LogDir) when is_list(LogDir) -> - start(normal,LogDir); + start(normal, LogDir, ?default_verbosity); start(Mode) -> - start(Mode,"."). + start(Mode, ".", ?default_verbosity). + +start(LogDir, Verbosity) when is_list(LogDir) -> + start(normal, LogDir, Verbosity). -start(Mode,LogDir) -> +start(Mode, LogDir, Verbosity) -> case whereis(ct_util_server) of undefined -> S = self(), - Pid = spawn_link(fun() -> do_start(S,Mode,LogDir) end), + Pid = spawn_link(fun() -> do_start(S, Mode, LogDir, Verbosity) end), receive {Pid,started} -> Pid; {Pid,Error} -> exit(Error); @@ -113,7 +122,7 @@ start(Mode,LogDir) -> end end. -do_start(Parent,Mode,LogDir) -> +do_start(Parent, Mode, LogDir, Verbosity) -> process_flag(trap_exit,true), register(ct_util_server,self()), create_table(?conn_table,#conn.handle), @@ -173,7 +182,7 @@ do_start(Parent,Mode,LogDir) -> false -> ok end, - {StartTime,TestLogDir} = ct_logs:init(Mode), + {StartTime,TestLogDir} = ct_logs:init(Mode, Verbosity), ct_event:notify(#event{name=test_start, node=node(), @@ -193,7 +202,7 @@ do_start(Parent,Mode,LogDir) -> self() ! {{stop,{self(),{user_error,CTHReason}}}, {Parent,make_ref()}} end, - loop(Mode,[],StartDir). + loop(Mode, [{{verbosity,Cat},Lvl} || {Cat,Lvl} <- Verbosity], StartDir). create_table(TableName,KeyPos) -> create_table(TableName,set,KeyPos). @@ -243,7 +252,10 @@ delete_testdata(Key) -> call({delete_testdata, Key}). update_testdata(Key, Fun) -> - call({update_testdata, Key, Fun}). + update_testdata(Key, Fun, []). + +update_testdata(Key, Fun, Opts) -> + call({update_testdata, Key, Fun, Opts}). set_testdata(TestData) -> call({set_testdata, TestData}). @@ -254,6 +266,9 @@ set_testdata_async(TestData) -> get_testdata(Key) -> call({get_testdata, Key}). +get_testdata(Key, Timeout) -> + call({get_testdata, Key}, Timeout). + set_cwd(Dir) -> call({set_cwd,Dir}). @@ -321,7 +336,7 @@ loop(Mode,TestData,StartDir) -> return(From,undefined) end, loop(From,TestData,StartDir); - {{update_testdata,Key,Fun},From} -> + {{update_testdata,Key,Fun,Opts},From} -> TestData1 = case lists:keysearch(Key,1,TestData) of {value,{Key,Val}} -> @@ -329,8 +344,15 @@ loop(Mode,TestData,StartDir) -> return(From,NewVal), [{Key,NewVal}|lists:keydelete(Key,1,TestData)]; _ -> - return(From,undefined), - TestData + case lists:member(create,Opts) of + true -> + InitVal = Fun(undefined), + return(From,InitVal), + [{Key,InitVal}|TestData]; + false -> + return(From,undefined), + TestData + end end, loop(From,TestData1,StartDir); {{set_cwd,Dir},From} -> @@ -369,14 +391,25 @@ loop(Mode,TestData,StartDir) -> {'EXIT',_Pid,normal} -> loop(Mode,TestData,StartDir); {'EXIT',Pid,Reason} -> - %% Let process crash in case of error, this shouldn't happen! - io:format("\n\nct_util_server got EXIT from ~p: ~p\n\n", - [Pid,Reason]), - file:set_cwd(StartDir), - exit(Reason) + case ets:lookup(?conn_table,Pid) of + [#conn{address=A,callback=CB}] -> + %% A connection crashed - remove the connection but don't die + ct_logs:tc_log_async(ct_error_notify, + "Connection process died: " + "Pid: ~p, Address: ~p, Callback: ~p\n" + "Reason: ~p\n\n", + [Pid,A,CB,Reason]), + catch CB:close(Pid), + loop(Mode,TestData,StartDir); + _ -> + %% Let process crash in case of error, this shouldn't happen! + io:format("\n\nct_util_server got EXIT from ~p: ~p\n\n", + [Pid,Reason]), + file:set_cwd(StartDir), + exit(Reason) + end end. - close_connections([#conn{handle=Handle,callback=CB}|Conns]) -> CB:close(Handle), close_connections(Conns); @@ -508,7 +541,7 @@ close_connections() -> %%% %%% @doc override_silence_all_connections() -> - Protocols = [telnet,ftp,rpc,snmp], + Protocols = [telnet,ftp,rpc,snmp,ssh], override_silence_connections(Protocols), Protocols. @@ -545,7 +578,10 @@ silence_connections(Conns) when is_list(Conns) -> set_testdata({silent_connections,Conns1}). is_silenced(Conn) -> - case get_testdata(silent_connections) of + is_silenced(Conn, infinity). + +is_silenced(Conn, Timeout) -> + case get_testdata(silent_connections, Timeout) of Conns when is_list(Conns) -> case lists:keysearch(Conn,1,Conns) of {value,{Conn,true}} -> @@ -553,6 +589,8 @@ is_silenced(Conn) -> _ -> false end; + Error = {error,_} -> + Error; _ -> false end. @@ -827,19 +865,28 @@ get_profile_data(Profile, Key, StartDir) -> %%%----------------------------------------------------------------- %%% Internal functions call(Msg) -> - case whereis(ct_util_server) of - undefined -> + call(Msg, infinity). + +call(Msg, Timeout) -> + case {self(),whereis(ct_util_server)} of + {_,undefined} -> {error,ct_util_server_not_running}; - Pid -> + {Pid,Pid} -> + %% the caller is ct_util_server, which must + %% be a mistake + {error,bad_invocation}; + {Self,Pid} -> MRef = erlang:monitor(process, Pid), Ref = make_ref(), - ct_util_server ! {Msg,{self(),Ref}}, + ct_util_server ! {Msg,{Self,Ref}}, receive {Ref, Result} -> erlang:demonitor(MRef, [flush]), Result; {'DOWN',MRef,process,_,Reason} -> {error,{ct_util_server_down,Reason}} + after + Timeout -> {error,timeout} end end. diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl index 6b016e95df..196b5e46d0 100644 --- a/lib/common_test/src/ct_util.hrl +++ b/lib/common_test/src/ct_util.hrl @@ -34,13 +34,19 @@ profile=[], logdir=["."], logopts=[], + basic_html=[], + verbosity=[], + silent_connections=[], cover=[], config=[], userconfig=[], event_handler=[], ct_hooks=[], enable_builtin_hooks=true, + release_shell=false, include=[], + auto_compile=[], + stylesheet=[], multiply_timetraps=[], scale_timetraps=[], create_priv_dir=[], @@ -64,3 +70,11 @@ -define(ct_config_txt, ct_config_plain). -define(ct_profile_file, ".common_test"). + +-define(css_default, "ct_default.css"). +-define(sortable_table_name, "SortableTable"). +-define(jquery_script, "jquery-latest.js"). +-define(tablesorter_script, "jquery.tablesorter.min.js"). + +%% Logging information for error handler +-record(conn_log, {client, name, address, action, module}). diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl new file mode 100644 index 0000000000..3af89db3a5 --- /dev/null +++ b/lib/common_test/src/cth_conn_log.erl @@ -0,0 +1,124 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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% +%% +%%---------------------------------------------------------------------- +%% CT hook for logging of connections. +%% +%% HookOptions can be hardcoded in the test suite: +%% +%% suite() -> +%% [{ct_hooks, [{cth_conn_log, +%% [{ct_netconfc:conn_mod(),ct_netconfc:hook_options()}]}]}]. +%% +%% or specified in a configuration file: +%% +%% {ct_conn_log,[{ct_netconfc:conn_mod(),ct_netconfc:hook_options()}]}. +%% +%% The conn_mod() is the common test module implementing the protocol, +%% e.g. ct_netconfc, ct_telnet, etc. This module must log by calling +%% +%% error_logger:info_report(ConnLogInfo,Data). +%% ConnLogInfo = #conn_log{} | {ct_connection,Action,ConnName} +%% Action = open | close | send | recv | term() +%% ConnName = atom() - The 'KeyOrName' argument used when opening the connection +%% +%% ct_conn_log_h will print to html log or separate file (depending on +%% log_type() option). conn_mod() must implement and export +%% +%% format_data(log_type(), Data). +%% +%% If logging to separate file, ct_conn_log_h will also log error +%% reports which are witten like this: +%% +%% error_logger:error_report([{ct_connection,ConnName} | Report]). +%% +%%---------------------------------------------------------------------- +-module(cth_conn_log). + +-include_lib("common_test/include/ct.hrl"). + +-export([init/2, + pre_init_per_testcase/3, + post_end_per_testcase/4]). + +-spec init(Id, HookOpts) -> Result when + Id :: term(), + HookOpts :: ct:hook_options(), + Result :: {ok,[{ct_netconfc:conn_mod(), + {ct_netconfc:log_type(),[ct_netconfc:key_or_name()]}}]}. +init(_Id, HookOpts) -> + ConfOpts = ct:get_config(ct_conn_log,[]), + {ok,merge_log_info(ConfOpts,HookOpts)}. + +merge_log_info([{Mod,ConfOpts}|ConfList],HookList) -> + {Opts,HookList1} = + case lists:keytake(Mod,1,HookList) of + false -> + {ConfOpts,HookList}; + {value,{_,HookOpts},HL1} -> + {ConfOpts ++ HookOpts, HL1} % ConfOpts overwrites HookOpts! + end, + [{Mod,get_log_opts(Opts)} | merge_log_info(ConfList,HookList1)]; +merge_log_info([],HookList) -> + [{Mod,get_log_opts(Opts)} || {Mod,Opts} <- HookList]. + +get_log_opts(Opts) -> + LogType = proplists:get_value(log_type,Opts,html), + Hosts = proplists:get_value(hosts,Opts,[]), + {LogType,Hosts}. + + +pre_init_per_testcase(TestCase,Config,CthState) -> + Logs = + lists:map( + fun({ConnMod,{LogType,Hosts}}) -> + case LogType of + LogType when LogType==raw; LogType==pretty -> + Dir = ?config(priv_dir,Config), + TCStr = atom_to_list(TestCase), + ConnModStr = atom_to_list(ConnMod), + DefLogName = TCStr ++ "-" ++ ConnModStr ++ ".txt", + DefLog = filename:join(Dir,DefLogName), + Ls = [{Host, + filename:join(Dir,TCStr ++ "-"++ + atom_to_list(Host) ++ "-" ++ + ConnModStr ++ + ".txt")} + || Host <- Hosts] + ++[{default,DefLog}], + Str = + "<table borders=1>" + "<b>" ++ ConnModStr ++ " logs:</b>\n" ++ + [io_lib:format( + "<tr><td>~p</td><td><a href=~p>~s</a></td></tr>", + [S,L,filename:basename(L)]) + || {S,L} <- Ls] ++ + "</table>", + io:format(Str,[]), + {ConnMod,{LogType,Ls}}; + _ -> + {ConnMod,{LogType,[]}} + end + end, + CthState), + error_logger:add_report_handler(ct_conn_log_h,{group_leader(),Logs}), + {Config,CthState}. + +post_end_per_testcase(_TestCase,_Config,Return,CthState) -> + error_logger:delete_report_handler(ct_conn_log_h), + {Return,CthState}. diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl index c42f956b3a..76b0f0b5ea 100644 --- a/lib/common_test/src/cth_surefire.erl +++ b/lib/common_test/src/cth_surefire.erl @@ -49,9 +49,12 @@ init(Path, Opts) -> properties = proplists:get_value(properties,Opts,[]), timer = now() }. -pre_init_per_suite(Suite,Config,State) -> +pre_init_per_suite(Suite,Config,#state{ test_cases = [] } = State) -> {Config, init_tc(State#state{ curr_suite = Suite, curr_suite_ts = now() }, - Config) }. + Config) }; +pre_init_per_suite(Suite,Config,State) -> + %% Have to close the previous suite + pre_init_per_suite(Suite,Config,close_suite(State)). post_init_per_suite(_Suite,Config, Result, State) -> {Result, end_tc(init_per_suite,Config,Result,State)}. @@ -59,11 +62,7 @@ post_init_per_suite(_Suite,Config, Result, State) -> pre_end_per_suite(_Suite,Config,State) -> {Config, init_tc(State, Config)}. post_end_per_suite(_Suite,Config,Result,State) -> - NewState = end_tc(end_per_suite,Config,Result,State), - TCs = NewState#state.test_cases, - Suite = get_suite(NewState, TCs), - {Result, State#state{ test_cases = [], - test_suites = [Suite | State#state.test_suites]}}. + {Result, end_tc(end_per_suite,Config,Result,State)}. pre_init_per_group(Group,Config,State) -> {Config, init_tc(State#state{ curr_group = [Group|State#state.curr_group]}, @@ -83,24 +82,36 @@ pre_init_per_testcase(_TC,Config,State) -> {Config, init_tc(State, Config)}. post_end_per_testcase(TC,Config,Result,State) -> {Result, end_tc(TC,Config, Result,State)}. +on_tc_fail(_TC, _Res, State = #state{test_cases = []}) -> + State; on_tc_fail(_TC, Res, State) -> TCs = State#state.test_cases, - TC = hd(State#state.test_cases), - NewTC = TC#testcase{ failure = - {fail,lists:flatten(io_lib:format("~p",[Res]))} }, + TC = hd(TCs), + NewTC = TC#testcase{ + failure = + {fail,lists:flatten(io_lib:format("~p",[Res]))} }, State#state{ test_cases = [NewTC | tl(TCs)]}. +on_tc_skip(Tc,{Type,_Reason} = Res, State) when Type == tc_auto_skip -> + do_tc_skip(Res, end_tc(Tc,[],Res,init_tc(State,[]))); +on_tc_skip(_Tc, _Res, State = #state{test_cases = []}) -> + State; on_tc_skip(_Tc, Res, State) -> + do_tc_skip(Res, State). + +do_tc_skip(Res, State) -> TCs = State#state.test_cases, - TC = hd(State#state.test_cases), + TC = hd(TCs), NewTC = TC#testcase{ failure = {skipped,lists:flatten(io_lib:format("~p",[Res]))} }, State#state{ test_cases = [NewTC | tl(TCs)]}. +init_tc(State, Config) when is_list(Config) == false -> + State#state{ timer = now(), tc_log = "" }; init_tc(State, Config) -> State#state{ timer = now(), - tc_log = proplists:get_value(tc_logfile, Config)}. + tc_log = proplists:get_value(tc_logfile, Config, [])}. end_tc(Func, Config, Res, State) when is_atom(Func) -> end_tc(atom_to_list(Func), Config, Res, State); @@ -118,26 +129,35 @@ end_tc(Name, _Config, _Res, State = #state{ curr_suite = Suite, name = Name, time = TimeTakes, failure = passed }| State#state.test_cases]}. - -get_suite(State, TCs) -> +close_suite(#state{ test_cases = [] } = State) -> + State; +close_suite(#state{ test_cases = TCs } = State) -> Total = length(TCs), Succ = length(lists:filter(fun(#testcase{ failure = F }) -> F == passed end,TCs)), Fail = Total - Succ, TimeTaken = timer:now_diff(now(),State#state.curr_suite_ts) / 1000000, - #testsuite{ name = atom_to_list(State#state.curr_suite), - package = State#state.package, - time = io_lib:format("~f",[TimeTaken]), - timestamp = now_to_string(State#state.curr_suite_ts), - errors = Fail, tests = Total, testcases = lists:reverse(TCs) }. - -terminate(State) -> - {ok,D} = file:open(State#state.filepath,[write]), + Suite = #testsuite{ name = atom_to_list(State#state.curr_suite), + package = State#state.package, + time = io_lib:format("~f",[TimeTaken]), + timestamp = now_to_string(State#state.curr_suite_ts), + errors = Fail, tests = Total, + testcases = lists:reverse(TCs) }, + State#state{ test_cases = [], + test_suites = [Suite | State#state.test_suites]}. + +terminate(State = #state{ test_cases = [] }) -> + {ok,D} = file:open(State#state.filepath,[write,{encoding,utf8}]), io:format(D, "<?xml version=\"1.0\" encoding= \"UTF-8\" ?>", []), io:format(D, to_xml(State), []), catch file:sync(D), - catch file:close(D). + catch file:close(D); +terminate(State) -> + %% Have to close the last suite + terminate(close_suite(State)). + + to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, timestamp = TS, failure = F}) -> ["<testcase ", diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index 2a52f8ed74..7628ada61a 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -39,13 +39,18 @@ MODULES= \ ct_sequence_1_SUITE \ ct_repeat_1_SUITE \ ct_testspec_1_SUITE \ + ct_testspec_2_SUITE \ ct_skip_SUITE \ ct_error_SUITE \ ct_test_server_if_1_SUITE \ ct_config_SUITE \ ct_master_SUITE \ ct_misc_1_SUITE \ - ct_hooks_SUITE + ct_hooks_SUITE \ + ct_netconfc_SUITE \ + ct_basic_html_SUITE \ + ct_auto_compile_SUITE \ + ct_verbosity_SUITE ERL_FILES= $(MODULES:%=%.erl) @@ -97,10 +102,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(COVERFILE) $(RELSYSDIR) - $(INSTALL_DATA) common_test.spec $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) $(COVERFILE) "$(RELSYSDIR)" + $(INSTALL_DATA) common_test.spec "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/common_test/test/common_test.spec b/lib/common_test/test/common_test.spec index 8755b08117..8bec66d6f2 100644 --- a/lib/common_test/test/common_test.spec +++ b/lib/common_test/test/common_test.spec @@ -1 +1 @@ -{suites,"../common_test_test",all}.
\ No newline at end of file +{suites,"../common_test_test",all}. diff --git a/lib/common_test/test/ct_auto_compile_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE.erl new file mode 100644 index 0000000000..cc546ed30d --- /dev/null +++ b/lib/common_test/test/ct_auto_compile_SUITE.erl @@ -0,0 +1,187 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_auto_compile_SUITE +%%% +%%% Description: +%%% +%%% +%%% The suites used for the test are located in the data directory. +%%%------------------------------------------------------------------- +-module(ct_auto_compile_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ac_flag, ac_spec]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +ac_flag(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + file:copy(filename:join(DataDir, "bad_SUITE.erl"), + filename:join(PrivDir, "bad_SUITE.erl")), + Suite = filename:join(DataDir, "dummy_SUITE"), + compile:file(Suite, [{outdir,PrivDir}]), + {Opts,ERPid} = setup([{dir,PrivDir}, + {auto_compile,false}, + {label,"ac_flag"}], + Config), + + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(ac_flag, + reformat(Events, ?eh), + PrivDir, + Opts), + + TestEvents = events_to_check(ac_flag), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + +%%%----------------------------------------------------------------- +%%% +ac_spec(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + file:copy(filename:join(DataDir, "bad_SUITE.erl"), + filename:join(PrivDir, "bad_SUITE.erl")), + TestSpec = [{label,ac_spec}, + {auto_compile,false}, + {suites,PrivDir,all}], + FileName = filename:join(?config(priv_dir, Config),"ac_spec.spec"), + {ok,Dev} = file:open(FileName, [write]), + [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec], + file:close(Dev), + + {Opts,ERPid} = setup([{spec,FileName}], Config), + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(ac_spec, + reformat(Events, ?eh), + PrivDir, + Opts), + + TestEvents = events_to_check(ac_spec), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + %reformat(Events, _EH) -> + % Events. + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test) -> + %% 2 tests (ct:run_test + script_start) is default + events_to_check(Test, 2). + +events_to_check(_, 0) -> + []; +events_to_check(Test, N) -> + test_events(Test) ++ events_to_check(Test, N-1). + +test_events(ac_flag) -> + [ + {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}}, + {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {ct_test_support_eh,start_info,{1,1,3}}, + {ct_test_support_eh,tc_start,{dummy_SUITE,init_per_suite}}, + {ct_test_support_eh,tc_done,{dummy_SUITE,init_per_suite,ok}}, + {ct_test_support_eh,test_stats,{1,1,{1,0}}}, + {ct_test_support_eh,tc_start,{dummy_SUITE,end_per_suite}}, + {ct_test_support_eh,tc_done,{dummy_SUITE,end_per_suite,ok}}, + {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}}, + {ct_test_support_eh,stop_logging,[]} + ]; + +test_events(ac_spec) -> + [ + {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}}, + {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {ct_test_support_eh,start_info,{1,1,3}}, + {ct_test_support_eh,tc_start,{dummy_SUITE,init_per_suite}}, + {ct_test_support_eh,tc_done,{dummy_SUITE,init_per_suite,ok}}, + {ct_test_support_eh,test_stats,{1,1,{1,0}}}, + {ct_test_support_eh,tc_start,{dummy_SUITE,end_per_suite}}, + {ct_test_support_eh,tc_done,{dummy_SUITE,end_per_suite,ok}}, + {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}}, + {ct_test_support_eh,stop_logging,[]} + ]. diff --git a/lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl new file mode 100644 index 0000000000..6ebcb3570e --- /dev/null +++ b/lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl @@ -0,0 +1,23 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. 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(bad_SUITE). + +-compile(export_all). + +bad_bad_suite diff --git a/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl new file mode 100644 index 0000000000..0b1eafc31d --- /dev/null +++ b/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl @@ -0,0 +1,130 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. 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(dummy_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% @spec suite() -> Info +%% Info = [tuple()] +%% @end +%%-------------------------------------------------------------------- +suite() -> + [{timetrap,{seconds,30}}]. + +%%-------------------------------------------------------------------- +%% @spec init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_suite(Config0) -> void() | {save_config,Config1} +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_group(GroupName, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_group(GroupName, Config0) -> +%% void() | {save_config,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_group(_GroupName, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_testcase(TestCase, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} | {fail,Reason} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec groups() -> [Group] +%% Group = {GroupName,Properties,GroupsAndTestCases} +%% GroupName = atom() +%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}] +%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase] +%% TestCase = atom() +%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}} +%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | +%% repeat_until_any_ok | repeat_until_any_fail +%% N = integer() | forever +%% @end +%%-------------------------------------------------------------------- +groups() -> + []. + +%%-------------------------------------------------------------------- +%% @spec all() -> GroupsAndTestCases | {skip,Reason} +%% GroupsAndTestCases = [{group,GroupName} | TestCase] +%% GroupName = atom() +%% TestCase = atom() +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +all() -> + [ok,fail,skip]. + + +ok(_Config) -> + ok. + +fail(Config) -> + tuple_to_list(Config), + ok. + +skip(_Config) -> + {skip,"should be skipped"}. diff --git a/lib/common_test/test/ct_basic_html_SUITE.erl b/lib/common_test/test/ct_basic_html_SUITE.erl new file mode 100644 index 0000000000..a5f2e6197e --- /dev/null +++ b/lib/common_test/test/ct_basic_html_SUITE.erl @@ -0,0 +1,180 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_basic_html_SUITE +%%% +%%% Description: +%%% +%%% +%%% The suites used for the test are located in the data directory. +%%%------------------------------------------------------------------- +-module(ct_basic_html_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [basic_flag, basic_spec]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +basic_flag(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Suites = [filename:join(DataDir, "babbling_SUITE")], + {Opts,ERPid} = setup([{suite,Suites}, + {basic_html,true}, + {label,"basic_flag"}], + Config), + + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(basic_flag, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(basic_flag), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + +%%%----------------------------------------------------------------- +%%% +basic_spec(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + TestSpec = [{label,basic_spec}, + {basic_html,true}, + {suites,DataDir,babbling_SUITE}], + FileName = filename:join(?config(priv_dir, Config),"basic_spec.spec"), + {ok,Dev} = file:open(FileName, [write]), + [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec], + file:close(Dev), + + {Opts,ERPid} = setup([{spec,FileName}], Config), + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(basic_spec, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(basic_spec), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + %reformat(Events, _EH) -> + % Events. + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test) -> + %% 2 tests (ct:run_test + script_start) is default + events_to_check(Test, 2). + +events_to_check(_, 0) -> + []; +events_to_check(Test, N) -> + test_events(Test) ++ events_to_check(Test, N-1). + +test_events(basic_flag) -> + [ + {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}}, + {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {ct_test_support_eh,start_info,{1,1,3}}, + {ct_test_support_eh,tc_start,{babbling_SUITE,init_per_suite}}, + {ct_test_support_eh,tc_done,{babbling_SUITE,init_per_suite,ok}}, + {ct_test_support_eh,test_stats,{1,1,{1,0}}}, + {ct_test_support_eh,tc_start,{babbling_SUITE,end_per_suite}}, + {ct_test_support_eh,tc_done,{babbling_SUITE,end_per_suite,ok}}, + {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}}, + {ct_test_support_eh,stop_logging,[]} + ]; + +test_events(basic_spec) -> + [ + {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}}, + {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {ct_test_support_eh,start_info,{1,1,3}}, + {ct_test_support_eh,tc_start,{babbling_SUITE,init_per_suite}}, + {ct_test_support_eh,tc_done,{babbling_SUITE,init_per_suite,ok}}, + {ct_test_support_eh,test_stats,{1,1,{1,0}}}, + {ct_test_support_eh,tc_start,{babbling_SUITE,end_per_suite}}, + {ct_test_support_eh,tc_done,{babbling_SUITE,end_per_suite,ok}}, + {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}}, + {ct_test_support_eh,stop_logging,[]} + ]. diff --git a/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl b/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl new file mode 100644 index 0000000000..d67383c606 --- /dev/null +++ b/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl @@ -0,0 +1,130 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. 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(babbling_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% @spec suite() -> Info +%% Info = [tuple()] +%% @end +%%-------------------------------------------------------------------- +suite() -> + [{timetrap,{seconds,30}}]. + +%%-------------------------------------------------------------------- +%% @spec init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_suite(Config0) -> void() | {save_config,Config1} +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_group(GroupName, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_group(GroupName, Config0) -> +%% void() | {save_config,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_group(_GroupName, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_testcase(TestCase, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} | {fail,Reason} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec groups() -> [Group] +%% Group = {GroupName,Properties,GroupsAndTestCases} +%% GroupName = atom() +%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}] +%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase] +%% TestCase = atom() +%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}} +%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | +%% repeat_until_any_ok | repeat_until_any_fail +%% N = integer() | forever +%% @end +%%-------------------------------------------------------------------- +groups() -> + []. + +%%-------------------------------------------------------------------- +%% @spec all() -> GroupsAndTestCases | {skip,Reason} +%% GroupsAndTestCases = [{group,GroupName} | TestCase] +%% GroupName = atom() +%% TestCase = atom() +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +all() -> + [ok,fail,skip]. + + +ok(_Config) -> + ok. + +fail(Config) -> + tuple_to_list(Config), + ok. + +skip(_Config) -> + {skip,"should be skipped"}. diff --git a/lib/common_test/test/ct_config_SUITE.erl b/lib/common_test/test/ct_config_SUITE.erl index 18218bee47..83b8c00458 100644 --- a/lib/common_test/test/ct_config_SUITE.erl +++ b/lib/common_test/test/ct_config_SUITE.erl @@ -88,7 +88,8 @@ require(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), run_test(config_static_SUITE, Config, - {config, filename:join(DataDir, "config/config.txt")}, + [{config, filename:join(DataDir, "config/shadow.txt")}, + {config, filename:join(DataDir, "config/config.txt")}], ["config_static_SUITE"]). install_config(Config) when is_list(Config) -> @@ -106,7 +107,8 @@ userconfig_static(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), run_test(config_static_SUITE, Config, - {userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}, + [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}, + {config, filename:join(DataDir, "config/shadow.txt")}], ["config_static_SUITE"]). userconfig_dynamic(Config) when is_list(Config) -> @@ -121,7 +123,8 @@ testspec_legacy(Config) when is_list(Config) -> make_spec(DataDir, ConfigDir, "spec_legacy.spec", [config_static_SUITE], - [{config, filename:join(DataDir, "config/config.txt")}]), + [{config, filename:join(DataDir, "config/shadow.txt")}, + {config, filename:join(DataDir, "config/config.txt")}]), run_test(config_static_SUITE, Config, {spec, filename:join(ConfigDir, "spec_legacy.spec")}, @@ -134,7 +137,8 @@ testspec_static(Config) when is_list(Config) -> make_spec(DataDir, ConfigDir, "spec_static.spec", [config_static_SUITE], - [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}]), + [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}, + {config, filename:join(DataDir, "config/shadow.txt")}]), run_test(config_static_SUITE, Config, {spec, filename:join(ConfigDir, "spec_static.spec")}, @@ -179,13 +183,15 @@ run_test(Name, Config, CTConfig, SuiteNames)-> ExpEvents = events_to_check(Name), ok = ct_test_support:verify_events(ExpEvents, TestEvents, Config). -setup_env(Test, Config, CTConfig) -> +setup_env(Test, Config, CTConfig) when is_list(CTConfig) -> Opts0 = ct_test_support:get_opts(Config), Level = ?config(trace_level, Config), EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], - Opts = Opts0 ++ [Test,{event_handler,{?eh,EvHArgs}}, CTConfig], + Opts = Opts0 ++ [Test,{event_handler,{?eh,EvHArgs}} | CTConfig], ERPid = ct_test_support:start_event_receiver(Config), - {Opts,ERPid}. + {Opts,ERPid}; +setup_env(Test, Config, CTConfig) -> + setup_env(Test, Config, [CTConfig]). reformat_events(Events, EH) -> ct_test_support:reformat(Events, EH). @@ -202,40 +208,49 @@ events_to_check(_, 0) -> events_to_check(Test, N) -> expected_events(Test) ++ events_to_check(Test, N-1). +-define(ok(Name,Suite,Stat),{?eh,tc_start,{Suite,Name}}, + {?eh,tc_done,{Suite,Name,ok}}, + {?eh,test_stats,Stat}). +-define(nok(Name,Suite,Reason,Stat),{?eh,tc_start,{Suite,Name}}, + {?eh,tc_done,{Suite,Name,Reason}}, + {?eh,test_stats,Stat}). + +-define(sok(Name,Stat),?ok(Name,config_static_SUITE,Stat)). +-define(snok(Name,Reason,Stat),?nok(Name,config_static_SUITE,Reason,Stat)). + +-define(dok(Name,Stat),?ok(Name,config_dynamic_SUITE,Stat)). +-define(dnok(Name,Reason,Stat),?nok(Name,config_dynamic_SUITE,Reason,Stat)). + expected_events(config_static_SUITE)-> [ {?eh,start_logging,{'DEF','RUNDIR'}}, {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, - {?eh,start_info,{1,1,8}}, + {?eh,start_info,{1,1,'_'}}, {?eh,tc_start,{config_static_SUITE,init_per_suite}}, {?eh,tc_done,{config_static_SUITE,init_per_suite,ok}}, - {?eh,tc_start,{config_static_SUITE,test_get_config_simple}}, - {?eh,tc_done,{config_static_SUITE,test_get_config_simple,ok}}, - {?eh,test_stats,{1,0,{0,0}}}, - {?eh,tc_start,{config_static_SUITE,test_get_config_nested}}, - {?eh,tc_done,{config_static_SUITE,test_get_config_nested,ok}}, - {?eh,test_stats,{2,0,{0,0}}}, - {?eh,tc_start,{config_static_SUITE,test_default_suitewide}}, - {?eh,tc_done,{config_static_SUITE,test_default_suitewide,ok}}, - {?eh,test_stats,{3,0,{0,0}}}, - {?eh,tc_start,{config_static_SUITE,test_config_name_already_in_use1}}, - {?eh,tc_done, - {config_static_SUITE,test_config_name_already_in_use1,{skipped,{config_name_already_in_use,[x1]}}}}, - {?eh,test_stats,{3,0,{1,0}}}, - {?eh,tc_start,{config_static_SUITE,test_default_tclocal}}, - {?eh,tc_done,{config_static_SUITE,test_default_tclocal,ok}}, - {?eh,test_stats,{4,0,{1,0}}}, - {?eh,tc_start,{config_static_SUITE,test_config_name_already_in_use2}}, - {?eh,tc_done, - {config_static_SUITE,test_config_name_already_in_use2, - {skipped,{config_name_already_in_use,[alias,x1]}}}}, - {?eh,test_stats,{4,0,{2,0}}}, - {?eh,tc_start,{config_static_SUITE,test_alias_tclocal}}, - {?eh,tc_done,{config_static_SUITE,test_alias_tclocal,ok}}, - {?eh,test_stats,{5,0,{2,0}}}, - {?eh,tc_start,{config_static_SUITE,test_get_config_undefined}}, - {?eh,tc_done,{config_static_SUITE,test_get_config_undefined,ok}}, - {?eh,test_stats,{6,0,{2,0}}}, + ?sok(test_get_config_simple,{1,0,{0,0}}), + ?sok(test_get_config_nested,{2,0,{0,0}}), + ?sok(test_get_config_deep_nested,{3,0,{0,0}}), + ?sok(test_default_suitewide,{4,0,{0,0}}), + ?snok(test_config_name_already_in_use1, + {skipped,{config_name_already_in_use,[x1]}},{4,0,{1,0}}), + ?sok(test_default_tclocal,{5,0,{1,0}}), + ?snok(test_config_name_already_in_use2, + {skipped,{config_name_already_in_use,[alias,x1]}},{5,0,{2,0}}), + ?sok(test_alias_tclocal,{6,0,{2,0}}), + ?sok(test_get_config_undefined,{7,0,{2,0}}), + ?sok(test_require_subvals,{8,0,{2,0}}), + ?snok(test_require_subvals2, + {skipped,{require_failed, + {not_available,{gen_cfg,[a,b,c,d]}}}},{8,0,{2,1}}), + ?sok(test_require_deep_config,{9,0,{2,1}}), + ?sok(test_shadow_all,{10,0,{2,1}}), + ?sok(test_element,{11,0,{2,1}}), + ?sok(test_shadow_all_element,{12,0,{2,1}}), + ?sok(test_internal_deep,{13,0,{2,1}}), + ?sok(test_alias_tclocal_nested,{14,0,{2,1}}), + ?sok(test_alias_tclocal_nested_backward_compat,{15,0,{2,1}}), + ?sok(test_alias_tclocal_nested_backward_compat_subvals,{16,0,{2,1}}), {?eh,tc_start,{config_static_SUITE,end_per_suite}}, {?eh,tc_done,{config_static_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, @@ -246,29 +261,14 @@ expected_events(config_dynamic_SUITE)-> [ {?eh,start_logging,{'DEF','RUNDIR'}}, {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, - {?eh,start_info,{1,1,5}}, + {?eh,start_info,{1,1,'_'}}, {?eh,tc_start,{config_dynamic_SUITE,init_per_suite}}, {?eh,tc_done,{config_dynamic_SUITE,init_per_suite,ok}}, - {?eh,tc_start,{config_dynamic_SUITE,test_get_known_variable}}, - {?eh,tc_done, - {config_dynamic_SUITE,test_get_known_variable,ok}}, - {?eh,test_stats,{1,0,{0,0}}}, - {?eh,tc_start,{config_dynamic_SUITE,test_localtime_update}}, - {?eh,tc_done,{config_dynamic_SUITE,test_localtime_update,ok}}, - {?eh,test_stats,{2,0,{0,0}}}, - {?eh,tc_start,{config_dynamic_SUITE,test_server_pid}}, - {?eh,tc_done,{config_dynamic_SUITE,test_server_pid,ok}}, - {?eh,test_stats,{3,0,{0,0}}}, - {?eh,tc_start, - {config_dynamic_SUITE,test_disappearable_variable}}, - {?eh,tc_done, - {config_dynamic_SUITE,test_disappearable_variable,ok}}, - {?eh,test_stats,{4,0,{0,0}}}, - {?eh,tc_start, - {config_dynamic_SUITE,test_disappearable_variable_alias}}, - {?eh,tc_done, - {config_dynamic_SUITE,test_disappearable_variable_alias,ok}}, - {?eh,test_stats,{5,0,{0,0}}}, + ?dok(test_get_known_variable,{1,0,{0,0}}), + ?dok(test_localtime_update,{2,0,{0,0}}), + ?dok(test_server_pid,{3,0,{0,0}}), + ?dok(test_disappearable_variable,{4,0,{0,0}}), + ?dok(test_disappearable_variable_alias,{5,0,{0,0}}), {?eh,tc_start,{config_dynamic_SUITE,end_per_suite}}, {?eh,tc_done,{config_dynamic_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, diff --git a/lib/common_test/test/ct_config_SUITE_data/config/config.txt b/lib/common_test/test/ct_config_SUITE_data/config/config.txt index fcbffcd7f3..e4bcc5ba6b 100644 --- a/lib/common_test/test/ct_config_SUITE_data/config/config.txt +++ b/lib/common_test/test/ct_config_SUITE_data/config/config.txt @@ -2,7 +2,8 @@ {gen_cfg, [ {a,a_value}, - {b,b_value} + {b,b_value}, + {c,[{d,d_value}]} ]}. {gen_cfg2, [ diff --git a/lib/common_test/test/ct_config_SUITE_data/config/config.xml b/lib/common_test/test/ct_config_SUITE_data/config/config.xml index 0a3e5f2e31..8eeff1482f 100644 --- a/lib/common_test/test/ct_config_SUITE_data/config/config.xml +++ b/lib/common_test/test/ct_config_SUITE_data/config/config.xml @@ -3,6 +3,7 @@ <gen_cfg> <a>a_value</a> <b>b_value</b> + <c><d>d_value</d></c> </gen_cfg> <gen_cfg2> <c>"Hello, world!"</c> diff --git a/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt b/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt new file mode 100644 index 0000000000..865bf9255a --- /dev/null +++ b/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt @@ -0,0 +1,12 @@ +{x, suite}. +{gen_cfg3, + [ + {l, + [ + {m, + [ + {n, "n"}, + {o, 'o'} + ]} + ]} + ]}. diff --git a/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl b/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl index 8751a2e8f3..d7119d7fde 100644 --- a/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl +++ b/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl @@ -46,7 +46,7 @@ suite() -> {require, gen_cfg3}, {require, alias, gen_cfg}, %% x1 default value - {x1, {x,suite}} + {default_config, x1, {x,suite}} ]. init_per_suite(Config) -> @@ -55,14 +55,24 @@ init_per_suite(Config) -> end_per_suite(_) -> ok. -all() -> [test_get_config_simple, test_get_config_nested, test_default_suitewide, +all() -> [test_get_config_simple, test_get_config_nested, + test_get_config_deep_nested, test_default_suitewide, test_config_name_already_in_use1, test_default_tclocal, test_config_name_already_in_use2, test_alias_tclocal, - test_get_config_undefined]. - -init_per_testcase(_, Config) -> + test_get_config_undefined, + test_require_subvals,test_require_subvals2,test_require_deep_config, + test_shadow_all,test_element,test_shadow_all_element, + test_internal_deep, test_alias_tclocal_nested, + test_alias_tclocal_nested_backward_compat, + test_alias_tclocal_nested_backward_compat_subvals +]. + +init_per_testcase(_,Config) -> Config. +end_per_testcase(test_alias_tclocal_nested_backward_compat, _) -> + os:putenv("COMMON_TEST_ALIAS_TOP",""), + ok; end_per_testcase(_, _) -> ok. @@ -76,6 +86,11 @@ test_get_config_nested(_)-> a_value = ct:get_config({gen_cfg, a}), ok. +%% test getting a deep nested value +test_get_config_deep_nested(_)-> + d_value = ct:get_config({gen_cfg, c, d}), + ok. + %% test suite-wide default value test_default_suitewide(_)-> suite = ct:get_config(x1), @@ -112,12 +127,73 @@ test_config_name_already_in_use2(_) -> %% test aliases test_alias_tclocal() -> [{require,newalias,gen_cfg}]. -test_alias_tclocal(_) -> - A = [{a,a_value},{b,b_value}] = ct:get_config(newalias), +test_alias_tclocal(C) when is_list(C) -> + test_alias_tclocal(newalias); +test_alias_tclocal(Alias) when is_atom(Alias) -> + A = [{a,a_value},{b,b_value},{c,[{d,d_value}]}] = ct:get_config(Alias), A = ct:get_config(gen_cfg), + B = b_value = ct:get_config({Alias,b}), + B = ct:get_config({gen_cfg,b}), + ok. + +%% test nested aliases +test_alias_tclocal_nested() -> + [{require,newalias2,{gen_cfg,c}}]. +test_alias_tclocal_nested(_) -> + A = [{d,d_value}] = ct:get_config(newalias2), + A = ct:get_config({gen_cfg,c}), + B = d_value = ct:get_config({newalias2,d}), + B = ct:get_config({gen_cfg,c,d}), ok. +%% test nested aliases backward compat option +test_alias_tclocal_nested_backward_compat() -> + os:putenv("COMMON_TEST_ALIAS_TOP","true"), + [{require,newalias3,{gen_cfg,c}}]. +test_alias_tclocal_nested_backward_compat(_) -> + test_alias_tclocal(newalias3). + +%% test nested aliases backward compat option +test_alias_tclocal_nested_backward_compat_subvals() -> + [{require,newalias4,{gen_cfg,[c]}}]. +test_alias_tclocal_nested_backward_compat_subvals(_) -> + test_alias_tclocal(newalias4). + %% test for getting undefined variables test_get_config_undefined(_) -> undefined = ct:get_config(y1), ok. + +test_require_subvals() -> + [{require, {gen_cfg,[a,b,c]}}]. +test_require_subvals(_) -> + ok. + +test_require_subvals2() -> + [{require, {gen_cfg,[a,b,c,d]}}]. +test_require_subvals2(_) -> + ct:fail("Test should've been skipped, you shouldn't see this!"), + ok. + +test_require_deep_config() -> + [{require, {gen_cfg3, m, n}}]. +test_require_deep_config(_) -> + ok. + + +test_shadow_all(_) -> + ["n","N"] = ct:get_config({gen_cfg3,l, m, n}, [], [all]). + +test_element(_) -> + {{gen_cfg3,l, m, n},"n"} = ct:get_config({gen_cfg3,l, m, n}, [], [element]). + +test_shadow_all_element(_) -> + [{{gen_cfg3,l, m, n},"n"},{{gen_cfg3,l, m, n},"N"}] = + ct:get_config({gen_cfg3,l, m, n}, [], [all,element]). + +%% The tests below are needed to verify that things like ct:telnet can use +%% nested configs +test_internal_deep(_) -> + "n" = ct:get_config({{gen_cfg3,l,m},n}), + a_value = ct:get_config({{gen_cfg},a}), + undefined = ct:get_config({{gen_cfg3,l,m},p}). diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl index c9ee47e01b..338e76264e 100644 --- a/lib/common_test/test/ct_error_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE.erl @@ -878,11 +878,11 @@ test_events(timetrap_fun) -> {failed,{timetrap_timeout,{'$approx',1000}}}}}, {?eh,test_stats,{0,5,{0,0}}}, {?eh,tc_start,{timetrap_5_SUITE,tc1}}, - {?eh,tc_done,{undefined,undefined,{user_timetrap_error, + {?eh,tc_done,{timetrap_5_SUITE,tc1,{user_timetrap_error, {kaboom,'_'}}}}, {?eh,test_stats,{0,6,{0,0}}}, {?eh,tc_start,{timetrap_5_SUITE,tc2}}, - {?eh,tc_done,{undefined,undefined,{user_timetrap_error, + {?eh,tc_done,{timetrap_5_SUITE,tc2,{user_timetrap_error, {kaboom,'_'}}}}, {?eh,test_stats,{0,7,{0,0}}}, {?eh,tc_start,{timetrap_5_SUITE,tc3}}, @@ -937,7 +937,7 @@ test_events(timetrap_fun) -> {?eh,tc_done,{timetrap_5_SUITE,end_per_suite,ok}}, {?eh,tc_start,{timetrap_6_SUITE,init_per_suite}}, - {?eh,tc_done,{undefined,undefined,{user_timetrap_error, + {?eh,tc_done,{timetrap_6_SUITE,init_per_suite,{user_timetrap_error, {kaboom,'_'}}}}, {?eh,tc_auto_skip,{timetrap_6_SUITE,tc0, {failed,{timetrap_6_SUITE,init_per_suite, diff --git a/lib/common_test/test/ct_misc_1_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE.erl index cb17af9ab5..d2318de445 100644 --- a/lib/common_test/test/ct_misc_1_SUITE.erl +++ b/lib/common_test/test/ct_misc_1_SUITE.erl @@ -106,7 +106,7 @@ beam_me_up(Config) when is_list(Config) -> {Opts,ERPid} = setup([{suite,Suites},{auto_compile,false}], Config), - ok = ct_test_support:run(ct, run_test, [Opts], Config), + {_Ok,_Fail,_Skip} = ct_test_support:run(ct, run_test, [Opts], Config), Events = ct_test_support:get_events(ERPid, Config), ct_test_support:log_events(beam_me_up, diff --git a/lib/common_test/test/ct_netconfc_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE.erl new file mode 100644 index 0000000000..e6e8d5b09c --- /dev/null +++ b/lib/common_test/test/ct_netconfc_SUITE.erl @@ -0,0 +1,124 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_netconfc_SUITE +%%% +%%% Description: +%%% Test ct_netconfc module +%%% +%%%------------------------------------------------------------------- +-module(ct_netconfc_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + default + ]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +default(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "netconfc1_SUITE"), + CfgFile = filename:join(DataDir, "netconfc1.cfg"), + {Opts,ERPid} = setup([{suite,Suite},{config,CfgFile}, + {label,default}], Config), + + ok = execute(default, Opts, ERPid, Config). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +execute(Name, Opts, ERPid, Config) -> + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(Name, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(Name,Config), + ct_test_support:verify_events(TestEvents, Events, Config). + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test,Config) -> + {module,_} = code:load_abs(filename:join(?config(data_dir,Config), + netconfc1_SUITE)), + TCs = netconfc1_SUITE:all(), + code:purge(netconfc1_SUITE), + code:delete(netconfc1_SUITE), + + OneTest = + [{?eh,start_logging,{'DEF','RUNDIR'}}] ++ + [{?eh,tc_done,{netconfc1_SUITE,TC,ok}} || TC <- TCs] ++ + [{?eh,stop_logging,[]}], + + %% 2 tests (ct:run_test + script_start) is default + OneTest ++ OneTest. diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg new file mode 100644 index 0000000000..b431301df6 --- /dev/null +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg @@ -0,0 +1,6 @@ +%% -*- erlang -*- +{netconf1,[{ssh,"127.0.0.1"}, + {port,2060}, + {user,"xxx"}, + {password,"xxx"}]}. +{ct_conn_log,[{ct_netconfc,[{log_type,pretty}]}]}. %overrides args to cth_conn_log diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl new file mode 100644 index 0000000000..d337158bce --- /dev/null +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl @@ -0,0 +1,1132 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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% +%% +%%---------------------------------------------------------------------- +%% File: ct_netconfc_SUITE.erl +%% +%% Description: +%% This file contains the test cases for the ct_netconfc API. +%% +%% @author Support +%% @doc Netconf Client Interface. +%% @end +%%---------------------------------------------------------------------- +%%---------------------------------------------------------------------- +-module(netconfc1_SUITE). +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/src/ct_netconfc.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +-compile(export_all). + +%% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). + +-define(NS,ns). +-define(LOCALHOST, "127.0.0.1"). +-define(SSH_PORT, 2060). + +-define(DEFAULT_SSH_OPTS,[{ssh,?LOCALHOST}, + {port,?SSH_PORT}, + {user,"xxx"}, + {password,"xxx"}]). +-define(DEFAULT_SSH_OPTS(Dir), ?DEFAULT_SSH_OPTS++[{user_dir,Dir}]). + +-define(ok,ok). + +suite() -> + [{ct_hooks, [{cth_conn_log, + [{ct_netconfc,[{log_type,html}, %will be overwritten by config + {hosts,[my_named_connection,netconf1]}] + }] + }] + }]. + +all() -> + case os:find_executable("ssh") of + false -> + {skip, "SSH not installed on host"}; + _ -> + [hello, + hello_from_server_first, + hello_named, + hello_configured, + hello_configured_extraopts, + hello_required, + hello_required_exists, + hello_global_pwd, + hello_no_session_id, + hello_incomp_base_vsn, + hello_no_base_cap, + hello_no_caps, + no_server_hello, + no_client_hello, + get_session_id, + get_capabilities, + faulty_user, + faulty_passwd, + faulty_port, + no_host, + no_port, + invalid_opt, + get, + get_xpath, + get_config, + get_config_xpath, + edit_config, + copy_config, + delete_config, + lock, + unlock, + kill_session, + get_no_such_client, + action, + send_any_rpc, + send_any, + hide_password, + not_proper_xml, + prefixed_namespace, + receive_chunked_data, + timeout_receive_chunked_data, + close_while_waiting_for_chunked_data, + connection_crash, + get_event_streams, + create_subscription, + receive_event + ] + end. + + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +init_per_testcase(_Case, Config) -> + ets:delete_all_objects(ns_tab), + Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +init_per_suite(Config) -> + case catch {crypto:start(), ssh:start()} of + {ok, ok} -> + {ok, _} = get_id_keys(Config), + make_dsa_files(Config), + Server = ?NS:start(?config(data_dir,Config)), + [{server,Server}|Config]; + _ -> + {skip, "Crypto and/or SSH could not be started!"} + end. + +end_per_suite(Config) -> + PrivDir = ?config(priv_dir, Config), + ?NS:stop(?config(server,Config)), + ssh:stop(), + crypto:stop(), + remove_id_keys(PrivDir), + Config. + +hello(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_from_server_first(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(1), + {ok,Client} = ct_netconfc:only_open(?DEFAULT_SSH_OPTS(DataDir)), + ct:sleep(500), + ?NS:expect(hello), + ?ok = ct_netconfc:hello(Client), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_named(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(any_name,DataDir), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_configured() -> + [{require, netconf1}]. +hello_configured(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_configured_success(netconf1,DataDir), + ?NS:expect_do_reply('close-session',close,ok), + {error, {no_such_name,netconf1}} = ct_netconfc:close_session(netconf1), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_configured_extraopts() -> + [{require, netconf1}]. +hello_configured_extraopts(Config) -> + DataDir = ?config(data_dir,Config), + %% Test that the cofiguration overwrites the ExtraOpts parameter + %% to ct_netconfc:open/2. + {ok,Client} = open_configured_success(netconf1,DataDir,[{password,"faulty"}]), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_required() -> + [{require, my_named_connection, netconf1}]. +hello_required(Config) -> + DataDir = ?config(data_dir,Config), + {ok,_Client} = open_configured_success(my_named_connection,DataDir), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(my_named_connection), + ok. + +hello_required_exists() -> + [{require, my_named_connection, netconf1}]. +hello_required_exists(Config) -> + DataDir = ?config(data_dir,Config), + {ok,_Client1} = open_configured_success(my_named_connection,DataDir), + + %% Check that same name can not be used twice + {error,{connection_exists,_Client1}} = + ct_netconfc:open(my_named_connection,[{user_dir,DataDir}]), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(my_named_connection), + timer:sleep(500), + + %% Then check that it can be used again after the first is closed + {ok,_Client2} = open_configured_success(my_named_connection,DataDir), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(my_named_connection), + ok. + +hello_global_pwd(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir,[{user,"any-user"}, + {password,"global-xxx"}]), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_no_session_id(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(no_session_id), + ?NS:expect(no_session_id,hello), + {error,{incorrect_hello,no_session_id_found}} = open(DataDir), + ok. + +hello_incomp_base_vsn(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(1,{base,"1.1"}), + ?NS:expect(hello), + {error,{incompatible_base_capability_vsn,"1.1"}} = open(DataDir), + ok. + +hello_no_base_cap(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(1,no_base), + ?NS:expect(hello), + {error,{incorrect_hello,no_base_capability_found}} = open(DataDir), + ok. + +hello_no_caps(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(1,no_caps), + ?NS:expect(hello), + {error,{incorrect_hello,capabilities_not_found}} = open(DataDir), + ok. + +no_server_hello(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:expect(undefined,hello), + {error,{hello_session_failed,timeout}} = open(DataDir,[{timeout,2000}]), + ok. + +no_client_hello(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(1), + {ok,Client} = ct_netconfc:only_open(?DEFAULT_SSH_OPTS(DataDir)), + + %% Allow server hello to arrive + ct:sleep(500), + + %% Tell server to receive a get request and then die without + %% replying since no hello has been received. (is this correct + %% behavoiur??) + ?NS:expect_do(get,close), + {error,closed} = ct_netconfc:get(Client,whatever), + ok. + +get_session_id(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + 1 = ct_netconfc:get_session_id(Client), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +get_capabilities(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + Caps = ct_netconfc:get_capabilities(Client), + BaseCap = ?NETCONF_BASE_CAP ++ ?NETCONF_BASE_CAP_VSN, + [BaseCap,"urn:ietf:params:netconf:capability:writable-running:1.0" |_] = Caps, + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +faulty_user(Config) -> + DataDir = ?config(data_dir,Config), + {error,{ssh,could_not_connect_to_server, + "Unable to connect using the available authentication methods"}} = + open(DataDir,[{user,"yyy"}]), + ok. + +faulty_passwd(Config) -> + DataDir = ?config(data_dir,Config), + {error,{ssh,could_not_connect_to_server, + "Unable to connect using the available authentication methods"}} = + open(DataDir,[{password,"yyy"}]), + ok. + +faulty_port(Config) -> + DataDir = ?config(data_dir,Config), + {error,{ssh,could_not_connect_to_server,econnrefused}} = + open(DataDir,[{port,2062}]), + ok. + +no_host(Config) -> + DataDir = ?config(data_dir,Config), + Opts = lists:keydelete(ssh,1,?DEFAULT_SSH_OPTS(DataDir)), + {error,no_host_address} = ct_netconfc:open(Opts), + ok. + +no_port(Config) -> + DataDir = ?config(data_dir,Config), + Opts = lists:keydelete(port,1,?DEFAULT_SSH_OPTS(DataDir)), + {error,no_port} = ct_netconfc:open(Opts), + ok. + +invalid_opt(Config) -> + DataDir = ?config(data_dir,Config), + Opts1 = ?DEFAULT_SSH_OPTS(DataDir) ++ [{timeout,invalidvalue}], + {error,{invalid_option,{timeout,invalidvalue}}} = ct_netconfc:open(Opts1), + Opts2 = ?DEFAULT_SSH_OPTS(DataDir) ++ [{some_other_opt,true}], + {error,{invalid_option,{some_other_opt,true}}} = ct_netconfc:open(Opts2), + ok. + +get(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + ?NS:expect_reply('get',{data,Data}), + {ok,Data} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +get_xpath(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + ?NS:expect_reply({'get',xpath},{data,Data}), + {ok,Data} = ct_netconfc:get(Client,{xpath,"/server"}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +get_config(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + ?NS:expect_reply('get-config',{data,Data}), + {ok,Data} = ct_netconfc:get_config(Client,running, + {server,[{xmlns,"myns"}],[]}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +get_config_xpath(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + ?NS:expect_reply({'get-config',xpath},{data,Data}), + {ok,Data} = ct_netconfc:get_config(Client,running,{xpath,"/server"}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +edit_config(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply('edit-config',ok), + ?ok = ct_netconfc:edit_config(Client,running, + {server,[{xmlns,"myns"}], + [{name,["myserver"]}]}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +copy_config(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply('copy-config',ok), + ?ok = ct_netconfc:copy_config(Client,startup,running), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +delete_config(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply('delete-config',ok), + ?ok = ct_netconfc:delete_config(Client,startup), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +lock(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply('lock',ok), + ?ok = ct_netconfc:lock(Client,running), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +unlock(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply('unlock',ok), + ?ok = ct_netconfc:unlock(Client,running), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +kill_session(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + ?NS:hello(2), + ?NS:expect(2,hello), + {ok,_OtherClient} = open(DataDir), + + ?NS:expect_do_reply('kill-session',{kill,2},ok), + ?ok = ct_netconfc:kill_session(Client,2), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + + ok. + +get_no_such_client(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + case ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}) of + {error,no_such_client} -> + ok; + {error,closed} -> + %% Means that the Client process was not terminated before the call. + %% Give it one more go. + {error,no_such_client} = + ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}) + end, + ok. + +action(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{myactionreturn,[{xmlns,"myns"}],["value"]}], + ?NS:expect_reply(action,{data,Data}), + {ok,Data} = ct_netconfc:action(Client,{myaction,[{xmlns,"myns"}],[]}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +send_any_rpc(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + GetConf = {'get-config', + [{source,["running"]}, + {filter,[{type,"subtree"}], + [{server,[{xmlns,"myns"}],[]}]}]}, + ?NS:expect_reply('get-config',{data,Data}), + [{data,?NETCONF_NAMESPACE_ATTR,Data}] = ct_netconfc:send_rpc(Client,GetConf), + + EditConf = {'edit-config', + [{target,["running"]}, + {config,[{server,[{xmlns,"myns"}], + [{name,["myserver"]}]}]}]}, + ?NS:expect_reply('edit-config',ok), + [{ok,?NETCONF_NAMESPACE_ATTR,[]}] = ct_netconfc:send_rpc(Client,EditConf), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +send_any(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + %% Correct get-config rpc + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + RpcAttr1 = ?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}], + RpcGetConf = {rpc,RpcAttr1, + [{'get-config', + [{source,["running"]}, + {filter,[{type,"subtree"}], + [{server,[{xmlns,"myns"}],[]}]}]}]}, + ?NS:expect_reply('get-config',{data,Data}), + {'rpc-reply',RpcAttr1,[{data,_,Data}]} = ct_netconfc:send(Client,RpcGetConf), + + %% Correct edit-config rpc + RpcAttr2 = ?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"2"}], + RpcEditConf = {rpc,RpcAttr2, + [{'edit-config', + [{target,["running"]}, + {config,[{server,[{xmlns,"myns"}], + [{name,["myserver"]}]}]}]}]}, + ?NS:expect_reply('edit-config',ok), + {'rpc-reply',RpcAttr2,[{ok,_,[]}]} = ct_netconfc:send(Client,RpcEditConf), + + %% Send any data + ?NS:expect_reply(any,{ok,[],[]}), + {ok,_,[]} = ct_netconfc:send(Client,{any,[],[]}), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hide_password(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Password = "my_very_secret_password", + Data = [{passwords,[{xmlns,"myns"}], + [{password,[{xmlns,"pwdns"}],[Password]}, + {password,[],[Password]}]}], + ?NS:expect_reply('get',{data,Data}), + ct:capture_start(), % in case of html logging + {ok,Data} = ct_netconfc:get(Client,{passwords,[{xmlns,"myns"}],[]}), + ct:capture_stop(), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + + Log = filename:join(?config(priv_dir,Config),"hide_password-netconf.txt"), + + Text = + case file:read_file(Log) of + {ok,Bin} -> + Bin; + _NoLog -> + %% Assume html logging + list_to_binary(ct:capture_get()) + end, + + nomatch = binary:match(Text,list_to_binary(Password)), + + ok. + +not_proper_xml(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + NS = list_to_binary(?NETCONF_NAMESPACE), + NotProper = <<"<rpc-reply message-id=\"1\" xmlns=\"", + NS/binary,"\"><data></rpc-reply>">>, + ?NS:expect_reply('get',NotProper), + {error,{failed_to_parse_received_data,_}} = + ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +prefixed_namespace(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + NS = list_to_binary(?NETCONF_NAMESPACE), + + %% Test that data element can be properly decoded and that + %% prefixed namespace attributes (exepct the netconf namespace) + %% are forwarded to the content of the data element - i.e. that + %% the xmlns:my is forwarded from the rpc-reply element to the + %% server element below. + Data = <<"<nc:rpc-reply message-id=\"1\" xmlns:nc=\"", + NS/binary,"\" xmlns:my=\"myns\"><nc:data><my:server>", + "<my:name my:lang=\"en\">myserver</my:name></my:server>" + "</nc:data></nc:rpc-reply>">>, + ?NS:expect_reply('get',Data), + {ok,[{'my:server',[{'xmlns:my',"myns"}], + [{'my:name',[{'my:lang',"en"}],["myserver"]}]}]} = + ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), + + Ok = <<"<nc:rpc-reply message-id=\"2\" xmlns:nc=\"", + NS/binary,"\"><nc:ok/></nc:rpc-reply>">>, + ?NS:expect_reply('edit-config',Ok), + ?ok = ct_netconfc:edit_config(Client,running, + {server,[{xmlns,"myns"}], + [{name,["myserver"]}]}), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +%% Test that the client can parse data which is received in chunks, +%% i.e. when the complete rpc-reply is not contained in one single ssh +%% data message. +receive_chunked_data(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + %% Construct the data to return from netconf server + Data = [{servers,[{xmlns,"myns"}], + [{server,[],[{name,[],["server0"]}]}, + {server,[],[{name,[],["server1"]}]}, + {server,[],[{name,[],["server2"]}]}, + {server,[],[{name,[],["server3"]}]}, + {server,[],[{name,[],["server4"]}]}, + {server,[],[{name,[],["server5"]}]}, + {server,[],[{name,[],["server6"]}]}, + {server,[],[{name,[],["server7"]}]}, + {server,[],[{name,[],["server8"]}]}, + {server,[],[{name,[],["server9"]}]}] + }], + Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}], + [{data,Data}]}, + Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)), + Netconf = + <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", + Xml/binary,"\n",?END_TAG/binary>>, + + %% Split the data in some chunks + PartLength = size(Netconf) div 3, + <<Part1:PartLength/binary,Part2:PartLength/binary,Part3:PartLength/binary, + Part4/binary>> = Netconf, + + %% Spawn a process which will wait a bit for the client to send + %% the request (below), then order the server to the chunks of the + %% rpc-reply one by one. + spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1), + timer:sleep(100),?NS:hupp(send,Part2), + timer:sleep(100),?NS:hupp(send,Part3), + timer:sleep(100),?NS:hupp(send,Part4) + end), + + %% Order server to expect a get - then the process above will make + %% sure the rpc-reply is sent. + ?NS:expect('get'), + {ok,Data} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +%% Same as receive_chunked_data, but timeout waiting for last part. +timeout_receive_chunked_data(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + %% Construct the data to return from netconf server + Data = [{servers,[{xmlns,"myns"}], + [{server,[],[{name,[],["server0"]}]}, + {server,[],[{name,[],["server1"]}]}, + {server,[],[{name,[],["server2"]}]}, + {server,[],[{name,[],["server3"]}]}, + {server,[],[{name,[],["server4"]}]}, + {server,[],[{name,[],["server5"]}]}, + {server,[],[{name,[],["server6"]}]}, + {server,[],[{name,[],["server7"]}]}, + {server,[],[{name,[],["server8"]}]}, + {server,[],[{name,[],["server9"]}]}] + }], + Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}], + [{data,Data}]}, + Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)), + Netconf = + <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", + Xml/binary,"\n",?END_TAG/binary>>, + + %% Split the data in some chunks + PartLength = size(Netconf) div 3, + <<Part1:PartLength/binary,Part2:PartLength/binary,_Part3:PartLength/binary, + _Part4/binary>> = Netconf, + + %% Spawn a process which will wait a bit for the client to send + %% the request (below), then order the server to the chunks of the + %% rpc-reply one by one. + spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1), + timer:sleep(100),?NS:hupp(send,Part2) + end), + + %% Order server to expect a get - then the process above will make + %% sure the rpc-reply is sent - but only a part of it - then timeout. + ?NS:expect('get'), + {error,timeout} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},2000), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +%% Same as receive_chunked_data, but timeout waiting for last part. +close_while_waiting_for_chunked_data(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + %% Construct the data to return from netconf server + Data = [{servers,[{xmlns,"myns"}], + [{server,[],[{name,[],["server0"]}]}, + {server,[],[{name,[],["server1"]}]}, + {server,[],[{name,[],["server2"]}]}, + {server,[],[{name,[],["server3"]}]}, + {server,[],[{name,[],["server4"]}]}, + {server,[],[{name,[],["server5"]}]}, + {server,[],[{name,[],["server6"]}]}, + {server,[],[{name,[],["server7"]}]}, + {server,[],[{name,[],["server8"]}]}, + {server,[],[{name,[],["server9"]}]}] + }], + Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}], + [{data,Data}]}, + Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)), + Netconf = + <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", + Xml/binary,"\n",?END_TAG/binary>>, + + %% Split the data in some chunks + PartLength = size(Netconf) div 3, + <<Part1:PartLength/binary,Part2:PartLength/binary,_Part3:PartLength/binary, + _Part4/binary>> = Netconf, + + %% Spawn a process which will wait a bit for the client to send + %% the request (below), then order the server to the chunks of the + %% rpc-reply one by one. + spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1), + timer:sleep(100),?NS:hupp(send,Part2), + timer:sleep(100),?NS:hupp(kill) + end), + + %% Order server to expect a get - then the process above will make + %% sure the rpc-reply is sent - but only a part of it - then close. + ?NS:expect('get'), + {error,closed} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},2000), + ok. + +connection_crash(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + %% Test that if the test survives killing the connection + %% process. Earlier this caused ct_util_server to terminate, and + %% this aborting the complete test run. + spawn(fun() -> timer:sleep(500),exit(Client,kill) end), + ?NS:expect(get), + {error,{closed,killed}}=ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), + ok. + +get_event_streams(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + StreamNames = ["NETCONF","stream1","stream2"], + Streams = [{N,[{description,"descr of " ++ N}]} || N <- StreamNames], + StreamsXml = [{stream,[{name,[N]}|[{Tag,[Value]} || {Tag,Value} <- Data]]} + || {N,Data} <- Streams], + ReplyData = [{netconf,?NETMOD_NOTIF_NAMESPACE_ATTR,[{streams,StreamsXml}]}], + ?NS:expect_reply('get',{data,ReplyData}), + {ok,Streams} = ct_netconfc:get_event_streams(Client,StreamNames), + + ?NS:expect_reply('get',{data,ReplyData}), + {ok,Streams} = ct_netconfc:get_event_streams(Client,StreamNames,5000), + + ?NS:expect('get'), + {error,timeout} = ct_netconfc:get_event_streams(Client,100), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +create_subscription(Config) -> + DataDir = ?config(data_dir,Config), + + %% All defaults + {ok,Client1} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream]},ok), + ?ok = ct_netconfc:create_subscription(Client1), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client1), + + %% All defaults with timeout + {ok,Client1a} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream]},ok), + ?ok = ct_netconfc:create_subscription(Client1a,5000), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client1a), + + %% All defaults timing out + {ok,Client1b} = open_success(DataDir), + ?NS:expect({'create-subscription',[stream]}), + {error,timeout} = ct_netconfc:create_subscription(Client1b,100), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client1b), + + %% Stream + {ok,Client2} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream]},ok), + Stream = "some_stream", + ?ok = ct_netconfc:create_subscription(Client2,Stream), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client2), + + %% Filter + {ok,Client3} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,filter]},ok), + Filter = {notification,?NETMOD_NOTIF_NAMESPACE_ATTR, + [eventTime]}, + ?ok = ct_netconfc:create_subscription(Client3,Filter), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client3), + + %% Filter with timeout + {ok,Client3a} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,filter]},ok), + ?ok = ct_netconfc:create_subscription(Client3a,Filter,5000), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client3a), + + %% Filter timing out + {ok,Client3b} = open_success(DataDir), + ?NS:expect({'create-subscription',[stream,filter]}), + {error,timeout}=ct_netconfc:create_subscription(Client3b,Filter,100), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client3b), + + %% Stream and filter + {ok,Client4} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,filter]},ok), + ?ok = ct_netconfc:create_subscription(Client4,Stream,Filter), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client4), + + %% Start/stop time + {ok,Client5} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok), + StartTime = xs_datetime({D,{H,M,S}}= calendar:local_time()), + StopTime = xs_datetime({D,{H+2,M,S}}), + ?ok = ct_netconfc:create_subscription(Client5,StartTime,StopTime), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client5), + + %% Start/stop time with timeout + {ok,Client5a} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok), + ?ok = ct_netconfc:create_subscription(Client5a,StartTime,StopTime,5000), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client5a), + + %% Start/stop time timing out + {ok,Client5b} = open_success(DataDir), + ?NS:expect({'create-subscription',[stream,startTime,stopTime]}), + {error,timeout} = + ct_netconfc:create_subscription(Client5b,StartTime,StopTime,100), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client5b), + + %% Stream and start/stop time + {ok,Client6} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok), + ?ok = ct_netconfc:create_subscription(Client6,Stream,StartTime,StopTime), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client6), + + %% Filter and start/stop time + {ok,Client7} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,filter,startTime,stopTime]}, + ok), + ?ok = ct_netconfc:create_subscription(Client7,Filter, + StartTime,StopTime), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client7), + + %% Stream, filter and start/stop time + {ok,Client8} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,filter,startTime,stopTime]}, + ok), + ?ok = ct_netconfc:create_subscription(Client8,Stream,Filter, + StartTime,StopTime), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client8), + + ok. + +receive_event(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream]},ok), + ?ok = ct_netconfc:create_subscription(Client), + + ?NS:hupp(send_event), + + receive + %% Matching ?NS:make_msg(event) + {notification,?NETCONF_NOTIF_NAMESPACE_ATTR, + [{eventTime,[],[_Time]}, + {event,[{xmlns,"http://my.namespaces.com/event"}], + [{severity,_,_}, + {description,_,_}]}]} -> + ok; + Other -> + ct:fail({got_unexpected_while_waiting_for_event, Other}) + after 3000 -> + ct:fail(timeout_waiting_for_event) + end, + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + + ok. + +%%%----------------------------------------------------------------- + +break(_Config) -> + test_server:break("break test case"). + +br() -> + test_server:break(""). + +%%%----------------------------------------------------------------- +%% Open a netconf session which is not specified in a config file +open_success(Dir) -> + open_success(Dir,[]). + +%% Open a netconf session which is not specified in a config file, and +%% give som extra options in addition to the test defaults. +open_success(Dir,ExtraOpts) when is_list(Dir), is_list(ExtraOpts) -> + ?NS:hello(1), % tell server to send hello with session id 1 + ?NS:expect(hello), % tell server to expect a hello message from client + open(Dir,ExtraOpts); + +%% Open a named netconf session which is not specified in a config file +open_success(KeyOrName,Dir) when is_atom(KeyOrName), is_list(Dir) -> + ?NS:hello(1), + ?NS:expect(hello), + ct_netconfc:open(KeyOrName,?DEFAULT_SSH_OPTS(Dir)). + +open(Dir) -> + open(Dir,[]). +open(Dir,ExtraOpts) -> + Opts = lists:ukeymerge(1,lists:keysort(1,ExtraOpts), + lists:keysort(1,?DEFAULT_SSH_OPTS(Dir))), + ct_netconfc:open(Opts). + +%%%----------------------------------------------------------------- +%%% Open a netconf session which is specified in a config file +%%% KeyOrName is the config key (server_id()) or name given in a +%%% require statement (target_name()). +open_configured_success(KeyOrName,Dir) when is_atom(KeyOrName) -> + open_configured_success(KeyOrName,Dir,[]). +open_configured_success(KeyOrName,Dir,ExtraOpts) when is_atom(KeyOrName) -> + ?NS:hello(1), + ?NS:expect(hello), + ct_netconfc:open(KeyOrName,[{user_dir,Dir}|ExtraOpts]). + +%%%----------------------------------------------------------------- +%%% Convert erlang datetime to the simplest variant of XML dateTime +xs_datetime({{Y,M,D},{H,Mi,S}}) -> + lists:flatten( + io_lib:format("~p-~s-~sT~s:~s:~s",[Y,pad(M),pad(D),pad(H),pad(Mi),pad(S)])). + +pad(I) when I<10 -> + "0"++integer_to_list(I); +pad(I) -> + integer_to_list(I). + + +%%%----------------------------------------------------------------- +%%% BEGIN SSH key management +%% copy private keys to given dir from ~/.ssh +get_id_keys(Config) -> + DstDir = ?config(priv_dir, Config), + SrcDir = filename:join(os:getenv("HOME"), ".ssh"), + RsaOk = copyfile(SrcDir, DstDir, "id_rsa"), + DsaOk = copyfile(SrcDir, DstDir, "id_dsa"), + case {RsaOk, DsaOk} of + {{ok, _}, {ok, _}} -> {ok, both}; + {{ok, _}, _} -> {ok, rsa}; + {_, {ok, _}} -> {ok, dsa}; + {Error, _} -> Error + end. + +%% Remove later on. Use make_dsa_files instead. +remove_id_keys(Config) -> + Dir = ?config(priv_dir, Config), + file:delete(filename:join(Dir, "id_rsa")), + file:delete(filename:join(Dir, "id_dsa")). + + +make_dsa_files(Config) -> + make_dsa_files(Config, rfc4716_public_key). +make_dsa_files(Config, Type) -> + {DSA, EncodedKey} = gen_dsa(128, 20), + PKey = DSA#'DSAPrivateKey'.y, + P = DSA#'DSAPrivateKey'.p, + Q = DSA#'DSAPrivateKey'.q, + G = DSA#'DSAPrivateKey'.g, + Dss = #'Dss-Parms'{p=P, q=Q, g=G}, + {ok, Hostname} = inet:gethostname(), + {ok, {A, B, C, D}} = inet:getaddr(Hostname, inet), + IP = lists:concat([A, ".", B, ".", C, ".", D]), + Attributes = [], % Could be [{comment,"user@" ++ Hostname}], + HostNames = [{hostnames,[IP, IP]}], + PublicKey = [{{PKey, Dss}, Attributes}], + KnownHosts = [{{PKey, Dss}, HostNames}], + + KnownHostsEnc = public_key:ssh_encode(KnownHosts, known_hosts), + KnownHosts = public_key:ssh_decode(KnownHostsEnc, known_hosts), + + PublicKeyEnc = public_key:ssh_encode(PublicKey, Type), + + SystemTmpDir = ?config(data_dir, Config), + filelib:ensure_dir(SystemTmpDir), + file:make_dir(SystemTmpDir), + + DSAFile = filename:join(SystemTmpDir, "ssh_host_dsa_key.pub"), + file:delete(DSAFile), + + DSAPrivateFile = filename:join(SystemTmpDir, "ssh_host_dsa_key"), + file:delete(DSAPrivateFile), + + KHFile = filename:join(SystemTmpDir, "known_hosts"), + file:delete(KHFile), + + PemBin = public_key:pem_encode([EncodedKey]), + + file:write_file(DSAFile, PublicKeyEnc), + file:write_file(KHFile, KnownHostsEnc), + file:write_file(DSAPrivateFile, PemBin), + ok. + +%%-------------------------------------------------------------------- +%% Creates a dsa key (OBS: for testing only) +%% the sizes are in bytes +%% gen_dsa(::integer()) -> {::atom(), ::binary(), ::opaque()} +%%-------------------------------------------------------------------- +gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> + Key = gen_dsa2(LSize, NSize), + {Key, encode_key(Key)}. + +encode_key(Key = #'RSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key), + {'RSAPrivateKey', list_to_binary(Der), not_encrypted}; +encode_key(Key = #'DSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), + {'DSAPrivateKey', list_to_binary(Der), not_encrypted}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% DSA key generation (OBS: for testing only) +%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm +%% and the fips_186-3.pdf +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +gen_dsa2(LSize, NSize) -> + Q = prime(NSize), %% Choose N-bit prime Q + X0 = prime(LSize), + P0 = prime((LSize div 2) +1), + + %% Choose L-bit prime modulus P such that p-1 is a multiple of q. + case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of + error -> + gen_dsa2(LSize, NSize); + P -> + G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. + %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used. + + X = prime(20), %% Choose x by some random method, where 0 < x < q. + Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p. + + #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X} + end. + +%% See fips_186-3.pdf +dsa_search(T, P0, Q, Iter) when Iter > 0 -> + P = 2*T*Q*P0 + 1, + case is_prime(crypto:mpint(P), 50) of + true -> P; + false -> dsa_search(T+1, P0, Q, Iter-1) + end; +dsa_search(_,_,_,_) -> + error. + + +%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +prime(ByteSize) -> + Rand = odd_rand(ByteSize), + crypto:erlint(prime_odd(Rand, 0)). + +prime_odd(Rand, N) -> + case is_prime(Rand, 50) of + true -> + Rand; + false -> + NotPrime = crypto:erlint(Rand), + prime_odd(crypto:mpint(NotPrime+2), N+1) + end. + +%% see http://en.wikipedia.org/wiki/Fermat_primality_test +is_prime(_, 0) -> true; +is_prime(Candidate, Test) -> + CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate), + case crypto:mod_exp(CoPrime, Candidate, Candidate) of + CoPrime -> is_prime(Candidate, Test-1); + _ -> false + end. + +odd_rand(Size) -> + Min = 1 bsl (Size*8-1), + Max = (1 bsl (Size*8))-1, + odd_rand(crypto:mpint(Min), crypto:mpint(Max)). + +odd_rand(Min,Max) -> + Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max), + BitSkip = (Sz+4)*8-1, + case Rand of + Odd = <<_:BitSkip, 1:1>> -> Odd; + Even = <<_:BitSkip, 0:1>> -> + crypto:mpint(crypto:erlint(Even)+1) + end. + +copyfile(SrcDir, DstDir, Fn) -> + file:copy(filename:join(SrcDir, Fn), + filename:join(DstDir, Fn)). + +%%% END SSH key management +%%%----------------------------------------------------------------- diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl new file mode 100644 index 0000000000..2427f37f52 --- /dev/null +++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl @@ -0,0 +1,555 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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% +%% +%%---------------------------------------------------------------------- +%% A netconf server used for testing of netconfc +-module(ns). + +%-compile(export_all). +-include_lib("common_test/src/ct_netconfc.hrl"). + + +%%%----------------------------------------------------------------- +%%% API +-export([start/1, + stop/1, + hello/1, + hello/2, + expect/1, + expect/2, + expect_reply/2, + expect_reply/3, + expect_do/2, + expect_do/3, + expect_do_reply/3, + expect_do_reply/4, + hupp/1, + hupp/2]). + +%%%----------------------------------------------------------------- +%%% ssh_channel callbacks +-export([init/1, + terminate/2, + handle_ssh_msg/2, + handle_msg/2]). + +%%%----------------------------------------------------------------- +%% Server specifications +-define(SERVER_DATA_NAMESPACE, "ClientTest"). +-define(CAPABILITIES,?CAPABILITIES_VSN("1.0")). +-define(CAPABILITIES_VSN(Vsn), + [ + ?NETCONF_BASE_CAP ++ Vsn, + "urn:ietf:params:netconf:capability:writable-running:1.0", + "urn:ietf:params:netconf:capability:candidate:1.0", + "urn:ietf:params:netconf:capability:confirmed-commit:1.0", + "urn:ietf:params:netconf:capability:rollback-on-error:1.0", + "urn:ietf:params:netconf:capability:startup:1.0", + "urn:ietf:params:netconf:capability:url:1.0", + "urn:ietf:params:netconf:capability:xpath:1.0", + "urn:ietf:params:netconf:capability:notification:1.0", + "urn:ietf:params:netconf:capability:interleave:1.0", + ?ACTION_NAMESPACE, + ?SERVER_DATA_NAMESPACE + ]). +-define(SSH_PORT, 2060). +-define(ssh_config(Dir),[{port, ?SSH_PORT}, + {interface, {127,0,0,1}}, + {system_dir, Dir}, + {user_dir, Dir}, + {user_passwords, [{"xxx","xxx"}]}, + {password, "global-xxx"}]). + +%% Some help for debugging +%-define(dbg(F,A),io:format(F,A)). +-define(dbg(F,A),ok). +-define(dbg_event(Event,Expect), + ?dbg("Event: ~p~nExpected: ~p~n",[Event,Expect])). + +%% State +-record(session, {cb, + connection, + buffer = <<>>, + session_id}). + + +%%%----------------------------------------------------------------- +%%% API + +%% Start the netconf server and use the given directory as system_dir +%% and user_dir +start(Dir) -> + spawn(fun() -> init_server(Dir) end). + +%% Stop the netconf server +stop(Pid) -> + Pid ! {stop,self()}, + receive stopped -> ok end. + +%% Set the session id for the hello message. +%% If this is not called prior to starting the session, no hello +%% message will be sent. +%% 'Stuff' indicates some special handling to e.g. provoke error cases +hello(SessionId) -> + hello(SessionId,undefined). +hello(SessionId,Stuff) -> + insert(hello,{SessionId,Stuff}). + +%% Tell server to expect the given message without doing any further +%% actions. To be called directly before sending a request. +expect(Expect) -> + expect_do_reply(Expect,undefined,undefined). +expect(SessionId,Expect) -> + expect_do_reply(SessionId,Expect,undefined,undefined). + +%% Tell server to expect the given message and reply with the give +%% reply. To be called directly before sending a request. +expect_reply(Expect,Reply) -> + expect_do_reply(Expect,undefined,Reply). +expect_reply(SessionId,Expect,Reply) -> + expect_do_reply(SessionId,Expect,undefined,Reply). + +%% Tell server to expect the given message and perform an action. To +%% be called directly before sending a request. +expect_do(Expect,Do) -> + expect_do_reply(Expect,Do,undefined). +expect_do(SessionId,Expect,Do) -> + expect_do_reply(SessionId,Expect,Do,undefined). + +%% Tell server to expect the given message, perform an action and +%% reply with the given reply. To be called directly before sending a +%% request. +expect_do_reply(Expect,Do,Reply) -> + add_expect(1,{Expect,Do,Reply}). +expect_do_reply(SessionId,Expect,Do,Reply) -> + add_expect(SessionId,{Expect,Do,Reply}). + +%% Hupp the server - i.e. tell it to do something - +%% e.g. hupp(send_event) will cause send_event(State) to be called on +%% the session channel process. +hupp(send_event) -> + hupp(send,[make_msg(event)]); +hupp(kill) -> + hupp(1,fun hupp_kill/1,[]). + +hupp(send,Data) -> + hupp(1,fun hupp_send/2,[Data]). + +hupp(SessionId,Fun,Args) when is_function(Fun) -> + [{_,Pid}] = lookup({channel_process,SessionId}), + Pid ! {hupp,Fun,Args}. + +%%%----------------------------------------------------------------- +%%% Main loop of the netconf server +init_server(Dir) -> + register(main_ns_proc,self()), + ets:new(ns_tab,[set,named_table,public]), + Config = ?ssh_config(Dir), + {_,Host} = lists:keyfind(interface, 1, Config), + {_,Port} = lists:keyfind(port, 1, Config), + Opts = lists:filter(fun({Key,_}) -> + lists:member(Key,[system_dir, + password, + user_passwords, + pwdfun]) + end, + Config), + {ok, Daemon} = + ssh:daemon(Host, Port, + [{subsystems,[{"netconf",{?MODULE,[]}}]} + |Opts]), + loop(Daemon). + +loop(Daemon) -> + receive + {stop,From} -> + ssh:stop_daemon(Daemon), + From ! stopped; + {table_trans,Fun,Args,From} -> + %% Simple transaction mechanism for ets table + R = apply(Fun,Args), + From ! {table_trans_done,R}, + loop(Daemon) + end. + +%%---------------------------------------------------------------------- +%% Behaviour callback functions (ssh_channel) +%%---------------------------------------------------------------------- +init([]) -> + {ok, undefined}. + +terminate(_Reason, _State) -> + ok. + +handle_ssh_msg({ssh_cm,CM,{data, Ch, _Type = 0, Data}}, State) -> + %% io:format("~p~n",[{self(),Data,CM,Ch,State}]), + data_for_channel(CM, Ch, Data, State); +handle_ssh_msg({ssh_cm,CM,{closed, Ch}}, State) -> + %% erlang:display({self(),closed,CM,Ch,State}), + stop_channel(CM, Ch, State); +handle_ssh_msg({ssh_cm,CM,{eof, Ch}}, State) -> + %% erlang:display({self(),eof,CM,Ch,State}), + data_for_channel(CM,Ch, <<>>, State). + + +handle_msg({'EXIT', _Pid, _Reason}, State) -> + {ok, State}; +handle_msg({ssh_channel_up,Ch,CM},undefined) -> + %% erlang:display({self(),up,CM,Ch}), + ConnRef = {CM,Ch}, + SessionId = maybe_hello(ConnRef), + insert({channel_process,SessionId},self()), % used to hupp the server + {ok, #session{connection = ConnRef, + session_id = SessionId}}; +handle_msg({hupp,Fun,Args},State) -> + {ok,apply(Fun,Args ++ [State])}. + +data_for_channel(CM, Ch, Data, State) -> + try data(Data, State) of + {ok, NewState} -> + case erase(stop) of + true -> + stop_channel(CM, Ch, NewState); + _ -> + {ok, NewState} + end + catch + Class:Reason -> + Stacktrace = erlang:get_stacktrace(), + error_logger:error_report([{?MODULE, data_for_channel}, + {request, Data}, + {buffer, State#session.buffer}, + {reason, {Class, Reason}}, + {stacktrace, Stacktrace}]), + stop_channel(CM, Ch, State) + end. + +data(Data, State = #session{connection = ConnRef, + buffer = Buffer, + session_id = SessionId}) -> + AllData = <<Buffer/binary,Data/binary>>, + case find_endtag(AllData) of + {ok,Msgs,Rest} -> + [check_expected(SessionId,ConnRef,Msg) || Msg <- Msgs], + {ok,State#session{buffer=Rest}}; + need_more -> + {ok,State#session{buffer=AllData}} + end. + +stop_channel(CM, Ch, State) -> + ssh:close(CM), + {stop, Ch, State}. + + +%%%----------------------------------------------------------------- +%%% Functions to trigg via hupp/1: + +%% Send data spontaneously - e.g. an event +hupp_send(Data,State = #session{connection = ConnRef}) -> + send(ConnRef,Data), + State. +hupp_kill(State = #session{connection = ConnRef}) -> + kill(ConnRef), + State. + +%%%----------------------------------------------------------------- +%%% Internal functions + + +%%% Send ssh data to the client +send({CM,Ch},Data) -> + ssh_connection:send(CM, Ch, Data). + +%%% Kill ssh connection +kill({CM,_Ch}) -> + ssh:close(CM). + +add_expect(SessionId,Add) -> + table_trans(fun do_add_expect/2,[SessionId,Add]). + +table_trans(Fun,Args) -> + S = self(), + case whereis(main_ns_proc) of + S -> + apply(Fun,Args); + Pid -> + Pid ! {table_trans,Fun,Args,self()}, + receive + {table_trans_done,Result} -> + Result + after 5000 -> + exit(table_trans_timeout) + end + end. + +do_add_expect(SessionId,Add) -> + case lookup({expect,SessionId}) of + [] -> + insert({expect,SessionId},[Add]); + [{_,First}] -> + insert({expect,SessionId},First ++ [Add]) + end, + ok. + +do_get_expect(SessionId) -> + case lookup({expect,SessionId}) of + [{_,[{Expect,Do,Reply}|Rest]}] -> + insert({expect,SessionId},Rest), + {Expect,Do,Reply}; + _ -> + error + end. + +insert(Key,Value) -> + ets:insert(ns_tab,{Key,Value}). +lookup(Key) -> + ets:lookup(ns_tab,Key). + +maybe_hello(ConnRef) -> + case lookup(hello) of + [{hello,{SessionId,Stuff}}] -> + %% erlang:display({SessionId,Stuff}), + ets:delete(ns_tab,hello), + insert({session,SessionId},ConnRef), + reply(ConnRef,{hello,SessionId,Stuff}), + SessionId; + [] -> + undefined + end. + +find_endtag(Data) -> + case binary:split(Data,[?END_TAG],[global]) of + [Data] -> + need_more; + Msgs -> + {ok,lists:sublist(Msgs,length(Msgs)-1),lists:last(Msgs)} + end. + +check_expected(SessionId,ConnRef,Msg) -> + %% io:format("~p~n",[{check_expected,SessionId,Msg}]), + case table_trans(fun do_get_expect/1,[SessionId]) of + {Expect,Do,Reply} -> + %% erlang:display({got,io_lib:format("~s",[Msg])}), + %% erlang:display({expected,Expect}), + match(Msg,Expect), + do(ConnRef, Do), + reply(ConnRef,Reply); + error -> + timer:sleep(1000), + exit({error,{got_unexpected,SessionId,Msg,ets:tab2list(ns_tab)}}) + end. + +match(Msg,Expect) -> + ?dbg("Match: ~p~n",[Msg]), + {ok,ok,<<>>} = xmerl_sax_parser:stream(Msg,[{event_fun,fun event/3}, + {event_state,Expect}]). + +event(Event,_Loc,Expect) -> + ?dbg_event(Event,Expect), + event(Event,Expect). + +event(startDocument,Expect) -> match(Expect); +event({startElement,_,Name,_,Attrs},[{se,Name}|Match]) -> + msg_id(Name,Attrs), + Match; +event({startElement,_,Name,_,Attrs},[ignore,{se,Name}|Match]) -> + msg_id(Name,Attrs), + Match; +event({startElement,_,Name,_,Attrs},[{se,Name,As}|Match]) -> + msg_id(Name,Attrs), + match_attrs(Name,As,Attrs), + Match; +event({startElement,_,Name,_,Attrs},[ignore,{se,Name,As}|Match]) -> + msg_id(Name,Attrs), + match_attrs(Name,As,Attrs), + Match; +event({startPrefixMapping,_,Ns},[{ns,Ns}|Match]) -> Match; +event({startPrefixMapping,_,Ns},[ignore,{ns,Ns}|Match]) -> Match; +event({endPrefixMapping,_},Match) -> Match; +event({endElement,_,Name,_},[{ee,Name}|Match]) -> Match; +event({endElement,_,Name,_},[ignore,{ee,Name}|Match]) -> Match; +event(endDocument,Match) when Match==[]; Match==[ignore] -> ok; +event(_,[ignore|_]=Match) -> Match; +event(Event,Match) -> throw({nomatch,{Event,Match}}). + +msg_id("rpc",Attrs) -> + case lists:keyfind("message-id",3,Attrs) of + {_,_,_,Str} -> put(msg_id,Str); + false -> erase(msg_id) + end; +msg_id(_,_) -> + ok. + +match_attrs(Name,[{Key,Value}|As],Attrs) -> + case lists:keyfind(atom_to_list(Key),3,Attrs) of + {_,_,_,Value} -> match_attrs(Name,As,Attrs); + false -> throw({missing_attr,Key,Name,Attrs}); + _ -> throw({faulty_attr_value,Key,Name,Attrs}) + end; +match_attrs(_,[],_) -> + ok. + +do(ConnRef, close) -> + ets:match_delete(ns_tab,{{session,'_'},ConnRef}), + put(stop,true); +do(_ConnRef, {kill,SessionId}) -> + case lookup({session,SessionId}) of + [{_,Owner}] -> + ets:delete(ns_tab,{session,SessionId}), + kill(Owner); + _ -> + exit({no_session_to_kill,SessionId}) + end; +do(_, undefined) -> + ok. + +reply(_,undefined) -> + ?dbg("no reply~n",[]), + ok; +reply(ConnRef,Reply) -> + ?dbg("Reply: ~p~n",[Reply]), + send(ConnRef, make_msg(Reply)). + +from_simple(Simple) -> + list_to_binary(xmerl:export_simple_element(Simple,xmerl_xml)). + +xml(Content) -> + <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", + Content/binary,"\n",?END_TAG/binary>>. + +rpc_reply(Content) when is_binary(Content) -> + MsgId = case erase(msg_id) of + undefined -> <<>>; + Id -> list_to_binary([" message-id=\"",Id,"\""]) + end, + <<"<rpc-reply xmlns=\"",?NETCONF_NAMESPACE,"\"",MsgId/binary,">\n", + Content/binary,"\n</rpc-reply>">>; +rpc_reply(Content) -> + rpc_reply(list_to_binary(Content)). + +session_id(no_session_id) -> + <<>>; +session_id(SessionId0) -> + SessionId = list_to_binary(integer_to_list(SessionId0)), + <<"<session-id>",SessionId/binary,"</session-id>\n">>. + +capabilities(undefined) -> + CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] + || C <- ?CAPABILITIES]), + <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>; +capabilities({base,Vsn}) -> + CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] + || C <- ?CAPABILITIES_VSN(Vsn)]), + <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>; +capabilities(no_base) -> + [_|Caps] = ?CAPABILITIES, + CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] || C <- Caps]), + <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>; +capabilities(no_caps) -> + <<>>. + +%%%----------------------------------------------------------------- +%%% Match received netconf message from the client. Add a new clause +%%% for each new message to recognize. The clause argument shall match +%%% the Expect argument in expect/1, expect_reply/2 or +%%% expect_do_reply/3. +%%% +%%% match(term()) -> [Match]. +%%% Match = ignore | {se,Name} | {se,Name,Attrs} | {ee,Name} | {ns,Namespace} +%%% Name = string() +%%% Attrs = [{atom(),string()}] +%%% Namespace = string() +%%% +%%% 'se' means start element, 'ee' means end element - i.e. to match +%%% an XML element you need one 'se' entry and one 'ee' entry with the +%%% same name in the match list. +match(hello) -> + [ignore,{se,"hello"},ignore,{ee,"hello"},ignore]; +match('close-session') -> + [ignore,{se,"rpc"},{se,"close-session"}, + {ee,"close-session"},{ee,"rpc"},ignore]; +match('edit-config') -> + [ignore,{se,"rpc"},{se,"edit-config"},{se,"target"},ignore,{ee,"target"}, + {se,"config"},ignore,{ee,"config"},{ee,"edit-config"},{ee,"rpc"},ignore]; +match('get') -> + match({get,subtree}); +match({'get',FilterType}) -> + [ignore,{se,"rpc"},{se,"get"},{se,"filter",[{type,atom_to_list(FilterType)}]}, + ignore,{ee,"filter"},{ee,"get"},{ee,"rpc"},ignore]; +match('get-config') -> + match({'get-config',subtree}); +match({'get-config',FilterType}) -> + [ignore,{se,"rpc"},{se,"get-config"},{se,"source"},ignore,{ee,"source"}, + {se,"filter",[{type,atom_to_list(FilterType)}]},ignore,{ee,"filter"}, + {ee,"get-config"},{ee,"rpc"},ignore]; +match('copy-config') -> + [ignore,{se,"rpc"},{se,"copy-config"},{se,"target"},ignore,{ee,"target"}, + {se,"source"},ignore,{ee,"source"},{ee,"copy-config"},{ee,"rpc"},ignore]; +match('delete-config') -> + [ignore,{se,"rpc"},{se,"delete-config"},{se,"target"},ignore,{ee,"target"}, + {ee,"delete-config"},{ee,"rpc"},ignore]; +match('lock') -> + [ignore,{se,"rpc"},{se,"lock"},{se,"target"},ignore,{ee,"target"}, + {ee,"lock"},{ee,"rpc"},ignore]; +match('unlock') -> + [ignore,{se,"rpc"},{se,"unlock"},{se,"target"},ignore,{ee,"target"}, + {ee,"unlock"},{ee,"rpc"},ignore]; +match('kill-session') -> + [ignore,{se,"rpc"},{se,"kill-session"},{se,"session-id"},ignore, + {ee,"session-id"},{ee,"kill-session"},{ee,"rpc"},ignore]; +match(action) -> + [ignore,{se,"rpc"},{ns,?ACTION_NAMESPACE},{se,"action"},{se,"data"},ignore, + {ee,"data"},{ee,"action"},{ee,"rpc"},ignore]; +match({'create-subscription',Content}) -> + [ignore,{se,"rpc"},{ns,?NETCONF_NOTIF_NAMESPACE}, + {se,"create-subscription"}] ++ + lists:flatmap(fun(X) -> + [{se,atom_to_list(X)},ignore,{ee,atom_to_list(X)}] + end, Content) ++ + [{ee,"create-subscription"},{ee,"rpc"},ignore]; +match(any) -> + [ignore]. + + + +%%%----------------------------------------------------------------- +%%% Make message to send to the client. +%%% Add a new clause for each new message that shall be sent. The +%%% clause shall match the Reply argument in expect_reply/2 or +%%% expect_do_reply/3. +make_msg({hello,SessionId,Stuff}) -> + SessionIdXml = session_id(SessionId), + CapsXml = capabilities(Stuff), + xml(<<"<hello xmlns=\"",?NETCONF_NAMESPACE,"\">\n",CapsXml/binary, + SessionIdXml/binary,"</hello>">>); +make_msg(ok) -> + xml(rpc_reply("<ok/>")); +make_msg({data,Data}) -> + xml(rpc_reply(from_simple({data,Data}))); +make_msg(event) -> + xml(<<"<notification xmlns=\"",?NETCONF_NOTIF_NAMESPACE,"\">" + "<eventTime>2012-06-14T14:50:54+02:00</eventTime>" + "<event xmlns=\"http://my.namespaces.com/event\">" + "<severity>major</severity>" + "<description>Something terrible happened</description>" + "</event>" + "</notification>">>); +make_msg(Xml) when is_binary(Xml) -> + xml(Xml); +make_msg(Simple) when is_tuple(Simple) -> + xml(from_simple(Simple)). diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl index 423cb2999b..7704a29768 100644 --- a/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl +++ b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl @@ -1,11 +1,21 @@ -%%%------------------------------------------------------------------- -%%% @author Peter Andersson <[email protected]> -%%% @copyright (C) 2012, Peter Andersson -%%% @doc -%%% -%%% @end -%%% Created : 23 Jan 2012 by Peter Andersson <[email protected]> -%%%------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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(priv_dir_SUITE). -compile(export_all). diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 02246b5763..80cca4a1cc 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -35,6 +35,8 @@ verify_events/3, reformat/2, log_events/4, join_abs_dirs/2]). +-export([ct_test_halt/1]). + -include_lib("kernel/include/file.hrl"). %%%----------------------------------------------------------------- @@ -229,8 +231,9 @@ run(Opts, Config) when is_list(Opts) -> %% use ct interface test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~p) on ~p~n", [Opts, CTNode]), - Result1 = rpc:call(CTNode, ct, run_test, [Opts]), - + CtRunTestResult = rpc:call(CTNode, ct, run_test, [Opts]), + test_server:format(Level, "~n[RUN #1] Got return value ~p~n", + [CtRunTestResult]), case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of undefined -> ok; @@ -242,17 +245,36 @@ run(Opts, Config) when is_list(Opts) -> undefined = rpc:call(CTNode, erlang, whereis, [ct_util_server]) end, %% use run_test interface (simulated) - test_server:format(Level, "Saving start opts on ~p: ~p~n", [CTNode,Opts]), - rpc:call(CTNode, application, set_env, [common_test, run_test_start_opts, Opts]), - test_server:format(Level, "[RUN #2] Calling ct_run:script_start() on ~p~n", [CTNode]), - Result2 = rpc:call(CTNode, ct_run, script_start, []), - case {Result1,Result2} of - {ok,ok} -> + Opts1 = [{halt_with,{?MODULE,ct_test_halt}} | Opts], + test_server:format(Level, "Saving start opts on ~p: ~p~n", + [CTNode, Opts1]), + rpc:call(CTNode, application, set_env, + [common_test, run_test_start_opts, Opts1]), + test_server:format(Level, "[RUN #2] Calling ct_run:script_start() on ~p~n", + [CTNode]), + ExitStatus = rpc:call(CTNode, ct_run, script_start, []), + test_server:format(Level, "[RUN #2] Got exit status value ~p~n", + [ExitStatus]), + case {CtRunTestResult,ExitStatus} of + {{_Ok,Failed,{_UserSkipped,_AutoSkipped}},1} when Failed > 0 -> + ok; + {{_Ok,0,{_UserSkipped,AutoSkipped}},ExitStatus} when AutoSkipped > 0 -> + case proplists:get_value(exit_status, Opts1) of + ignore_config when ExitStatus == 1 -> + {error,{wrong_exit_status,ExitStatus}}; + _ -> + ok + end; + {{error,_}=Error,ExitStatus} -> + if ExitStatus /= 2 -> + {error,{wrong_exit_status,ExitStatus}}; + ExitStatus == 2 -> + Error + end; + {{_Ok,0,{_UserSkipped,_AutoSkipped}},0} -> ok; - {E,_} when E =/= ok -> - E; - {_,E} when E =/= ok -> - E + Unexpected -> + {error,{unexpected_return_value,Unexpected}} end. run(M, F, A, Config) -> @@ -272,6 +294,10 @@ run({M,F,A}, InitCalls, Config) -> [M, F, A, CTNode]), rpc:call(CTNode, M, F, A). +%% this is the last function that ct_run:script_start() calls, so the +%% return value here is what rpc:call/4 above returns +ct_test_halt(ExitStatus) -> + ExitStatus. %%%----------------------------------------------------------------- %%% wait_for_ct_stop/1 diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl index b6dcf63fdf..693e8c6567 100644 --- a/lib/common_test/test/ct_testspec_1_SUITE.erl +++ b/lib/common_test/test/ct_testspec_1_SUITE.erl @@ -621,7 +621,9 @@ setup_and_execute(TCName, TestSpec, Config) -> ok = ct_test_support:run(Opts, Config), TestSpec1 = [{logdir,proplists:get_value(logdir,Opts)}, {label,proplists:get_value(label,TestTerms)} | TestSpec], - ok = ct_test_support:run(ct, run_testspec, [TestSpec1], Config), + {_Ok,_Failed,{_USkipped,_ASkipped}} = + ct_test_support:run(ct, run_testspec, [TestSpec1], Config), + Events = ct_test_support:get_events(ERPid, Config), ct_test_support:log_events(TCName, diff --git a/lib/common_test/test/ct_testspec_2_SUITE.erl b/lib/common_test/test/ct_testspec_2_SUITE.erl new file mode 100644 index 0000000000..411529b52a --- /dev/null +++ b/lib/common_test/test/ct_testspec_2_SUITE.erl @@ -0,0 +1,759 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2011. 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% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_testspec_2_SUITE +%%% +%%% Description: +%%% Test test specifications +%%% +%%% The suites used for the test are located in the data directory. +%%%------------------------------------------------------------------- +-module(ct_testspec_2_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/src/ct_util.hrl"). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(_TestCase, _Config) -> + ok. + +%% suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [basic_compatible_no_nodes, + basic_compatible_nodes, + unknown_terms, + no_merging, + multiple_specs, + misc_config_terms, + define_names_1]. + + +%%-------------------------------------------------------------------- +%% VALID TEST SPEC TERMS (R15B02): +%% +%% {node,3} +%% {cover,2} +%% {cover,3} +%% {config,2} +%% {config,3} +%% {config,4} +%% {userconfig,2} +%% {userconfig,3} +%% {alias,3} +%% {merge_tests,2} +%% {logdir,2} +%% {logdir,3} +%% {logopts,2} +%% {logopts,3} +%% {basic_html,2} +%% {basic_html,3} +%% {verbosity,2} +%% {verbosity,3} +%% {silent_connections,2} +%% {silent_connections,3} +%% {label,2} +%% {label,3} +%% {event_handler,2} +%% {event_handler,3} +%% {event_handler,4} +%% {ct_hooks,2} +%% {ct_hooks,3} +%% {enable_builtin_hooks,2} +%% {release_shell,2} +%% {multiply_timetraps,2} +%% {multiply_timetraps,3} +%% {scale_timetraps,2} +%% {scale_timetraps,3} +%% {include,2} +%% {include,3} +%% {auto_compile,2} +%% {auto_compile,3} +%% {stylesheet,2} +%% {stylesheet,3} +%% {suites,3} +%% {suites,4} +%% {groups,4} +%% {groups,5} +%% {groups,6} +%% {cases,4} +%% {cases,5} +%% {skip_suites,4} +%% {skip_suites,5} +%% {skip_groups,5} +%% {skip_groups,6} +%% {skip_groups,7} +%% {skip_cases,5} +%% {skip_cases,6} +%% {create_priv_dir,2} +%% +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +basic_compatible_no_nodes(_Config) -> + + AliasDir1 = "../tests/to1", + AliasDir2 = "../tests/to2", + CfgDir1 = "../cfgs/to1/x.cfg", + CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"], + LogDir = "../logs", + IncludeDir1 = "../../include", + IncludeDir2 = ["../tests/to1/include","../tests/to2/include"], + + Spec = + [ + {label,"basic_compatible_no_nodes"}, + {alias,to1,AliasDir1}, + {alias,to2,AliasDir2}, + {config,CfgDir1}, + {config,CfgDir2}, + {userconfig,{?MODULE,"cfg_str1"}}, + {userconfig,{?MODULE,"cfg_str2"}}, + {logdir,LogDir}, + {logopts,[no_nl]}, + {event_handler,evh1,[1]}, + {event_handler,[evh2,evh3],[[2,3]]}, + {ct_hooks,[{cth_mod1,[]}]}, + {ct_hooks,[{cth_mod2,[]}]}, + {multiply_timetraps,2}, + {include,IncludeDir1}, + {include,IncludeDir2}, + {suites,to1,[x_SUITE]}, + {groups,to1,y_SUITE,[g1,g2]}, + {cases,to1,y_SUITE,[tc1,tc2]}, + {skip_suites,to1,z_SUITE,"skipped"}, + {suites,to2,[x_SUITE,y_SUITE]}, + {skip_groups,to2,x_SUITE,[g1,g2],"skipped"}, + {skip_cases,to2,y_SUITE,[tc1,tc2],"skipped"} + ], + + {ok,SpecDir} = file:get_cwd(), + + ListResult = ct_testspec:collect_tests_from_list(Spec, false), + ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]), + SpecFile = ct_test_support:write_testspec(Spec,SpecDir, + "basic_compatible_no_nodes.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile], false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + Node = node(), + LogDirV = get_absdir(filename:join(SpecDir,"../logs")), + Alias1V = get_absdir(filename:join(SpecDir,AliasDir1)), + Alias2V = get_absdir(filename:join(SpecDir,AliasDir2)), + CFGs = [{Node,get_absdir(filename:join(SpecDir,CfgDir))} || + CfgDir <- [CfgDir1 | CfgDir2]], + Incls = [{Node,get_absdir(filename:join(SpecDir,IncludeDir))} || + IncludeDir <- [IncludeDir1 | IncludeDir2]], + + Verify = #testspec{spec_dir = SpecDir, + nodes = [{undefined,Node}], + init = [], + label = [{Node,"basic_compatible_no_nodes"}], + logdir = [{Node,LogDirV},"."], + logopts = [{Node,[no_nl]}], + basic_html = [], + cover = [], + config = CFGs, + userconfig = [{Node,{?MODULE,"cfg_str1"}}, + {Node,{?MODULE,"cfg_str2"}}], + event_handler = [{Node,evh1,[1]}, + {Node,evh2,[[2,3]]}, + {Node,evh3,[[2,3]]}], + ct_hooks = [{Node,{cth_mod1,[]}}, + {Node,{cth_mod2,[]}}], + enable_builtin_hooks = true, + release_shell = false, + include = Incls, + auto_compile = [], + stylesheet = [], + multiply_timetraps = [{Node,2}], + scale_timetraps = [], + create_priv_dir = [], + alias = [{to1,Alias1V},{to2,Alias2V}], + tests = [{{Node,Alias1V}, + [{x_SUITE,[all]}, + {y_SUITE,[{g1,all},{g2,all},tc1,tc2]}, + {z_SUITE,[{all,{skip,"skipped"}}]}]}, + {{Node,Alias2V}, + [{x_SUITE,[all, + {{g1,all},{skip,"skipped"}}, + {{g2,all},{skip,"skipped"}}]}, + {y_SUITE,[all, + {tc1,{skip,"skipped"}}, + {tc2,{skip,"skipped"}}]}]}], + merge_tests = true}, + + verify_result(Verify,ListResult,FileResult). + +%%%----------------------------------------------------------------- +%%% +basic_compatible_nodes(_Config) -> + + Node1 = node1@host1, + Node2 = node2@host2, + TODir1 = "../tests/to1", + TODir2 = "../tests/to2", + CfgDir1 = "../cfgs/to1/x.cfg", + CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"], + LogDir = "../logs", + MasterLogDir = "../master_logs", + IncludeDir1 = "../../include", + IncludeDir2 = ["../tests/to1/include","../tests/to2/include"], + + Spec = + [ + {node,n1,Node1}, + {node,n2,Node2}, + {init,[n1],[{node_start,[{callback_module,cbm}]}]}, + {init,n2,[{node_start,[]}]}, + {init,all_nodes,{eval,{mod,func,[]}}}, + {label,"basic_compatible_nodes"}, + {label,n1,basic_compatible_nodes_1}, + {config,n1,CfgDir1}, + {config,n2,CfgDir2}, + {userconfig,{?MODULE,"cfg_str1"}}, + {userconfig,{?MODULE,"cfg_str2"}}, + {logdir,all_nodes,LogDir}, + {logdir,master,MasterLogDir}, + {logopts,node2@host2,[no_nl]}, + {event_handler,master,evh1,[1]}, + {event_handler,[n1,n2],[evh2,evh3],[[2,3]]}, + {ct_hooks,all_nodes,[{cth_mod1,[]}]}, + {ct_hooks,[{cth_mod2,[]}]}, + {multiply_timetraps,node1@host1,2}, + {include,n1,IncludeDir1}, + {include,[n1,n2],IncludeDir2}, + {suites,n1,TODir1,[x_SUITE]}, + {groups,n1,TODir1,y_SUITE,[g1,g2]}, + {cases,n1,TODir1,y_SUITE,[tc1,tc2]}, + {skip_suites,n1,TODir1,z_SUITE,"skipped"}, + {suites,n2,TODir2,[x_SUITE,y_SUITE]}, + {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"}, + {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"} + ], + + {ok,SpecDir} = file:get_cwd(), + + ListResult = ct_testspec:collect_tests_from_list(Spec, false), + ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]), + SpecFile = ct_test_support:write_testspec(Spec,SpecDir, + "basic_compatible_nodes.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile], false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + Node = node(), + LogDirV = get_absdir(filename:join(SpecDir,"../logs")), + MasterLogDirV = get_absdir(filename:join(SpecDir,"../master_logs")), + TO1V = get_absdir(filename:join(SpecDir,TODir1)), + TO2V = get_absdir(filename:join(SpecDir,TODir2)), + CFGs = [{Node1,get_absdir(filename:join(SpecDir,CfgDir1))} | + [{Node2,get_absdir(filename:join(SpecDir,CfgDir))} || CfgDir <- CfgDir2]], + Incls = [{Node1,get_absdir(filename:join(SpecDir,IncludeDir1))} | + [{Node1,get_absdir(filename:join(SpecDir,IncludeDir))} || + IncludeDir <- IncludeDir2] ++ + [{Node2,get_absdir(filename:join(SpecDir,IncludeDir))} || + IncludeDir <- IncludeDir2]], + + Verify = #testspec{spec_dir = SpecDir, + nodes = [{undefined,Node},{n1,Node1},{n2,Node2}], + init = [{Node1,[{node_start,[{callback_module,cbm}]}, + {eval,[{mod,func,[]}]}]}, + {Node2,[{node_start,[{callback_module,ct_slave}]}, + {eval,[{mod,func,[]}]}]}, + {Node,[{node_start,[]}, + {eval,[{mod,func,[]}]}]}], + label = [{Node,"basic_compatible_nodes"}, + {Node2,"basic_compatible_nodes"}, + {Node1,basic_compatible_nodes_1}], + logdir = [{Node,LogDirV},{Node1,LogDirV},{Node2,LogDirV}, + {master,MasterLogDirV},"."], + logopts = [{Node2,[no_nl]}], + basic_html = [], + cover = [], + config = CFGs, + userconfig = [{Node,{?MODULE,"cfg_str1"}}, + {Node1,{?MODULE,"cfg_str1"}}, + {Node2,{?MODULE,"cfg_str1"}}, + {Node,{?MODULE,"cfg_str2"}}, + {Node1,{?MODULE,"cfg_str2"}}, + {Node2,{?MODULE,"cfg_str2"}}], + event_handler = [{master,evh1,[1]}, + {Node1,evh2,[[2,3]]}, + {Node1,evh3,[[2,3]]}, + {Node2,evh2,[[2,3]]}, + {Node2,evh3,[[2,3]]}], + ct_hooks = [{Node,{cth_mod1,[]}}, + {Node1,{cth_mod1,[]}}, + {Node2,{cth_mod1,[]}}, + {Node,{cth_mod2,[]}}, + {Node1,{cth_mod2,[]}}, + {Node2,{cth_mod2,[]}}], + enable_builtin_hooks = true, + release_shell = false, + include = Incls, + auto_compile = [], + stylesheet = [], + multiply_timetraps = [{Node1,2}], + scale_timetraps = [], + create_priv_dir = [], + tests = [{{Node1,TO1V}, + [{x_SUITE,[all]}, + {y_SUITE,[{g1,all},{g2,all},tc1,tc2]}, + {z_SUITE,[{all,{skip,"skipped"}}]}]}, + {{Node2,TO2V}, + [{x_SUITE,[all, + {{g1,all},{skip,"skipped"}}, + {{g2,all},{skip,"skipped"}}]}, + {y_SUITE,[all, + {tc1,{skip,"skipped"}}, + {tc2,{skip,"skipped"}}]}]}], + merge_tests = true}, + + verify_result(Verify,ListResult,FileResult). + +%%%----------------------------------------------------------------- +%%% +unknown_terms(Config) -> + PrivDir = ?config(priv_dir, Config), + + Spec1 = [{suites,PrivDir,all}, + {userdata,"I've got news for you"}], + {error,{undefined_term_in_spec,{userdata,_}}} = + (catch ct_testspec:collect_tests_from_list(Spec1, false)), + true = is_record(ct_testspec:collect_tests_from_list(Spec1, true), + testspec), + + Spec2 = [{logdir,{logdir,PrivDir}}], + {error,{invalid_directory_name,_}} = + (catch ct_testspec:collect_tests_from_list(Spec2, false)), + + Spec3 = [{suite,PrivDir,all}], + {error,{undefined_term_in_spec,{suite,_,_}}} = + (catch ct_testspec:collect_tests_from_list(Spec3, false)), + true = is_record(ct_testspec:collect_tests_from_list(Spec3, true), testspec), + + Spec4 = [{suites,PrivDir,all}, + {skip_suites,PrivDir,x_SUITE}], + {error,{bad_term_in_spec,{skip_suites,_,_}}} = + (catch ct_testspec:collect_tests_from_list(Spec4, false)), + {error,{bad_term_in_spec,{skip_suites,_,_}}} = + (catch ct_testspec:collect_tests_from_list(Spec4, true)), + + Spec5 = [{configs,all_nodes,PrivDir}], + {error,{undefined_term_in_spec,{configs,_,_}}} = + (catch ct_testspec:collect_tests_from_list(Spec5, false)), + true = is_record(ct_testspec:collect_tests_from_list(Spec5, true), testspec), + + ok. + +%%%----------------------------------------------------------------- +%%% +no_merging(_Config) -> + Node1 = node1@host1, + Node2 = node2@host2, + TODir1 = "../tests/to1", + TODir2 = "../tests/to2", + Spec = + [ + {merge_tests,false}, + {node,n1,Node1}, + {node,n2,Node2}, + {suites,n1,TODir1,[x_SUITE]}, + {groups,n1,TODir1,y_SUITE,[g1,g2]}, + {cases,n1,TODir1,y_SUITE,[tc1,tc2]}, + {skip_suites,n1,TODir1,z_SUITE,"skipped"}, + {suites,n2,TODir2,[x_SUITE,y_SUITE]}, + {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"}, + {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"} + ], + + {ok,SpecDir} = file:get_cwd(), + + ListResult = ct_testspec:collect_tests_from_list(Spec, false), + ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]), + SpecFile = ct_test_support:write_testspec(Spec,SpecDir, + "no_merging.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile], false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + Node = node(), + TO1V = get_absdir(filename:join(SpecDir,TODir1)), + TO2V = get_absdir(filename:join(SpecDir,TODir2)), + + Verify = #testspec{merge_tests = false, + spec_dir = SpecDir, + nodes = [{undefined,Node},{n1,Node1},{n2,Node2}], + tests = [{{Node1,TO1V}, + [{x_SUITE,[all]}]}, + {{Node1,TO1V}, + [{y_SUITE,[{g1,all},{g2,all}]}]}, + {{Node1,TO1V}, + [{y_SUITE,[tc1,tc2]}]}, + {{Node1,TO1V}, + [{z_SUITE,[{all,{skip,"skipped"}}]}]}, + {{Node2,TO2V}, + [{x_SUITE,[all]}]}, + {{Node2,TO2V}, + [{y_SUITE,[all]}]}, + {{Node2,TO2V}, + [{x_SUITE,[{{g1,all},{skip,"skipped"}}, + {{g2,all},{skip,"skipped"}}]}]}, + {{Node2,TO2V}, + [{y_SUITE,[{tc1,{skip,"skipped"}}, + {tc2,{skip,"skipped"}}]}]}]}, + + verify_result(Verify,ListResult,FileResult). + +%%%----------------------------------------------------------------- +%%% +multiple_specs(_Config) -> + Node1 = node1@host1, + Node2 = node2@host2, + TODir1 = "../tests/to1", + TODir2 = "../tests/to2", + CfgDir1 = "../cfgs/to1/x.cfg", + CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"], + LogDir = "../logs", + Spec1 = + [ + {node,n1,Node1}, + {node,n2,Node2}, + {alias,to1,TODir1}, + {alias,to2,TODir2}, + {label,"multiple_specs1"}, + {config,n1,CfgDir1}, + {config,n2,CfgDir2}, + {logdir,all_nodes,LogDir} + ], + Spec2 = + [ + {merge_tests,false}, + {label,"multiple_specs2"}, + {suites,n1,TODir1,[x_SUITE]}, + {groups,n1,TODir1,y_SUITE,[g1,g2]}, + {cases,n1,TODir1,y_SUITE,[tc1,tc2]}, + {skip_suites,n1,TODir1,z_SUITE,"skipped"}, + {suites,n2,TODir2,[x_SUITE,y_SUITE]}, + {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"}, + {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"} + ], + + {ok,SpecDir} = file:get_cwd(), + SpecFile1 = ct_test_support:write_testspec(Spec1,SpecDir, + "multiple_specs.1.spec"), + SpecFile2 = ct_test_support:write_testspec(Spec2,SpecDir, + "multiple_specs.2.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile1,SpecFile2], + false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + Node = node(), + TO1V = get_absdir(filename:join(SpecDir,TODir1)), + TO2V = get_absdir(filename:join(SpecDir,TODir2)), + CFGs = [{Node1,get_absdir(filename:join(SpecDir,CfgDir1))} | + [{Node2,get_absdir(filename:join(SpecDir,CfgDir))} || CfgDir <- CfgDir2]], + LogDirV = get_absdir(filename:join(SpecDir,"../logs")), + + Verify = #testspec{merge_tests = false, + spec_dir = SpecDir, + nodes = [{undefined,Node},{n1,Node1},{n2,Node2}], + alias = [{to1,TO1V},{to2,TO2V}], + label = [{Node,"multiple_specs1"}, + {Node1,"multiple_specs1"}, + {Node2,"multiple_specs1"}], + logdir = [{Node,LogDirV},{Node1,LogDirV},{Node2,LogDirV},"."], + config = CFGs, + tests = [{{Node1,TO1V}, + [{x_SUITE,[all]}]}, + {{Node1,TO1V}, + [{y_SUITE,[{g1,all},{g2,all}]}]}, + {{Node1,TO1V}, + [{y_SUITE,[tc1,tc2]}]}, + {{Node1,TO1V}, + [{z_SUITE,[{all,{skip,"skipped"}}]}]}, + {{Node2,TO2V}, + [{x_SUITE,[all]}]}, + {{Node2,TO2V}, + [{y_SUITE,[all]}]}, + {{Node2,TO2V}, + [{x_SUITE,[{{g1,all},{skip,"skipped"}}, + {{g2,all},{skip,"skipped"}}]}]}, + {{Node2,TO2V}, + [{y_SUITE,[{tc1,{skip,"skipped"}}, + {tc2,{skip,"skipped"}}]}]}]}, + + verify_result(Verify,FileResult,FileResult). + +%%%----------------------------------------------------------------- +%%% +misc_config_terms(_Config) -> + CfgDir = "../cfgs/to1", + + Spec = + [{node,x,n1@h1},{node,y,n2@h2}, + + {config,CfgDir,"a.cfg"}, + {config,n1@h1,CfgDir,"b.cfg"}, + {config,all_nodes,CfgDir,"c.cfg"}, + {config,all_nodes,filename:join(CfgDir,"d.cfg")}, + + {basic_html,true}, + {basic_html,n1@h1,false}, + {basic_html,n2@h2,true}, + + {silent_connections,n1@h1,all}, + {silent_connections,n2@h2,[ssh]}, + + {enable_builtin_hooks,false}, + + {release_shell,true}, + + {auto_compile,false}, + {auto_compile,n1@h1,true}, + {auto_compile,n2@h2,false}, + + {stylesheet,"../css"}, + {stylesheet,n1@h1,"./n1/css"}, + {stylesheet,n2@h2,"./n2/css"}, + + {create_priv_dir,[auto_per_tc]}, + {create_priv_dir,n1@h1,[manual_per_tc]}, + {create_priv_dir,n2@h2,[auto_per_run]} + ], + + {ok,SpecDir} = file:get_cwd(), + + ListResult = ct_testspec:collect_tests_from_list(Spec, false), + ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]), + SpecFile = ct_test_support:write_testspec(Spec,SpecDir, + "misc_config_terms.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile], false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + Node = node(), + CfgA = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "a.cfg")), + CfgB = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "b.cfg")), + CfgC = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "c.cfg")), + CfgD = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "d.cfg")), + CSS = get_absdir(filename:join(SpecDir,"../css")), + CSS1 = get_absdir(filename:join(SpecDir,"./n1/css")), + CSS2 = get_absdir(filename:join(SpecDir,"./n2/css")), + + Verify = #testspec{spec_dir = SpecDir, + nodes = [{undefined,Node},{x,n1@h1},{y,n2@h2}], + basic_html = [{Node,true},{n1@h1,false},{n2@h2,true}], + silent_connections = [{n1@h1,[all]},{n2@h2,[ssh]}], + config = [{Node,CfgA}, + {n1@h1,CfgA}, + {n2@h2,CfgA}, + {n1@h1,CfgB}, + {Node,CfgC}, + {n1@h1,CfgC}, + {n2@h2,CfgC}, + {Node,CfgD}, + {n1@h1,CfgD}, + {n2@h2,CfgD}], + enable_builtin_hooks = false, + release_shell = true, + auto_compile = [{Node,false}, + {n1@h1,true}, + {n2@h2,false}], + stylesheet = [{Node,CSS}, + {n1@h1,CSS1}, + {n2@h2,CSS2}], + create_priv_dir = [{Node,[auto_per_tc]}, + {n1@h1,[manual_per_tc]}, + {n2@h2,[auto_per_run]}] + }, + + verify_result(Verify,ListResult,FileResult). + +%%%----------------------------------------------------------------- +%%% +define_names_1(_Config) -> + Spec = + [ + {define,'HOST','eniac'}, + {define,'NODE1',testnode1}, + {define,'NODE2',testnode2}, + {define,'NODES',['NODE1@HOST', + 'NODE2@HOST']}, + {define,'TOPDIR',".."}, + {define,'TO1',"to1"}, + {define,'TO2',"to2"}, + {define,'LOGDIR',"'TOPDIR'/logdir"}, + {define,'LOGDIR1',"'TOPDIR'/logdir1"}, + {define,'LOGDIR2',"'TOPDIR'/logdir2"}, + {define,'CFGDIR',"'TOPDIR'/cfgs"}, + {define,'CFGFILES',["cfgX","cfgY"]}, + {define,'TESTDIR',"'TOPDIR'/test"}, + {define,'TO1DIR',"'TESTDIR'/'TO1'"}, + {define,'TO2DIR',"'TESTDIR'/'TO2'"}, + {define,'EXSUITE',ex_SUITE}, + {define,'EXGRS',[g1,g2]}, + + {logdir,'LOGDIR'}, + {logdir,'NODE1@HOST','LOGDIR1'}, + {logdir,'NODE2@HOST','LOGDIR2'}, + + {config,["a.cfg","b.cfg"]}, + {config,'NODES',"./'CFGDIR'/c.cfg"}, + {config,'CFGDIR',["d.cfg","e.cfg"]}, + {config,'NODE2@HOST','CFGDIR','CFGFILES'}, + + {suites,'NODE1@HOST','TO1DIR',all}, + {suites,'NODES','TO2DIR',all}, + + {groups,'TO1DIR','EXSUITE','EXGRS'} + ], + + {ok,SpecDir} = file:get_cwd(), + + ListResult = ct_testspec:collect_tests_from_list(Spec, false), + ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]), + SpecFile = ct_test_support:write_testspec(Spec,SpecDir, + "define_names_1.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile], false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + N = node(), + N1 = testnode1@eniac, + N2 = testnode2@eniac, + Join = fun(Dir) -> shorten_path(filename:join(SpecDir,Dir),SpecDir) end, + + Verify = #testspec{spec_dir = SpecDir, + nodes = [{undefined,N2}, + {undefined,N1}, + {undefined,N}], + config = [{N2,Join("a.cfg")},{N2,Join("b.cfg")}, + {N1,Join("a.cfg")},{N1,Join("b.cfg")}, + {N,Join("a.cfg")},{N,Join("b.cfg")}, + {N1,Join("../cfgs/c.cfg")},{N2,Join("../cfgs/c.cfg")}, + {N2,Join("../cfgs/d.cfg")},{N2,Join("../cfgs/e.cfg")}, + {N1,Join("../cfgs/d.cfg")},{N1,Join("../cfgs/e.cfg")}, + {N,Join("../cfgs/d.cfg")},{N,Join("../cfgs/e.cfg")}, + {N2,Join("../cfgs/cfgX")},{N2,Join("../cfgs/cfgY")}], + logdir = [{N,Join("../logdir")}, + {N1,Join("../logdir1")}, + {N2,Join("../logdir2")}, + "."], + tests = [{{N1,Join("../test/to1")},[{all,[all]}]}, + {{N1,Join("../test/to2")},[{all,[all]}]}, + {{N2,Join("../test/to2")},[{all,[all]}]}, + {{N2,Join("../test/to1")}, + [{ex_SUITE,[{g1,all},{g2,all}]}]}, + {{N,Join("../test/to1")}, + [{ex_SUITE,[{g1,all},{g2,all}]}]}] + }, + verify_result(Verify,ListResult,FileResult). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +verify_result(Verify,ListResult,FileResult) -> + {_,TSLTuples} = rec2proplist(ListResult), + {_,TSFTuples} = rec2proplist(FileResult), + {_,VTuples} = rec2proplist(Verify), + VResult = + (catch lists:foldl(fun({Tag,Val},{[{Tag,Val}|TSL],[{Tag,Val}|TSF]}) -> + {TSL,TSF}; + ({Tag,Val},{[{_Tag,TSLVal}|_TSL],[{Tag,Val}|_TSF]}) -> + exit({ts_list_mismatch,Tag,Val,TSLVal}); + ({Tag,Val},{[{Tag,Val}|_TSL],[{_Tag,TSFVal}|_TSF]}) -> + exit({ts_file_mismatch,Tag,Val,TSFVal}); + ({Tag,Val},{_,[{_Tag,TSFVal}|_TSF]}) -> + exit({ts_mismatch,Tag,Val,TSFVal}) + end, {TSLTuples,TSFTuples}, VTuples)), + case VResult of + {'EXIT',Reason} -> + ct:fail(Reason); + _ -> + ok + end, + ok. + + +check_parameter(S="cfg_str1") -> + {ok,{config,S}}; +check_parameter(S="cfg_str2") -> + {ok,{config,S}}. +read_config(S) -> + {ok,[{cfg,S}]}. + +rec2proplist(E={error,_What}) -> + exit({invalid_testspec_record,E}); +rec2proplist(Rec) -> + [RecName|RecList] = tuple_to_list(Rec), + FieldNames = + if RecName == testspec -> + record_info(fields, testspec); + true -> + undefined + end, + {RecName,combine_names_and_vals(FieldNames,RecList)}. + +combine_names_and_vals([FN|FNs], [V|Vs]) -> + [{FN,V} | combine_names_and_vals(FNs, Vs)]; +combine_names_and_vals([], []) -> + []; +combine_names_and_vals(_, _) -> + []. + +get_absdir(Dir) -> + shorten_path(filename:absname(Dir),Dir). + +shorten_path(Path,SpecDir) -> + case shorten_split_path(filename:split(Path),[]) of + [] -> + [Root|_] = filename:split(SpecDir), + Root; + Short -> + filename:join(Short) + end. + +shorten_split_path([".."|Path],SoFar) -> + shorten_split_path(Path,tl(SoFar)); +shorten_split_path(["."|Path],SoFar) -> + shorten_split_path(Path,SoFar); +shorten_split_path([Dir|Path],SoFar) -> + shorten_split_path(Path,[Dir|SoFar]); +shorten_split_path([],SoFar) -> + lists:reverse(SoFar). diff --git a/lib/common_test/test/ct_verbosity_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE.erl new file mode 100644 index 0000000000..349319de94 --- /dev/null +++ b/lib/common_test/test/ct_verbosity_SUITE.erl @@ -0,0 +1,244 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_verbosity_SUITE +%%% +%%% Description: +%%% Test that verbosity levels vs the importance parameter works as +%%% expected. +%%% +%%%------------------------------------------------------------------- +-module(ct_verbosity_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + no_levels, + general_level_low, + general_level_std, + general_level_hi, + change_default, + combine_categories, + testspec_only, + merge_with_testspec + ]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +no_levels(Config) -> + TC = no_levels, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +general_level_low(Config) -> + TC = general_level_low, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}, + {verbosity,0}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +general_level_std(Config) -> + TC = general_level_std, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}, + {verbosity,50}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +general_level_hi(Config) -> + TC = general_level_high, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}, + {verbosity,100}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +change_default(Config) -> + TC = change_default, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}, + {verbosity,[{default,49}]}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +combine_categories(Config) -> + TC = combine_categories, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}, + {verbosity,[{error,?HI_VERBOSITY}, + {default,?LOW_VERBOSITY}]}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +testspec_only(Config) -> + TC = testspec_only, + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + + TestSpec = [{verbosity,[{default,1},{error,75},100]}, + {suites,DataDir,[io_test_SUITE]}, + {label,TC}], + + TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir, + "verbosity_1_spec"), + {Opts,ERPid} = setup([{spec,TestSpecName}], Config), + + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +merge_with_testspec(Config) -> + TC = merge_with_testspec, + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + + TestSpec = [{verbosity,[{default,100},{error,100}]}, + {suites,DataDir,[io_test_SUITE]}, + {label,TC}], + + TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir, + "verbosity_2_spec"), + + %% below should override verbosity categories in testspec + {Opts,ERPid} = setup([{spec,TestSpecName}, + {verbosity,[{default,0},0]}], + Config), + + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +execute(Name, Opts, ERPid, Config) -> + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(Name, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(Name), + ct_test_support:verify_events(TestEvents, Events, Config). + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test) -> + %% 2 tests (ct:run_test + script_start) is default + events_to_check(Test, 2). + +events_to_check(_, 0) -> + []; +events_to_check(Test, N) -> + test_events(Test) ++ events_to_check(Test, N-1). + + +test_events(_) -> + [ + {?eh,tc_done,{io_test_SUITE,tc1,ok}}, + {?eh,tc_done,{io_test_SUITE,tc2,ok}}, + {?eh,tc_done,{io_test_SUITE,tc3,ok}}, + + {parallel, + [ + {?eh,tc_start,{io_test_SUITE,tc1}}, + {?eh,tc_start,{io_test_SUITE,tc2}}, + {?eh,tc_start,{io_test_SUITE,tc3}}, + {?eh,tc_done,{io_test_SUITE,tc1,ok}}, + {?eh,tc_done,{io_test_SUITE,tc2,ok}}, + {?eh,tc_done,{io_test_SUITE,tc3,ok}}, + {parallel, + [ + {?eh,tc_start,{io_test_SUITE,tc1}}, + {?eh,tc_start,{io_test_SUITE,tc2}}, + {?eh,tc_start,{io_test_SUITE,tc3}}, + {?eh,tc_done,{io_test_SUITE,tc1,ok}}, + {?eh,tc_done,{io_test_SUITE,tc2,ok}}, + {?eh,tc_done,{io_test_SUITE,tc3,ok}}, + {?eh,test_stats,{9,0,{0,0}}} + ]} + ]}, + + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]. + diff --git a/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl new file mode 100644 index 0000000000..946e1c1989 --- /dev/null +++ b/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl @@ -0,0 +1,156 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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(io_test_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% @spec suite() -> Info +%% Info = [tuple()] +%% @end +%%-------------------------------------------------------------------- +suite() -> + [{timetrap,{seconds,10}}]. + +%%-------------------------------------------------------------------- +%% @spec init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_suite(Config0) -> void() | {save_config,Config1} +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_group(GroupName, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_group(GroupName, Config0) -> +%% void() | {save_config,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_group(_GroupName, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_testcase(TestCase, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} | {fail,Reason} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec groups() -> [Group] +%% Group = {GroupName,Properties,GroupsAndTestCases} +%% GroupName = atom() +%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}] +%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase] +%% TestCase = atom() +%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}} +%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | +%% repeat_until_any_ok | repeat_until_any_fail +%% N = integer() | forever +%% @end +%%-------------------------------------------------------------------- +groups() -> + [{g1, [parallel], [tc1,tc2,tc3,{group,g2}]}, + {g2, [parallel], [tc1,tc2,tc3]}]. + +%%-------------------------------------------------------------------- +%% @spec all() -> GroupsAndTestCases | {skip,Reason} +%% GroupsAndTestCases = [{group,GroupName} | TestCase] +%% GroupName = atom() +%% TestCase = atom() +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +all() -> + [tc1,tc2,tc3,{group,g1}]. + +tc1(_C) -> + io:format("This is an io:format(~p)~n", [[]]), + ct:log("ct:log(default)", []), + ct:log(?STD_IMPORTANCE, "ct:log(default,~p)", [?STD_IMPORTANCE]), + ct:log(error, "ct:log(error)", []), + ct:log(error, ?STD_IMPORTANCE, "ct:log(error,~p)", [?STD_IMPORTANCE]), + ct:log(1, "ct:log(default,~p)", [1]), + ct:log(error, 1, "ct:log(error,~p)", [1]), + ct:log(99, "ct:log(default,~p)", [99]), + ct:log(error, 99, "ct:log(error,~p)", [99]), + ok. + +tc2(_C) -> + io:format("This is an io:format(~p)~n", [[]]), + ct:pal("ct:pal(default)", []), + ct:pal(?STD_IMPORTANCE, "ct:pal(default,~p)", [?STD_IMPORTANCE]), + ct:pal(error, "ct:pal(error)", []), + ct:pal(error, ?STD_IMPORTANCE, "ct:pal(error,~p)", [?STD_IMPORTANCE]), + ct:pal(1, "ct:pal(default,~p)", [1]), + ct:pal(error, 1, "ct:pal(error,~p)", [1]), + ct:pal(99, "ct:pal(default,~p)", [99]), + ct:pal(error, 99, "ct:pal(error,~p)", [99]), + ok. + +tc3(_C) -> + io:format("This is an io:format(~p)~n", [[]]), + ct:print("ct:print(default)", []), + ct:print(?STD_IMPORTANCE, "ct:print(default,~p)", [?STD_IMPORTANCE]), + ct:print(error, "ct:print(error)", []), + ct:print(error, ?STD_IMPORTANCE, "ct:print(error,~p)", [?STD_IMPORTANCE]), + ct:print(1, "ct:print(default,~p)", [1]), + ct:print(error, 1, "ct:print(error,~p)", [1]), + ct:print(99, "ct:print(default,~p)", [99]), + ct:print(error, 99, "ct:print(error,~p)", [99]), + ok. diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk index b94f7f7593..877aa775fd 100644 --- a/lib/common_test/vsn.mk +++ b/lib/common_test/vsn.mk @@ -1 +1 @@ -COMMON_TEST_VSN = 1.6.1 +COMMON_TEST_VSN = 1.6.2 diff --git a/lib/compiler/doc/src/Makefile b/lib/compiler/doc/src/Makefile index ee41a7074f..308e9c4d02 100644 --- a/lib/compiler/doc/src/Makefile +++ b/lib/compiler/doc/src/Makefile @@ -99,14 +99,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/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index 84e9922847..27d750f929 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -294,6 +294,12 @@ module.beam: module.erl \ describing what it is doing.</p> </item> + <tag><c>{source,FileName}</c></tag> + <item> + <p>Sets the value of the source, as returned by + <c>module_info(compile)</c>.</p> + </item> + <tag><c>{outdir,Dir}</c></tag> <item> <p>Sets a new directory for the object code. The current @@ -310,7 +316,7 @@ module.beam: module.erl \ <item> <p>Add <c>Dir</c> to the list of directories to be searched when including a file. When encountering an - <c>-include</c> or <c>-include_dir</c> directive, + <c>-include</c> or <c>-include_lib</c> directive, the compiler searches for header files in the following directories:</p> <list type="ordered"> @@ -333,7 +339,8 @@ module.beam: module.erl \ <tag><c>{d,Macro,Value}</c></tag> <item> <p>Defines a macro <c>Macro</c> to have the value - <c>Value</c>. The default is <c>true</c>.</p> + <c>Value</c>. <c>Macro</c> is of type atom, and <c>Value</c> can be any term. + The default <c>Value</c> is <c>true</c>.</p> </item> <tag><c>{parse_transform,Module}</c></tag> diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile index 3415517fff..56c45d369c 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -163,11 +163,11 @@ $(EBIN)/cerl_inline.beam: $(ESRC)/cerl_inline.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/src" $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(EXTRA_FILES) \ - $(YRL_FILE) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(INSTALL_FILES) $(RELSYSDIR)/ebin + $(YRL_FILE) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(INSTALL_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl index 6f0ffb5b25..d307d192b2 100644 --- a/lib/compiler/src/beam_type.erl +++ b/lib/compiler/src/beam_type.erl @@ -29,10 +29,17 @@ module({Mod,Exp,Attr,Fs0,Lc}, _Opts) -> {ok,{Mod,Exp,Attr,Fs,Lc}}. function({function,Name,Arity,CLabel,Asm0}) -> - Asm1 = beam_utils:live_opt(Asm0), - Asm2 = opt(Asm1, [], tdb_new()), - Asm = beam_utils:delete_live_annos(Asm2), - {function,Name,Arity,CLabel,Asm}. + try + Asm1 = beam_utils:live_opt(Asm0), + Asm2 = opt(Asm1, [], tdb_new()), + Asm = beam_utils:delete_live_annos(Asm2), + {function,Name,Arity,CLabel,Asm} + catch + Class:Error -> + Stack = erlang:get_stacktrace(), + io:fwrite("Function: ~w/~w\n", [Name,Arity]), + erlang:raise(Class, Error, Stack) + end. %% opt([Instruction], Accumulator, TypeDb) -> {[Instruction'],TypeDb'} %% Keep track of type information; try to simplify. diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index abcd93f280..194f089ba1 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -741,6 +741,9 @@ live_opt([{badmatch,Src}=I|Is], _, D, Acc) -> live_opt([{case_end,Src}=I|Is], _, D, Acc) -> Regs = x_live([Src], 0), live_opt(Is, Regs, D, [I|Acc]); +live_opt([{try_case_end,Src}=I|Is], _, D, Acc) -> + Regs = x_live([Src], 0), + live_opt(Is, Regs, D, [I|Acc]); live_opt([if_end=I|Is], _, D, Acc) -> Regs = 0, live_opt(Is, Regs, D, [I|Acc]); @@ -802,8 +805,6 @@ live_opt([{deallocate,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); live_opt([{kill,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); -live_opt([{try_case_end,_}=I|Is], Regs, D, Acc) -> - live_opt(Is, Regs, D, [I|Acc]); live_opt([{try_end,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); live_opt([{loop_rec_end,_}=I|Is], Regs, D, Acc) -> diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index a52e7bb761..9f0bca9dd5 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -783,15 +783,27 @@ valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) -> valfun_4({bs_bits_to_bytes,{f,Fail},Src,Dst}, Vst) -> assert_term(Src, Vst), set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst)); -valfun_4({bs_init2,{f,Fail},_,Heap,Live,_,Dst}, Vst0) -> +valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) -> verify_live(Live, Vst0), + if + is_integer(Sz) -> + ok; + true -> + assert_term(Sz, Vst0) + end, Vst1 = heap_alloc(Heap, Vst0), Vst2 = branch_state(Fail, Vst1), Vst3 = prune_x_regs(Live, Vst2), Vst = bs_zero_bits(Vst3), set_type_reg(binary, Dst, Vst); -valfun_4({bs_init_bits,{f,Fail},_,Heap,Live,_,Dst}, Vst0) -> +valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) -> verify_live(Live, Vst0), + if + is_integer(Sz) -> + ok; + true -> + assert_term(Sz, Vst0) + end, Vst1 = heap_alloc(Heap, Vst0), Vst2 = branch_state(Fail, Vst1), Vst3 = prune_x_regs(Live, Vst2), diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 9b505ad15c..7365706b94 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -146,10 +146,17 @@ env_default_opts() -> do_compile(Input, Opts0) -> Opts = expand_opts(Opts0), - Self = self(), - Serv = spawn_link(fun() -> internal(Self, Input, Opts) end), + {Pid,Ref} = + spawn_monitor(fun() -> + exit(try + internal(Input, Opts) + catch + error:Reason -> + {error,Reason} + end) + end), receive - {Serv,Rep} -> Rep + {'DOWN',Ref,process,Pid,Rep} -> Rep end. expand_opts(Opts0) -> @@ -242,15 +249,12 @@ format_error({module_name,Mod,Filename}) -> errors=[], warnings=[]}). -internal(Master, Input, Opts) -> - Master ! {self(), try internal(Input, Opts) - catch error:Reason -> {error, Reason} - end}. - -internal({forms,Forms}, Opts) -> - {_,Ps} = passes(forms, Opts), - internal_comp(Ps, "", "", #compile{code=Forms,options=Opts, - mod_options=Opts}); +internal({forms,Forms}, Opts0) -> + {_,Ps} = passes(forms, Opts0), + Source = proplists:get_value(source, Opts0, ""), + Opts1 = proplists:delete(source, Opts0), + Compile = #compile{code=Forms,options=Opts1,mod_options=Opts1}, + internal_comp(Ps, Source, "", Compile); internal({file,File}, Opts) -> {Ext,Ps} = passes(file, Opts), Compile = #compile{options=Opts,mod_options=Opts}, diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl index ba9cde1de0..6cea783090 100644 --- a/lib/compiler/src/sys_pre_expand.erl +++ b/lib/compiler/src/sys_pre_expand.erl @@ -42,7 +42,7 @@ compile=[], %Compile flags attributes=[], %Attributes callbacks=[], %Callbacks - defined=[], %Defined functions + defined, %Defined functions (gb_set) vcount=0, %Variable counter func=[], %Current function arity=[], %Arity for current function @@ -83,7 +83,7 @@ module(Fs0, Opts0) -> {Efs,St2} = expand_pmod(Tfs, St1), %% Get the correct list of exported functions. Exports = case member(export_all, St2#expand.compile) of - true -> St2#expand.defined; + true -> gb_sets:to_list(St2#expand.defined); false -> St2#expand.exports end, %% Generate all functions from stored info. @@ -106,10 +106,11 @@ expand_pmod(Fs0, St0) -> true -> Ps0 end, + Def = gb_sets:to_list(St0#expand.defined), {Fs1,Xs,Ds} = sys_expand_pmod:forms(Fs0, Ps, St0#expand.exports, - St0#expand.defined), - St1 = St0#expand{exports=Xs, defined=Ds}, + Def), + St1 = St0#expand{exports=Xs,defined=gb_sets:from_list(Ds)}, {Fs2,St2} = add_instance(Ps, Fs1, St1), {Fs3,St3} = ensure_new(Base, Ps0, Fs2, St2), {Fs3,St3#expand{attributes = [{abstract, 0, [true]} @@ -118,7 +119,7 @@ expand_pmod(Fs0, St0) -> get_base(As) -> case lists:keyfind(extends, 1, As) of - {extends,[Base]} when is_atom(Base) -> + {extends,_,[Base]} when is_atom(Base) -> Base; _ -> [] @@ -159,7 +160,7 @@ add_func(Name, Args, Body, Fs, St) -> F = {function,0,Name,A,[{clause,0,Args,[],Body}]}, NA = {Name,A}, {[F|Fs],St#expand{exports=add_element(NA, St#expand.exports), - defined=add_element(NA, St#expand.defined)}}. + defined=gb_sets:add_element(NA, St#expand.defined)}}. %% define_function(Form, State) -> State. %% Add function to defined if form is a function. @@ -168,7 +169,7 @@ define_functions(Forms, #expand{defined=Predef}=St) -> Fs = foldl(fun({function,_,N,A,_Cs}, Acc) -> [{N,A}|Acc]; (_, Acc) -> Acc end, Predef, Forms), - St#expand{defined=ordsets:from_list(Fs)}. + St#expand{defined=gb_sets:from_list(Fs)}. module_attrs(#expand{attributes=Attributes}=St) -> Attrs = [{attribute,Line,Name,Val} || {Name,Line,Val} <- Attributes], @@ -187,7 +188,7 @@ module_predef_func_beh_info(#expand{callbacks=Callbacks,defined=Defined, PreDef=[{behaviour_info,1}], PreExp=PreDef, {[gen_beh_info(Callbacks)], - St#expand{defined=union(from_list(PreDef), Defined), + St#expand{defined=gb_sets:union(gb_sets:from_list(PreDef), Defined), exports=union(from_list(PreExp), Exports)}}. gen_beh_info(Callbacks) -> @@ -215,7 +216,8 @@ module_predef_funcs_mod_info(St) -> [{clause,0,[{var,0,'X'}],[], [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}}, [{atom,0,St#expand.module},{var,0,'X'}]}]}]}], - St#expand{defined=union(from_list(PreDef), St#expand.defined), + St#expand{defined=gb_sets:union(gb_sets:from_list(PreDef), + St#expand.defined), exports=union(from_list(PreExp), St#expand.exports)}}. %% forms(Forms, State) -> @@ -721,4 +723,4 @@ imported(F, A, St) -> end. defined(F, A, St) -> - ordsets:is_element({F,A}, St#expand.defined). + gb_sets:is_element({F,A}, St#expand.defined). diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 242196c593..01042cc56f 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -823,6 +823,13 @@ bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) -> {_,_} -> throw(bad_binary) end, + case Size1 of + #c_var{} -> ok; + #c_literal{val=Sz} when is_integer(Sz), Sz >= 0 -> ok; + #c_literal{val=undefined} -> ok; + #c_literal{val=all} -> ok; + _ -> throw(bad_binary) + end, {#c_bitstr{val=E1,size=Size1, unit=#c_literal{val=Unit}, type=#c_literal{val=Type}, diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index c4e7b45aac..b184987625 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -278,11 +278,12 @@ expr(#c_binary{anno=A,segments=Cv}, Sub, St0) -> {#k_binary{anno=A,segs=Kv},Ep,St1} catch throw:bad_element_size -> + St1 = add_warning(get_line(A), bad_segment_size, A, St0), Erl = #c_literal{val=erlang}, Name = #c_literal{val=error}, Args = [#c_literal{val=badarg}], Error = #c_call{anno=A,module=Erl,name=Name,args=Args}, - expr(Error, Sub, St0) + expr(Error, Sub, St1) end; expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0, #kern{ff=OldFF,func=Func}=St0) -> FA = case OldFF of @@ -1827,7 +1828,9 @@ format_error({nomatch_shadow,Line}) -> format_error(nomatch_shadow) -> "this clause cannot match because a previous clause always matches"; format_error(bad_call) -> - "invalid module and/or function name; this call will always fail". + "invalid module and/or function name; this call will always fail"; +format_error(bad_segment_size) -> + "binary construction will fail because of a type mismatch". add_warning(none, Term, Anno, #kern{ws=Ws}=St) -> File = get_file(Anno), diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile index e13ad4ae90..e047166ade 100644 --- a/lib/compiler/test/Makefile +++ b/lib/compiler/test/Makefile @@ -154,12 +154,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) compiler.spec compiler.cover \ - $(EMAKEFILE) $(ERL_FILES) $(CORE_FILES) $(RELSYSDIR) + $(EMAKEFILE) $(ERL_FILES) $(CORE_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) \ - $(INLINE_ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INLINE_ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl index 31c7890f26..e8b30f44ce 100644 --- a/lib/compiler/test/bs_construct_SUITE.erl +++ b/lib/compiler/test/bs_construct_SUITE.erl @@ -468,6 +468,10 @@ opt(Config) when is_list(Config) -> ?line {'EXIT',_} = (catch <<<<23,56,0,2>>:64/float>>), ?line {'EXIT',_} = (catch <<<<23,56,0,2:7>>/binary>>), + %% Test constant propagation - there should be a warning. + BadSz = 2.5, + {'EXIT',_} = (catch <<<<N,56,0,2>>:BadSz/binary>>), + case id(false) of true -> ?line opt_dont_call_me(); false -> ok diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 512fa0e4ac..2cd75944f4 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -25,7 +25,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, app_test/1, - file_1/1, module_mismatch/1, big_file/1, outdir/1, + file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1, binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1, other_output/1, package_forms/1, encrypted_abstr/1, bad_record_use1/1, bad_record_use2/1, strict_record/1, @@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> test_lib:recompile(?MODULE), - [app_test, file_1, module_mismatch, big_file, outdir, + [app_test, file_1, forms_2, module_mismatch, big_file, outdir, binary, makedep, cond_and_ifdef, listings, listings_big, other_output, package_forms, encrypted_abstr, {group, bad_record_use}, strict_record, @@ -76,6 +76,9 @@ app_test(Config) when is_list(Config) -> file_1(Config) when is_list(Config) -> ?line Dog = test_server:timetrap(test_server:minutes(5)), + + process_flag(trap_exit, true), + ?line {Simple, Target} = files(Config, "file_1"), ?line {ok, Cwd} = file:get_cwd(), ?line ok = file:set_cwd(filename:dirname(Target)), @@ -102,9 +105,37 @@ file_1(Config) when is_list(Config) -> %% Cleanup. ?line ok = file:delete(Target), ?line ok = file:del_dir(filename:dirname(Target)), + + %% There should not be any messages in the messages. + receive + Any -> + ?t:fail({unexpected,Any}) + after 10 -> + ok + end, + ?line test_server:timetrap_cancel(Dog), ok. +forms_2(Config) when is_list(Config) -> + Src = "/foo/bar", + AbsSrc = filename:absname(Src), + {ok,simple,Binary} = compile:forms([{attribute,1,module,simple}], + [binary,{source,Src}]), + code:load_binary(simple, Src, Binary), + Info = simple:module_info(compile), + + %% Test that the proper source is returned. + AbsSrc = proplists:get_value(source, Info), + + %% Ensure that the options are not polluted with 'source'. + [] = proplists:get_value(options, Info), + + %% Cleanup. + true = code:delete(simple), + false = code:purge(simple), + ok. + module_mismatch(Config) when is_list(Config) -> ?line DataDir = ?config(data_dir, Config), ?line File = filename:join(DataDir, "wrong_module_name.erl"), @@ -211,6 +242,12 @@ makedep(Config) when is_list(Config) -> [makedep,{makedep_output,Target}|IncludeOptions]), ?line {ok,Mf6} = file:read_file(Target), ?line BasicMf2 = makedep_canonicalize_result(Mf6, DataDir), + %% Rule with creating phony target. + ?line PhonyMfName = SimpleRootname ++ "-phony.mk", + ?line {ok,PhonyMf} = file:read_file(PhonyMfName), + ?line {ok,_,Mf7} = compile:file(Simple, + [binary,makedep,makedep_phony|IncludeOptions]), + ?line PhonyMf = makedep_canonicalize_result(Mf7, DataDir), ?line ok = file:delete(Target), ?line ok = file:del_dir(filename:dirname(Target)), diff --git a/lib/compiler/test/compile_SUITE_data/simple-phony.mk b/lib/compiler/test/compile_SUITE_data/simple-phony.mk new file mode 100644 index 0000000000..c84bcedd2a --- /dev/null +++ b/lib/compiler/test/compile_SUITE_data/simple-phony.mk @@ -0,0 +1,3 @@ +simple.beam: $(srcdir)/simple.erl $(srcdir)/include/simple.hrl + +$(srcdir)/include/simple.hrl: diff --git a/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S b/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S index c0bf04ed8f..cffb792920 100644 --- a/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S +++ b/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S @@ -19,10 +19,10 @@ {get_tuple_element,{x,0},1,{x,2}}. {get_tuple_element,{x,0},2,{x,3}}. {get_tuple_element,{x,0},3,{x,4}}. - {gc_bif,'+',{f,0},5,[{x,1},{x,2}],{x,0}}. - {gc_bif,'+',{f,0},5,[{x,0},{x,3}],{x,0}}. - {gc_bif,'+',{f,0},5,[{x,0},{x,4}],{x,0}}. - {gc_bif,'+',{f,0},5,[{x,0},{x,5}],{x,0}}. + {gc_bif,'+',{f,0},6,[{x,1},{x,2}],{x,0}}. + {gc_bif,'+',{f,0},6,[{x,0},{x,3}],{x,0}}. + {gc_bif,'+',{f,0},6,[{x,0},{x,4}],{x,0}}. + {gc_bif,'+',{f,0},6,[{x,0},{x,5}],{x,0}}. return. {label,3}. {badmatch,{x,0}}. diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl index 5e13a93c52..b53d0dba1d 100644 --- a/lib/compiler/test/misc_SUITE.erl +++ b/lib/compiler/test/misc_SUITE.erl @@ -190,6 +190,15 @@ silly_coverage(Config) when is_list(Config) -> {label,2}|non_proper_list]}],99}, ?line expect_error(fun() -> beam_block:module(BlockInput, []) end), + %% beam_type + TypeInput = {?MODULE,[{foo,0}],[], + [{function,foo,0,2, + [{label,1}, + {line,loc}, + {func_info,{atom,?MODULE},{atom,foo},0}, + {label,2}|non_proper_list]}],99}, + expect_error(fun() -> beam_type:module(TypeInput, []) end), + %% beam_except ExceptInput = {?MODULE,[{foo,0}],[], [{function,foo,0,2, diff --git a/lib/cosEvent/doc/src/Makefile b/lib/cosEvent/doc/src/Makefile index 6a9b4201d7..4a0a7429e9 100644 --- a/lib/cosEvent/doc/src/Makefile +++ b/lib/cosEvent/doc/src/Makefile @@ -137,13 +137,13 @@ debug opt: 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/cosEvent/src/Makefile b/lib/cosEvent/src/Makefile index f8e751f218..1825beacc5 100644 --- a/lib/cosEvent/src/Makefile +++ b/lib/cosEvent/src/Makefile @@ -197,14 +197,14 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) ../info $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) ../info "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosEvent/test/Makefile b/lib/cosEvent/test/Makefile index c3f07c156f..d5350d8293 100644 --- a/lib/cosEvent/test/Makefile +++ b/lib/cosEvent/test/Makefile @@ -141,11 +141,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ - $(RELSYSDIR)/$(IDLOUTDIR) + "$(RELSYSDIR)/$(IDLOUTDIR)" diff --git a/lib/cosEventDomain/doc/src/Makefile b/lib/cosEventDomain/doc/src/Makefile index cd159c623c..4e54b0fb55 100644 --- a/lib/cosEventDomain/doc/src/Makefile +++ b/lib/cosEventDomain/doc/src/Makefile @@ -131,13 +131,13 @@ debug opt: 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/cosEventDomain/src/Makefile b/lib/cosEventDomain/src/Makefile index 409cac47f1..41a31b332f 100644 --- a/lib/cosEventDomain/src/Makefile +++ b/lib/cosEventDomain/src/Makefile @@ -166,14 +166,14 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) ../info $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) ../info "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosEventDomain/test/Makefile b/lib/cosEventDomain/test/Makefile index 160c8565e8..9349067eb5 100644 --- a/lib/cosEventDomain/test/Makefile +++ b/lib/cosEventDomain/test/Makefile @@ -98,8 +98,8 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" diff --git a/lib/cosFileTransfer/doc/src/Makefile b/lib/cosFileTransfer/doc/src/Makefile index d9c68987e4..706dec00ce 100644 --- a/lib/cosFileTransfer/doc/src/Makefile +++ b/lib/cosFileTransfer/doc/src/Makefile @@ -134,13 +134,13 @@ debug opt: 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/cosFileTransfer/src/Makefile b/lib/cosFileTransfer/src/Makefile index 1d51304f6b..1bb765db73 100644 --- a/lib/cosFileTransfer/src/Makefile +++ b/lib/cosFileTransfer/src/Makefile @@ -176,12 +176,12 @@ $(TARGET_FILES): IDL-GENERATED include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosFileTransfer/test/Makefile b/lib/cosFileTransfer/test/Makefile index b46fb35356..177ad7616b 100644 --- a/lib/cosFileTransfer/test/Makefile +++ b/lib/cosFileTransfer/test/Makefile @@ -126,8 +126,8 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" diff --git a/lib/cosNotification/doc/src/Makefile b/lib/cosNotification/doc/src/Makefile index 5caee09ec5..d970818928 100644 --- a/lib/cosNotification/doc/src/Makefile +++ b/lib/cosNotification/doc/src/Makefile @@ -159,13 +159,13 @@ debug opt: 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/cosNotification/src/Makefile b/lib/cosNotification/src/Makefile index 92225c6cfd..7b8b73aa11 100644 --- a/lib/cosNotification/src/Makefile +++ b/lib/cosNotification/src/Makefile @@ -367,12 +367,12 @@ $(GEN_YECC_ERL_FILES) $(GEN_YECC_HRL_FILES): cosNotification_Grammar.yrl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(YECC_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(IDL_FILES) $(YECC_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(YECC_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(IDL_FILES) $(YECC_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosNotification/test/Makefile b/lib/cosNotification/test/Makefile index f509370430..17b60a8e9f 100644 --- a/lib/cosNotification/test/Makefile +++ b/lib/cosNotification/test/Makefile @@ -128,6 +128,7 @@ ERL_COMPILE_FLAGS += \ $(ERL_IDL_FLAGS) \ -pa $(ERL_TOP)/lib/orber/include \ -pa $(ERL_TOP)/internal_tools/test_server/ebin \ + -pa $(ERL_TOP)/lib/cosEvent/ebin \ -pa $(ERL_TOP)/lib/cosNotification/ebin \ -pa $(ERL_TOP)/lib/cosNotification/test/idl_output \ -pa $(ERL_TOP)/lib/cosTime/ebin \ @@ -182,11 +183,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ - $(RELSYSDIR)/$(IDLOUTDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) + "$(RELSYSDIR)/$(IDLOUTDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" diff --git a/lib/cosProperty/doc/src/Makefile b/lib/cosProperty/doc/src/Makefile index 50f2e06f6c..9bfacc27bd 100644 --- a/lib/cosProperty/doc/src/Makefile +++ b/lib/cosProperty/doc/src/Makefile @@ -136,13 +136,13 @@ debug opt: 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/cosProperty/src/Makefile b/lib/cosProperty/src/Makefile index 8060421b4e..cb2c56743c 100644 --- a/lib/cosProperty/src/Makefile +++ b/lib/cosProperty/src/Makefile @@ -176,12 +176,12 @@ $(TARGET_FILES): IDL-GENERATED include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosProperty/test/Makefile b/lib/cosProperty/test/Makefile index f6e0d0dbba..d85b8a655a 100644 --- a/lib/cosProperty/test/Makefile +++ b/lib/cosProperty/test/Makefile @@ -120,11 +120,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) -# $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" +# $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" # $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ -# $(RELSYSDIR)/$(IDLOUTDIR) +# "$(RELSYSDIR)/$(IDLOUTDIR)" diff --git a/lib/cosTime/doc/src/Makefile b/lib/cosTime/doc/src/Makefile index 4e97b07cf4..f89c022c4a 100644 --- a/lib/cosTime/doc/src/Makefile +++ b/lib/cosTime/doc/src/Makefile @@ -130,13 +130,13 @@ debug opt: 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/cosTime/src/Makefile b/lib/cosTime/src/Makefile index 18c25ca8f1..35f16edcef 100644 --- a/lib/cosTime/src/Makefile +++ b/lib/cosTime/src/Makefile @@ -195,12 +195,12 @@ $(TARGET_FILES): IDL-GENERATED include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosTime/test/Makefile b/lib/cosTime/test/Makefile index a07b27eecb..ab4d140537 100644 --- a/lib/cosTime/test/Makefile +++ b/lib/cosTime/test/Makefile @@ -126,11 +126,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) -# $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" +# $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" # $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ -# $(RELSYSDIR)/$(IDLOUTDIR) +# "$(RELSYSDIR)/$(IDLOUTDIR)" diff --git a/lib/cosTransactions/doc/src/Makefile b/lib/cosTransactions/doc/src/Makefile index 4341ec04a3..4edad9829b 100644 --- a/lib/cosTransactions/doc/src/Makefile +++ b/lib/cosTransactions/doc/src/Makefile @@ -134,13 +134,13 @@ debug opt: 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/cosTransactions/examples/Makefile b/lib/cosTransactions/examples/Makefile index 24cd12202a..0408cad75d 100644 --- a/lib/cosTransactions/examples/Makefile +++ b/lib/cosTransactions/examples/Makefile @@ -149,9 +149,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(ERL_FILES) $(JAVA_FILES) $(IDL_FILES) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(INETRC_EXAMPLE) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(ERL_FILES) $(JAVA_FILES) $(IDL_FILES) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(INETRC_EXAMPLE) "$(RELSYSDIR)/examples" @tar cf - java_output | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/cosTransactions/src/Makefile b/lib/cosTransactions/src/Makefile index 3c799ca0ca..0922a21641 100644 --- a/lib/cosTransactions/src/Makefile +++ b/lib/cosTransactions/src/Makefile @@ -170,11 +170,11 @@ $(TARGET_FILES): IDL-GENERATED include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILE) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILE) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosTransactions/test/Makefile b/lib/cosTransactions/test/Makefile index 0bc8c007da..80f4b6c624 100644 --- a/lib/cosTransactions/test/Makefile +++ b/lib/cosTransactions/test/Makefile @@ -142,11 +142,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(COVER_FILE) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ - $(RELSYSDIR)/$(IDLOUTDIR) + "$(RELSYSDIR)/$(IDLOUTDIR)" diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index 285537643e..cfc9a447e0 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -85,7 +85,7 @@ DYNAMIC_CRYPTO_LIB=@SSL_DYNAMIC_ONLY@ ifeq ($(DYNAMIC_CRYPTO_LIB),yes) SSL_DED_LD_RUNTIME_LIBRARY_PATH = @SSL_DED_LD_RUNTIME_LIBRARY_PATH@ -CRYPTO_LINK_LIB=$(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME) +CRYPTO_LINK_LIB=$(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) -l$(SSL_CRYPTO_LIBNAME) else SSL_DED_LD_RUNTIME_LIBRARY_PATH= CRYPTO_LINK_LIB=$(SSL_LIBDIR)/lib$(SSL_CRYPTO_LIBNAME).a @@ -133,11 +133,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/obj - $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_DATA) $(NIF_MAKEFILE) $(RELSYSDIR)/priv/obj - $(INSTALL_PROGRAM) $(OBJS) $(RELSYSDIR)/priv/obj - $(INSTALL_PROGRAM) $(NIF_LIB) $(RELSYSDIR)/priv/lib + $(INSTALL_DIR) "$(RELSYSDIR)/priv/obj" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" + $(INSTALL_DATA) $(NIF_MAKEFILE) "$(RELSYSDIR)/priv/obj" + $(INSTALL_PROGRAM) $(OBJS) "$(RELSYSDIR)/priv/obj" + $(INSTALL_PROGRAM) $(NIF_LIB) "$(RELSYSDIR)/priv/lib" release_docs_spec: diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 4dc62421d2..a24747a872 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-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 @@ -53,6 +53,10 @@ #include <openssl/evp.h> #include <openssl/hmac.h> +#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_SHA224) && defined(NID_sha224)\ + && !defined(OPENSSL_NO_SHA256) /* disabled like this in my sha.h (?) */ +# define HAVE_SHA224 +#endif #if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_SHA256) && defined(NID_sha256) # define HAVE_SHA256 #endif @@ -135,10 +139,18 @@ static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha224_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha224_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha224_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha224_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha256_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha256_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha256_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha384_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha384_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha384_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha384_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha512_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha512_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha512_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -149,6 +161,10 @@ static ERL_NIF_TERM md4_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv static ERL_NIF_TERM md4_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM md5_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha224_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha256_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha384_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM hmac_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -201,12 +217,33 @@ static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr, #endif /* OPENSSL_THREADS */ /* helpers */ +static void init_digest_types(ErlNifEnv* env); static void hmac_md5(unsigned char *key, int klen, unsigned char *dbuf, int dlen, unsigned char *hmacbuf); static void hmac_sha1(unsigned char *key, int klen, unsigned char *dbuf, int dlen, unsigned char *hmacbuf); +#ifdef HAVE_SHA224 +static void hmac_sha224(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf); +#endif +#ifdef HAVE_SHA256 +static void hmac_sha256(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf); +#endif +#ifdef HAVE_SHA384 +static void hmac_sha384(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf); +#endif +#ifdef HAVE_SHA512 +static void hmac_sha512(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf); +#endif static int library_refc = 0; /* number of users of this dynamic library */ @@ -220,10 +257,18 @@ static ErlNifFunc nif_funcs[] = { {"sha_init", 0, sha_init}, {"sha_update", 2, sha_update}, {"sha_final", 1, sha_final}, + {"sha224_nif", 1, sha224_nif}, + {"sha224_init_nif", 0, sha224_init_nif}, + {"sha224_update_nif", 2, sha224_update_nif}, + {"sha224_final_nif", 1, sha224_final_nif}, {"sha256_nif", 1, sha256_nif}, {"sha256_init_nif", 0, sha256_init_nif}, {"sha256_update_nif", 2, sha256_update_nif}, {"sha256_final_nif", 1, sha256_final_nif}, + {"sha384_nif", 1, sha384_nif}, + {"sha384_init_nif", 0, sha384_init_nif}, + {"sha384_update_nif", 2, sha384_update_nif}, + {"sha384_final_nif", 1, sha384_final_nif}, {"sha512_nif", 1, sha512_nif}, {"sha512_init_nif", 0, sha512_init_nif}, {"sha512_update_nif", 2, sha512_update_nif}, @@ -234,6 +279,10 @@ static ErlNifFunc nif_funcs[] = { {"md4_final", 1, md4_final}, {"md5_mac_n", 3, md5_mac_n}, {"sha_mac_n", 3, sha_mac_n}, + {"sha224_mac_nif", 3, sha224_mac_nif}, + {"sha256_mac_nif", 3, sha256_mac_nif}, + {"sha384_mac_nif", 3, sha384_mac_nif}, + {"sha512_mac_nif", 3, sha512_mac_nif}, {"hmac_init", 2, hmac_init}, {"hmac_update", 2, hmac_update}, {"hmac_final", 1, hmac_final}, @@ -287,10 +336,12 @@ ERL_NIF_INIT(crypto,nif_funcs,load,reload,upgrade,unload) #define SHA_CTX_LEN (sizeof(SHA_CTX)) #define SHA_LEN 20 #define SHA_LEN_96 12 +#define SHA224_LEN (224/8) #define SHA256_LEN (256/8) #define SHA384_LEN (384/8) #define SHA512_LEN (512/8) #define HMAC_INT_LEN 64 +#define HMAC_INT2_LEN 128 #define HMAC_IPAD 0x36 #define HMAC_OPAD 0x5c @@ -300,6 +351,7 @@ static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */ static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_sha; +static ERL_NIF_TERM atom_sha224; static ERL_NIF_TERM atom_sha256; static ERL_NIF_TERM atom_sha384; static ERL_NIF_TERM atom_sha512; @@ -320,6 +372,7 @@ static ERL_NIF_TERM atom_check_failed; static ERL_NIF_TERM atom_unknown; static ERL_NIF_TERM atom_none; static ERL_NIF_TERM atom_notsup; +static ERL_NIF_TERM atom_digest; static int is_ok_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info) @@ -374,6 +427,7 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_true = enif_make_atom(env,"true"); atom_false = enif_make_atom(env,"false"); atom_sha = enif_make_atom(env,"sha"); + atom_sha224 = enif_make_atom(env,"sha224"); atom_sha256 = enif_make_atom(env,"sha256"); atom_sha384 = enif_make_atom(env,"sha384"); atom_sha512 = enif_make_atom(env,"sha512"); @@ -393,6 +447,9 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_unknown = enif_make_atom(env,"unknown"); atom_none = enif_make_atom(env,"none"); atom_notsup = enif_make_atom(env,"notsup"); + atom_digest = enif_make_atom(env,"digest"); + + init_digest_types(env); *priv_data = NULL; library_refc++; @@ -557,6 +614,67 @@ static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ return ret; } +static ERL_NIF_TERM sha224_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data) */ +#ifdef HAVE_SHA224 + ErlNifBinary ibin; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) { + return enif_make_badarg(env); + } + SHA224((unsigned char *) ibin.data, ibin.size, + enif_make_new_binary(env,SHA224_LEN, &ret)); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha224_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ +#ifdef HAVE_SHA224 + ERL_NIF_TERM ret; + SHA224_Init((SHA256_CTX *) enif_make_new_binary(env, sizeof(SHA256_CTX), &ret)); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha224_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ +#ifdef HAVE_SHA224 + SHA256_CTX* new_ctx; + ErlNifBinary ctx_bin, data_bin; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX) + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + new_ctx = (SHA256_CTX*) enif_make_new_binary(env,sizeof(SHA256_CTX), &ret); + memcpy(new_ctx, ctx_bin.data, sizeof(SHA256_CTX)); + SHA224_Update(new_ctx, data_bin.data, data_bin.size); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha224_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) */ +#ifdef HAVE_SHA224 + ErlNifBinary ctx_bin; + SHA256_CTX ctx_clone; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX)) { + return enif_make_badarg(env); + } + memcpy(&ctx_clone, ctx_bin.data, sizeof(SHA256_CTX)); /* writable */ + SHA224_Final(enif_make_new_binary(env, SHA224_LEN, &ret), &ctx_clone); + return ret; +#else + return atom_notsup; +#endif +} + static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Data) */ #ifdef HAVE_SHA256 @@ -618,6 +736,67 @@ static ERL_NIF_TERM sha256_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER #endif } +static ERL_NIF_TERM sha384_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data) */ +#ifdef HAVE_SHA384 + ErlNifBinary ibin; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) { + return enif_make_badarg(env); + } + SHA384((unsigned char *) ibin.data, ibin.size, + enif_make_new_binary(env,SHA384_LEN, &ret)); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha384_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ +#ifdef HAVE_SHA384 + ERL_NIF_TERM ret; + SHA384_Init((SHA512_CTX *) enif_make_new_binary(env, sizeof(SHA512_CTX), &ret)); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha384_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ +#ifdef HAVE_SHA384 + SHA512_CTX* new_ctx; + ErlNifBinary ctx_bin, data_bin; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX) + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + new_ctx = (SHA512_CTX*) enif_make_new_binary(env,sizeof(SHA512_CTX), &ret); + memcpy(new_ctx, ctx_bin.data, sizeof(SHA512_CTX)); + SHA384_Update(new_ctx, data_bin.data, data_bin.size); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha384_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) */ +#ifdef HAVE_SHA384 + ErlNifBinary ctx_bin; + SHA512_CTX ctx_clone; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX)) { + return enif_make_badarg(env); + } + memcpy(&ctx_clone, ctx_bin.data, sizeof(SHA512_CTX)); /* writable */ + SHA384_Final(enif_make_new_binary(env, SHA384_LEN, &ret), &ctx_clone); + return ret; +#else + return atom_notsup; +#endif +} + static ERL_NIF_TERM sha512_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Data) */ #ifdef HAVE_SHA512 @@ -760,6 +939,95 @@ static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ return ret; } +static ERL_NIF_TERM sha224_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, MacSize) */ +#ifdef HAVE_SHA224 + unsigned char hmacbuf[SHA224_DIGEST_LENGTH]; + ErlNifBinary key, data; + unsigned mac_sz; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA224_DIGEST_LENGTH) { + return enif_make_badarg(env); + } + hmac_sha224(key.data, key.size, data.data, data.size, hmacbuf); + memcpy(enif_make_new_binary(env, mac_sz, &ret), + hmacbuf, mac_sz); + return ret; +#else + return atom_notsup; +#endif +} + +static ERL_NIF_TERM sha256_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, MacSize) */ +#ifdef HAVE_SHA256 + unsigned char hmacbuf[SHA256_DIGEST_LENGTH]; + ErlNifBinary key, data; + unsigned mac_sz; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA256_DIGEST_LENGTH) { + return enif_make_badarg(env); + } + hmac_sha256(key.data, key.size, data.data, data.size, hmacbuf); + memcpy(enif_make_new_binary(env, mac_sz, &ret), + hmacbuf, mac_sz); + return ret; +#else + return atom_notsup; +#endif +} + +static ERL_NIF_TERM sha384_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, MacSize) */ +#ifdef HAVE_SHA384 + unsigned char hmacbuf[SHA384_DIGEST_LENGTH]; + ErlNifBinary key, data; + unsigned mac_sz; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA384_DIGEST_LENGTH) { + return enif_make_badarg(env); + } + hmac_sha384(key.data, key.size, data.data, data.size, hmacbuf); + memcpy(enif_make_new_binary(env, mac_sz, &ret), + hmacbuf, mac_sz); + return ret; +#else + return atom_notsup; +#endif +} + + +static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, MacSize) */ +#ifdef HAVE_SHA512 + unsigned char hmacbuf[SHA512_DIGEST_LENGTH]; + ErlNifBinary key, data; + unsigned mac_sz; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA512_DIGEST_LENGTH) { + return enif_make_badarg(env); + } + hmac_sha512(key.data, key.size, data.data, data.size, hmacbuf); + memcpy(enif_make_new_binary(env, mac_sz, &ret), + hmacbuf, mac_sz); + return ret; +#else + return atom_notsup; +#endif +} + static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type, Key) */ ErlNifBinary key; @@ -768,6 +1036,18 @@ static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ const EVP_MD *md; if (argv[0] == atom_sha) md = EVP_sha1(); +#ifdef HAVE_SHA224 + else if (argv[0] == atom_sha224) md = EVP_sha224(); +#endif +#ifdef HAVE_SHA256 + else if (argv[0] == atom_sha256) md = EVP_sha256(); +#endif +#ifdef HAVE_SHA384 + else if (argv[0] == atom_sha384) md = EVP_sha384(); +#endif +#ifdef HAVE_SHA512 + else if (argv[0] == atom_sha512) md = EVP_sha512(); +#endif else if (argv[0] == atom_md5) md = EVP_md5(); else if (argv[0] == atom_ripemd160) md = EVP_ripemd160(); else goto badarg; @@ -954,8 +1234,7 @@ static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 16 || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16 - || !enif_inspect_iolist_as_binary(env, argv[2], &text) - || text.size % 16 != 0) { + || !enif_inspect_iolist_as_binary(env, argv[2], &text)) { return enif_make_badarg(env); } @@ -1208,14 +1487,43 @@ static int inspect_mpint(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifBinary* bin) } static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (DigestType,Data,Signature,Key=[P, Q, G, Y]) */ +{/* (DigestType|none, Data|{digest,Digest}, Signature,Key=[P, Q, G, Y]) */ ErlNifBinary data_bin, sign_bin; BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_y = NULL; unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + unsigned char* digest; ERL_NIF_TERM head, tail; + const ERL_NIF_TERM* tpl_terms; + int tpl_arity; DSA *dsa; int i; + if (argv[0] == atom_sha) { + if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &data_bin) + || data_bin.size != SHA_DIGEST_LENGTH) { + + return enif_make_badarg(env); + } + digest = data_bin.data; + } + else { + if (!inspect_mpint(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); + digest = hmacbuf; + } + } + else if (argv[0] == atom_none && enif_inspect_binary(env, argv[1], &data_bin) + && data_bin.size == SHA_DIGEST_LENGTH) { + digest = data_bin.data; + } + else { + return enif_make_badarg(env); + } + if (!inspect_mpint(env, argv[2], &sign_bin) || !enif_get_list_cell(env, argv[3], &head, &tail) || !get_bn_from_mpint(env, head, &dsa_p) @@ -1226,23 +1534,13 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv || !enif_get_list_cell(env, tail, &head, &tail) || !get_bn_from_mpint(env, head, &dsa_y) || !enif_is_empty_list(env,tail)) { - badarg: + if (dsa_p) BN_free(dsa_p); if (dsa_q) BN_free(dsa_q); if (dsa_g) BN_free(dsa_g); if (dsa_y) BN_free(dsa_y); return enif_make_badarg(env); } - if (argv[0] == atom_sha && inspect_mpint(env, argv[1], &data_bin)) { - SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); - } - else if (argv[0] == atom_none && enif_inspect_binary(env, argv[1], &data_bin) - && data_bin.size == SHA_DIGEST_LENGTH) { - memcpy(hmacbuf, data_bin.data, SHA_DIGEST_LENGTH); - } - else { - goto badarg; - } dsa = DSA_new(); dsa->p = dsa_p; @@ -1250,23 +1548,134 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv dsa->g = dsa_g; dsa->priv_key = NULL; dsa->pub_key = dsa_y; - i = DSA_verify(0, hmacbuf, SHA_DIGEST_LENGTH, + i = DSA_verify(0, digest, SHA_DIGEST_LENGTH, sign_bin.data+4, sign_bin.size-4, dsa); DSA_free(dsa); return(i > 0) ? atom_true : atom_false; } + +static void md5_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + MD5(in, in_len, out); +} +static void sha1_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + SHA1(in, in_len, out); +} +#ifdef HAVE_SHA224 +static void sha224_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + SHA224(in, in_len, out); +} +#endif +#ifdef HAVE_SHA256 +static void sha256_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + SHA256(in, in_len, out); +} +#endif +#ifdef HAVE_SHA384 +static void sha384_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + SHA384(in, in_len, out); +} +#endif +#ifdef HAVE_SHA512 +static void sha512_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + SHA512(in, in_len, out); +} +#endif + +struct digest_type_t { + const char* type_str; + unsigned len; /* 0 if notsup */ + int NID_type; + void (*funcp)(unsigned char* in, unsigned int in_len, unsigned char* out); + ERL_NIF_TERM type_atom; +}; + +struct digest_type_t digest_types[] = +{ + {"md5", MD5_DIGEST_LENGTH, NID_md5, md5_digest}, + {"sha", SHA_DIGEST_LENGTH, NID_sha1, sha1_digest}, + {"sha224", +#ifdef HAVE_SHA224 + SHA224_LEN, NID_sha224, sha224_digest +#else + 0 +#endif + }, + {"sha256", +#ifdef HAVE_SHA256 + SHA256_LEN, NID_sha256, sha256_digest +#else + 0 +#endif + }, + {"sha384", +#ifdef HAVE_SHA384 + SHA384_LEN, NID_sha384, sha384_digest +#else + 0 +#endif + }, + {"sha512", +#ifdef HAVE_SHA512 + SHA512_LEN, NID_sha512, sha512_digest +#else + 0 +#endif + }, + {NULL} +}; + +static void init_digest_types(ErlNifEnv* env) +{ + struct digest_type_t* p = digest_types; + + for (p = digest_types; p->type_str; p++) { + p->type_atom = enif_make_atom(env, p->type_str); + } + +} + +static struct digest_type_t* get_digest_type(ERL_NIF_TERM type) +{ + struct digest_type_t* p = NULL; + for (p = digest_types; p->type_str; p++) { + if (type == p->type_atom) { + return p; + } + } + return NULL; +} + static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Data, Signature, Key=[E,N]) */ +{/* (Type, Data|{digest,Digest}, Signature, Key=[E,N]) */ ErlNifBinary data_bin, sign_bin; unsigned char hmacbuf[SHA512_LEN]; ERL_NIF_TERM head, tail, ret; int i; - RSA* rsa = RSA_new(); + RSA* rsa; const ERL_NIF_TERM type = argv[0]; + const ERL_NIF_TERM* tpl_terms; + int tpl_arity; + struct digest_type_t* digp = NULL; + unsigned char* digest = NULL; + + digp = get_digest_type(type); + if (!digp) { + return enif_make_badarg(env); + } + if (!digp->len) { + return atom_notsup; + } - if (!inspect_mpint(env, argv[1], &data_bin) - || !inspect_mpint(env, argv[2], &sign_bin) + rsa = RSA_new(); + + if (!inspect_mpint(env, argv[2], &sign_bin) || !enif_get_list_cell(env, argv[3], &head, &tail) || !get_bn_from_mpint(env, head, &rsa->e) || !enif_get_list_cell(env, tail, &head, &tail) @@ -1274,59 +1683,38 @@ static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM || !enif_is_empty_list(env, tail)) { ret = enif_make_badarg(env); + goto done; } - else { - if (type == atom_sha) { - SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); - i = RSA_verify(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH, - sign_bin.data+4, sign_bin.size-4, rsa); - } - else if (type == atom_sha256) { - #ifdef HAVE_SHA256 - SHA256(data_bin.data+4, data_bin.size-4, hmacbuf); - i = RSA_verify(NID_sha256, hmacbuf, SHA256_LEN, - sign_bin.data+4, sign_bin.size-4, rsa); - #else - ret = atom_notsup; - goto done; - #endif - } - else if (type == atom_sha384) { - #ifdef HAVE_SHA384 - SHA384(data_bin.data+4, data_bin.size-4, hmacbuf); - i = RSA_verify(NID_sha384, hmacbuf, SHA384_LEN, - sign_bin.data+4, sign_bin.size-4, rsa); - #else - ret = atom_notsup; - goto done; - #endif - } - else if (type == atom_sha512) { - #ifdef HAVE_SHA512 - SHA512(data_bin.data+4, data_bin.size-4, hmacbuf); - i = RSA_verify(NID_sha512, hmacbuf, SHA512_LEN, - sign_bin.data+4, sign_bin.size-4, rsa); - #else - ret = atom_notsup; - goto done; - #endif - } - else if (type == atom_md5) { - MD5(data_bin.data+4, data_bin.size-4, hmacbuf); - i = RSA_verify(NID_md5, hmacbuf, MD5_DIGEST_LENGTH, - sign_bin.data+4, sign_bin.size-4, rsa); - } - else { + if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &data_bin) + || data_bin.size != digp->len) { + ret = enif_make_badarg(env); goto done; } - ret = (i==1 ? atom_true : atom_false); - } + digest = data_bin.data; + } + else if (inspect_mpint(env, argv[1], &data_bin)) { + digest = hmacbuf; + digp->funcp(data_bin.data+4, data_bin.size-4, digest); + } + else { + ret = enif_make_badarg(env); + goto done; + } + + i = RSA_verify(digp->NID_type, digest, digp->len, + sign_bin.data+4, sign_bin.size-4, rsa); + + ret = (i==1 ? atom_true : atom_false); + done: RSA_free(rsa); return ret; } + static ERL_NIF_TERM aes_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Key, IVec, Data, IsEncrypt) */ ErlNifBinary key_bin, ivec_bin, data_bin; @@ -1456,48 +1844,88 @@ static ERL_NIF_TERM rc2_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a return ret; } -static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type,Data,Key=[E,N,D]) */ - ErlNifBinary data_bin, ret_bin; +static int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) +{ + /* key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C] */ ERL_NIF_TERM head, tail; - unsigned char hmacbuf[SHA_DIGEST_LENGTH]; - unsigned rsa_s_len; - RSA *rsa = RSA_new(); - int i, is_sha; - if (argv[0] == atom_sha) is_sha = 1; - else if (argv[0] == atom_md5) is_sha = 0; - else goto badarg; - - if (!inspect_mpint(env,argv[1],&data_bin) - || !enif_get_list_cell(env, argv[2], &head, &tail) + if (!enif_get_list_cell(env, key, &head, &tail) || !get_bn_from_mpint(env, head, &rsa->e) || !enif_get_list_cell(env, tail, &head, &tail) || !get_bn_from_mpint(env, head, &rsa->n) || !enif_get_list_cell(env, tail, &head, &tail) || !get_bn_from_mpint(env, head, &rsa->d) - || !enif_is_empty_list(env,tail)) { - badarg: - RSA_free(rsa); + || (!enif_is_empty_list(env, tail) && + (!enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->q) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->dmp1) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->dmq1) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->iqmp) + || !enif_is_empty_list(env, tail)))) { + return 0; + } + return 1; +} + +static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Data|{digest,Digest}, Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C]) */ + ErlNifBinary data_bin, ret_bin; + unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + unsigned rsa_s_len; + RSA* rsa; + int i; + const ERL_NIF_TERM* tpl_terms; + int tpl_arity; + struct digest_type_t *digp; + unsigned char* digest; + + digp = get_digest_type(argv[0]); + if (!digp) { return enif_make_badarg(env); } - enif_alloc_binary(RSA_size(rsa), &ret_bin); - if (is_sha) { - SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); - ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, SHA_DIGEST_LENGTH); - i = RSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH, - ret_bin.data, &rsa_s_len, rsa); + if (!digp->len) { + return atom_notsup; + } + + if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &data_bin) + || data_bin.size != digp->len) { + + return enif_make_badarg(env); + } + digest = data_bin.data; } else { - MD5(data_bin.data+4, data_bin.size-4, hmacbuf); - ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, MD5_DIGEST_LENGTH); - i = RSA_sign(NID_md5, hmacbuf,MD5_DIGEST_LENGTH, - ret_bin.data, &rsa_s_len, rsa); + if (!inspect_mpint(env,argv[1],&data_bin)) { + return enif_make_badarg(env); + } + digest = hmacbuf; + digp->funcp(data_bin.data+4, data_bin.size-4, digest); } + + rsa = RSA_new(); + if (!get_rsa_private_key(env, argv[2], rsa)) { + RSA_free(rsa); + return enif_make_badarg(env); + } + + + enif_alloc_binary(RSA_size(rsa), &ret_bin); + + ERL_VALGRIND_ASSERT_MEM_DEFINED(digest, digp->len); + i = RSA_sign(digp->NID_type, digest, digp->len, + ret_bin.data, &rsa_s_len, rsa); + RSA_free(rsa); if (i) { ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, rsa_s_len); - if (rsa_s_len != data_bin.size) { + if (rsa_s_len != ret_bin.size) { enif_realloc_binary(&ret_bin, rsa_s_len); ERL_VALGRIND_ASSERT_MEM_DEFINED(ret_bin.data, rsa_s_len); } @@ -1509,15 +1937,49 @@ static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar } } + static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (DigesType, Data, Key=[P,Q,G,PrivKey]) */ +{/* (DigesType|none, Data|{digest,Digest}, Key=[P,Q,G,PrivKey]) */ ErlNifBinary data_bin, ret_bin; ERL_NIF_TERM head, tail; unsigned char hmacbuf[SHA_DIGEST_LENGTH]; unsigned int dsa_s_len; - DSA* dsa = DSA_new(); + const ERL_NIF_TERM* tpl_terms; + int tpl_arity; + unsigned char* digest = NULL; + DSA* dsa; int i; + if (argv[0] == atom_sha) { + if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &data_bin) + || data_bin.size != SHA_DIGEST_LENGTH) { + + return enif_make_badarg(env); + } + digest = data_bin.data; + } + else { + if (!inspect_mpint(env,argv[1],&data_bin)) { + return enif_make_badarg(env); + } + SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); + digest = hmacbuf; + } + } + else if (argv[0] == atom_none + && enif_inspect_binary(env,argv[1],&data_bin) + && data_bin.size == SHA_DIGEST_LENGTH) { + + digest = data_bin.data; + } + else { + return enif_make_badarg(env); + } + + dsa = DSA_new(); + dsa->pub_key = NULL; if (!enif_get_list_cell(env, argv[2], &head, &tail) || !get_bn_from_mpint(env, head, &dsa->p) @@ -1528,23 +1990,12 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar || !enif_get_list_cell(env, tail, &head, &tail) || !get_bn_from_mpint(env, head, &dsa->priv_key) || !enif_is_empty_list(env,tail)) { - goto badarg; - } - if (argv[0] == atom_sha && inspect_mpint(env, argv[1], &data_bin)) { - SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); - } - else if (argv[0] == atom_none && enif_inspect_binary(env,argv[1],&data_bin) - && data_bin.size == SHA_DIGEST_LENGTH) { - memcpy(hmacbuf, data_bin.data, SHA_DIGEST_LENGTH); - } - else { - badarg: DSA_free(dsa); return enif_make_badarg(env); } enif_alloc_binary(DSA_size(dsa), &ret_bin); - i = DSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH, + i = DSA_sign(NID_sha1, digest, SHA_DIGEST_LENGTH, ret_bin.data, &dsa_s_len, dsa); DSA_free(dsa); if (i) { @@ -1558,6 +2009,7 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar } } + static int rsa_pad(ERL_NIF_TERM term, int* padding) { if (term == atom_rsa_pkcs1_padding) { @@ -1623,20 +2075,13 @@ static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TER } static ERL_NIF_TERM rsa_private_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Data, PublKey=[E,N,D], Padding, IsEncrypt) */ +{/* (Data, Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Padding, IsEncrypt) */ ErlNifBinary data_bin, ret_bin; - ERL_NIF_TERM head, tail; int padding, i; RSA* rsa = RSA_new(); if (!enif_inspect_binary(env, argv[0], &data_bin) - || !enif_get_list_cell(env, argv[1], &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->e) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->n) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->d) - || !enif_is_empty_list(env,tail) + || !get_rsa_private_key(env, argv[1], rsa) || !rsa_pad(argv[2], &padding)) { RSA_free(rsa); @@ -2026,3 +2471,166 @@ static void hmac_sha1(unsigned char *key, int klen, SHA1_Final((unsigned char *) hmacbuf, &ctx); } +#ifdef HAVE_SHA224 +static void hmac_sha224(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf) +{ + SHA256_CTX ctx; + char ipad[HMAC_INT_LEN]; + char opad[HMAC_INT_LEN]; + unsigned char nkey[SHA224_DIGEST_LENGTH]; + int i; + + /* Change key if longer than 64 bytes */ + if (klen > HMAC_INT_LEN) { + SHA224(key, klen, nkey); + key = nkey; + klen = SHA224_DIGEST_LENGTH; + } + + memset(ipad, '\0', sizeof(ipad)); + memset(opad, '\0', sizeof(opad)); + memcpy(ipad, key, klen); + memcpy(opad, key, klen); + + for (i = 0; i < HMAC_INT_LEN; i++) { + ipad[i] ^= HMAC_IPAD; + opad[i] ^= HMAC_OPAD; + } + + /* inner SHA */ + SHA224_Init(&ctx); + SHA224_Update(&ctx, ipad, HMAC_INT_LEN); + SHA224_Update(&ctx, dbuf, dlen); + SHA224_Final((unsigned char *) hmacbuf, &ctx); + /* outer SHA */ + SHA224_Init(&ctx); + SHA224_Update(&ctx, opad, HMAC_INT_LEN); + SHA224_Update(&ctx, hmacbuf, SHA224_DIGEST_LENGTH); + SHA224_Final((unsigned char *) hmacbuf, &ctx); +} +#endif + +#ifdef HAVE_SHA256 +static void hmac_sha256(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf) +{ + SHA256_CTX ctx; + char ipad[HMAC_INT_LEN]; + char opad[HMAC_INT_LEN]; + unsigned char nkey[SHA256_DIGEST_LENGTH]; + int i; + + /* Change key if longer than 64 bytes */ + if (klen > HMAC_INT_LEN) { + SHA256(key, klen, nkey); + key = nkey; + klen = SHA256_DIGEST_LENGTH; + } + + memset(ipad, '\0', sizeof(ipad)); + memset(opad, '\0', sizeof(opad)); + memcpy(ipad, key, klen); + memcpy(opad, key, klen); + + for (i = 0; i < HMAC_INT_LEN; i++) { + ipad[i] ^= HMAC_IPAD; + opad[i] ^= HMAC_OPAD; + } + + /* inner SHA */ + SHA256_Init(&ctx); + SHA256_Update(&ctx, ipad, HMAC_INT_LEN); + SHA256_Update(&ctx, dbuf, dlen); + SHA256_Final((unsigned char *) hmacbuf, &ctx); + /* outer SHA */ + SHA256_Init(&ctx); + SHA256_Update(&ctx, opad, HMAC_INT_LEN); + SHA256_Update(&ctx, hmacbuf, SHA256_DIGEST_LENGTH); + SHA256_Final((unsigned char *) hmacbuf, &ctx); +} +#endif + +#ifdef HAVE_SHA384 +static void hmac_sha384(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf) +{ + SHA512_CTX ctx; + char ipad[HMAC_INT2_LEN]; + char opad[HMAC_INT2_LEN]; + unsigned char nkey[SHA384_DIGEST_LENGTH]; + int i; + + /* Change key if longer than 64 bytes */ + if (klen > HMAC_INT2_LEN) { + SHA384(key, klen, nkey); + key = nkey; + klen = SHA384_DIGEST_LENGTH; + } + + memset(ipad, '\0', sizeof(ipad)); + memset(opad, '\0', sizeof(opad)); + memcpy(ipad, key, klen); + memcpy(opad, key, klen); + + for (i = 0; i < HMAC_INT2_LEN; i++) { + ipad[i] ^= HMAC_IPAD; + opad[i] ^= HMAC_OPAD; + } + + /* inner SHA */ + SHA384_Init(&ctx); + SHA384_Update(&ctx, ipad, HMAC_INT2_LEN); + SHA384_Update(&ctx, dbuf, dlen); + SHA384_Final((unsigned char *) hmacbuf, &ctx); + /* outer SHA */ + SHA384_Init(&ctx); + SHA384_Update(&ctx, opad, HMAC_INT2_LEN); + SHA384_Update(&ctx, hmacbuf, SHA384_DIGEST_LENGTH); + SHA384_Final((unsigned char *) hmacbuf, &ctx); +} +#endif + +#ifdef HAVE_SHA512 +static void hmac_sha512(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf) +{ + SHA512_CTX ctx; + char ipad[HMAC_INT2_LEN]; + char opad[HMAC_INT2_LEN]; + unsigned char nkey[SHA512_DIGEST_LENGTH]; + int i; + + /* Change key if longer than 64 bytes */ + if (klen > HMAC_INT2_LEN) { + SHA512(key, klen, nkey); + key = nkey; + klen = SHA512_DIGEST_LENGTH; + } + + memset(ipad, '\0', sizeof(ipad)); + memset(opad, '\0', sizeof(opad)); + memcpy(ipad, key, klen); + memcpy(opad, key, klen); + + for (i = 0; i < HMAC_INT2_LEN; i++) { + ipad[i] ^= HMAC_IPAD; + opad[i] ^= HMAC_OPAD; + } + + /* inner SHA */ + SHA512_Init(&ctx); + SHA512_Update(&ctx, ipad, HMAC_INT2_LEN); + SHA512_Update(&ctx, dbuf, dlen); + SHA512_Final((unsigned char *) hmacbuf, &ctx); + /* outer SHA */ + SHA512_Init(&ctx); + SHA512_Update(&ctx, opad, HMAC_INT2_LEN); + SHA512_Update(&ctx, hmacbuf, SHA512_DIGEST_LENGTH); + SHA512_Final((unsigned char *) hmacbuf, &ctx); +} +#endif diff --git a/lib/crypto/doc/src/Makefile b/lib/crypto/doc/src/Makefile index 03aaba939b..00ae70fb4a 100644 --- a/lib/crypto/doc/src/Makefile +++ b/lib/crypto/doc/src/Makefile @@ -100,16 +100,16 @@ clean clean_docs clean_tex: 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 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 8cb893cd1c..045ad4c050 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -256,6 +256,57 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </desc> </func> <func> + <name>hash(Type, Data) -> Digest</name> + <fsummary></fsummary> + <type> + <v>Type = md4 | md5 | sha | sha224 | sha256 | sha384 | sha512</v> + <v>Data = iodata()</v> + <v>Digest = binary()</v> + </type> + <desc> + <p>Computes a message digest of type <c>Type</c> from <c>Data</c>.</p> + </desc> + </func> + <func> + <name>hash_init(Type) -> Context</name> + <fsummary></fsummary> + <type> + <v>Type = md4 | md5 | sha | sha224 | sha256 | sha384 | sha512</v> + </type> + <desc> + <p>Initializes the context for streaming hash operations. <c>Type</c> determines + which digest to use. The returned context should be used as argument + to <seealso marker="#hash_update/2">hash_update</seealso>.</p> + </desc> + </func> + <func> + <name>hash_update(Context, Data) -> NewContext</name> + <fsummary></fsummary> + <type> + <v>Data = iodata()</v> + </type> + <desc> + <p>Updates the digest represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> + must have been generated using <seealso marker="#hash_init/1">hash_init</seealso> + or a previous call to this function. <c>Data</c> can be any length. <c>NewContext</c> + must be passed into the next call to <c>hash_update</c> + or <seealso marker="#hash_final/1">hash_final</seealso>.</p> + </desc> + </func> + <func> + <name>hash_final(Context) -> Digest</name> + <fsummary></fsummary> + <type> + <v>Digest = binary()</v> + </type> + <desc> + <p>Finalizes the hash operation referenced by <c>Context</c> returned + from a previous call to <seealso marker="#hash_update/2">hash_update</seealso>. + The size of <c>Digest</c> is determined by the type of hash + function used to generate it.</p> + </desc> + </func> + <func> <name>md5_mac(Key, Data) -> Mac</name> <fsummary>Compute an <c>MD5 MAC</c>message authentification code</fsummary> <type> @@ -643,16 +694,14 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> <func> <name>aes_cfb_128_encrypt(Key, IVec, Text) -> Cipher</name> - <name>aes_cbc_128_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to AES in Cipher Feedback mode or Cipher Block Chaining mode</fsummary> + <fsummary>Encrypt <c>Text</c>according to AES in Cipher Feedback mode</fsummary> <type> <v>Key = Text = iolist() | binary()</v> <v>IVec = Cipher = binary()</v> </type> <desc> <p>Encrypts <c>Text</c> according to AES in Cipher Feedback - mode (CFB) or Cipher Block Chaining mode (CBC). <c>Text</c> - must be a multiple of 128 bits (16 bytes). <c>Key</c> is the + mode (CFB). <c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary initializing vector. The lengths of <c>Key</c> and <c>IVec</c> must be 128 bits (16 bytes).</p> @@ -660,15 +709,45 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </func> <func> <name>aes_cfb_128_decrypt(Key, IVec, Cipher) -> Text</name> + <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Feedback mode</fsummary> + <type> + <v>Key = Cipher = iolist() | binary()</v> + <v>IVec = Text = binary()</v> + </type> + <desc> + <p>Decrypts <c>Cipher</c> according to AES in Cipher Feedback Mode (CFB). + <c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary + initializing vector. <c>Key</c> and <c>IVec</c> must have + the same values as those used when encrypting. The lengths of + <c>Key</c> and <c>IVec</c> must be 128 bits (16 bytes).</p> + </desc> + </func> + <func> + <name>aes_cbc_128_encrypt(Key, IVec, Text) -> Cipher</name> + <fsummary>Encrypt <c>Text</c>according to AES in Cipher Block Chaining mode</fsummary> + <type> + <v>Key = Text = iolist() | binary()</v> + <v>IVec = Cipher = binary()</v> + </type> + <desc> + <p>Encrypts <c>Text</c> according to AES in Cipher Block Chaining + mode (CBC). <c>Text</c> + must be a multiple of 128 bits (16 bytes). <c>Key</c> is the + AES key, and <c>IVec</c> is an arbitrary initializing vector. + The lengths of <c>Key</c> and <c>IVec</c> must be 128 bits + (16 bytes).</p> + </desc> + </func> + <func> <name>aes_cbc_128_decrypt(Key, IVec, Cipher) -> Text</name> - <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Feedback mode or Cipher Block Chaining mode</fsummary> + <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Block Chaining mode</fsummary> <type> <v>Key = Cipher = iolist() | binary()</v> <v>IVec = Text = binary()</v> </type> <desc> - <p>Decrypts <c>Cipher</c> according to Cipher Feedback Mode (CFB) - or Cipher Block Chaining mode (CBC). + <p>Decrypts <c>Cipher</c> according to AES in Cipher Block + Chaining mode (CBC). <c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary initializing vector. <c>Key</c> and <c>IVec</c> must have the same values as those used when encrypting. <c>Cipher</c> @@ -865,46 +944,56 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </func> <func> - <name>rsa_sign(Data, Key) -> Signature</name> - <name>rsa_sign(DigestType, Data, Key) -> Signature</name> + <name>rsa_sign(DataOrDigest, Key) -> Signature</name> + <name>rsa_sign(DigestType, DataOrDigest, Key) -> Signature</name> <fsummary>Sign the data using rsa with the given key.</fsummary> <type> + <v>DataOrDigest = Data | {digest,Digest}</v> <v>Data = Mpint</v> - <v>Key = [E, N, D]</v> + <v>Digest = binary()</v> + <v>Key = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v> <v>E, N, D = Mpint</v> <d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and <c>D</c> is the private exponent.</d> - <v>DigestType = md5 | sha</v> + <v>P1, P2, E1, E2, C = Mpint</v> + <d>The longer key format contains redundant information that will make + the calculation faster. <c>P1,P2</c> are first and second prime factors. + <c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient. + Terminology is taken from RFC 3447.</d> + <v>DigestType = md5 | sha | sha224 | sha256 | sha384 | sha512</v> <d>The default <c>DigestType</c> is sha.</d> <v>Mpint = binary()</v> <v>Signature = binary()</v> </type> <desc> - <p>Calculates a <c>DigestType</c> digest of the <c>Data</c> - and creates a RSA signature with the private key <c>Key</c> - of the digest.</p> + <p>Creates a RSA signature with the private key <c>Key</c> + of a digest. The digest is either calculated as a + <c>DigestType</c> digest of <c>Data</c> or a precalculated + binary <c>Digest</c>.</p> </desc> </func> <func> - <name>rsa_verify(Data, Signature, Key) -> Verified</name> - <name>rsa_verify(DigestType, Data, Signature, Key) -> Verified </name> + <name>rsa_verify(DataOrDigest, Signature, Key) -> Verified</name> + <name>rsa_verify(DigestType, DataOrDigest, Signature, Key) -> Verified </name> <fsummary>Verify the digest and signature using rsa with given public key.</fsummary> <type> <v>Verified = boolean()</v> + <v>DataOrDigest = Data | {digest|Digest}</v> <v>Data, Signature = Mpint</v> + <v>Digest = binary()</v> <v>Key = [E, N]</v> <v>E, N = Mpint</v> <d>Where <c>E</c> is the public exponent and <c>N</c> is public modulus.</d> - <v>DigestType = md5 | sha | sha256 | sha384 | sha512</v> - <d> The default <c>DigestType</c> is sha.</d> + <v>DigestType = md5 | sha | sha224 | sha256 | sha384 | sha512</v> + <d>The default <c>DigestType</c> is sha.</d> <v>Mpint = binary()</v> </type> <desc> - <p>Calculates a <c>DigestType</c> digest of the <c>Data</c> - and verifies that the digest matches the RSA signature using the + <p>Verifies that a digest matches the RSA signature using the signer's public key <c>Key</c>. - </p> + The digest is either calculated as a <c>DigestType</c> + digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p> <p>May throw exception <c>notsup</c> in case the chosen <c>DigestType</c> is not supported by the underlying OpenSSL implementation.</p> </desc> @@ -943,10 +1032,15 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> <fsummary>Decrypts ChipherText using the private Key.</fsummary> <type> <v>ChipherText = binary()</v> - <v>PrivateKey = [E, N, D]</v> + <v>PrivateKey = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v> <v>E, N, D = Mpint</v> <d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and <c>D</c> is the private exponent.</d> + <v>P1, P2, E1, E2, C = Mpint</v> + <d>The longer key format contains redundant information that will make + the calculation faster. <c>P1,P2</c> are first and second prime factors. + <c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient. + Terminology is taken from RFC 3447.</d> <v>Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding | rsa_no_padding</v> <v>PlainText = binary()</v> </type> @@ -965,10 +1059,15 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> <fsummary>Encrypts Msg using the private Key.</fsummary> <type> <v>PlainText = binary()</v> - <v>PrivateKey = [E, N, D]</v> - <v>E, N, D = Mpint</v> + <v>PrivateKey = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v> + <v>E, N, D = Mpint</v> <d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and <c>D</c> is the private exponent.</d> + <v>P1, P2, E1, E2, C = Mpint</v> + <d>The longer key format contains redundant information that will make + the calculation faster. <c>P1,P2</c> are first and second prime factors. + <c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient. + Terminology is taken from RFC 3447.</d> <v>Padding = rsa_pkcs1_padding | rsa_no_padding</v> <v>ChipherText = binary()</v> </type> @@ -1007,45 +1106,52 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </func> <func> - <name>dss_sign(Data, Key) -> Signature</name> - <name>dss_sign(DigestType, Data, Key) -> Signature</name> + <name>dss_sign(DataOrDigest, Key) -> Signature</name> + <name>dss_sign(DigestType, DataOrDigest, Key) -> Signature</name> <fsummary>Sign the data using dsa with given private key.</fsummary> <type> - <v>DigestType = sha | none (default is sha)</v> - <v>Data = Mpint | ShaDigest</v> + <v>DigestType = sha</v> + <v>DataOrDigest = Mpint | {digest,Digest}</v> <v>Key = [P, Q, G, X]</v> <v>P, Q, G, X = Mpint</v> <d> Where <c>P</c>, <c>Q</c> and <c>G</c> are the dss parameters and <c>X</c> is the private key.</d> - <v>ShaDigest = binary() with length 20 bytes</v> + <v>Digest = binary() with length 20 bytes</v> <v>Signature = binary()</v> </type> <desc> - <p>Creates a DSS signature with the private key <c>Key</c> of a digest. - If <c>DigestType</c> is 'sha', the digest is calculated as SHA1 of <c>Data</c>. - If <c>DigestType</c> is 'none', <c>Data</c> is the precalculated SHA1 digest.</p> + <p>Creates a DSS signature with the private key <c>Key</c> of + a digest. The digest is either calculated as a SHA1 + digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p> + <p>A deprecated feature is having <c>DigestType = 'none'</c> + in which case <c>DataOrDigest</c> is a precalculated SHA1 + digest.</p> </desc> </func> <func> - <name>dss_verify(Data, Signature, Key) -> Verified</name> - <name>dss_verify(DigestType, Data, Signature, Key) -> Verified</name> + <name>dss_verify(DataOrDigest, Signature, Key) -> Verified</name> + <name>dss_verify(DigestType, DataOrDigest, Signature, Key) -> Verified</name> <fsummary>Verify the data and signature using dsa with given public key.</fsummary> <type> <v>Verified = boolean()</v> - <v>DigestType = sha | none</v> + <v>DigestType = sha</v> + <v>DataOrDigest = Mpint | {digest,Digest}</v> <v>Data = Mpint | ShaDigest</v> <v>Signature = Mpint</v> <v>Key = [P, Q, G, Y]</v> <v>P, Q, G, Y = Mpint</v> <d> Where <c>P</c>, <c>Q</c> and <c>G</c> are the dss parameters and <c>Y</c> is the public key.</d> - <v>ShaDigest = binary() with length 20 bytes</v> + <v>Digest = binary() with length 20 bytes</v> </type> <desc> - <p>Verifies that a digest matches the DSS signature using the public key <c>Key</c>. - If <c>DigestType</c> is 'sha', the digest is calculated as SHA1 of <c>Data</c>. - If <c>DigestType</c> is 'none', <c>Data</c> is the precalculated SHA1 digest.</p> + <p>Verifies that a digest matches the DSS signature using the + public key <c>Key</c>. The digest is either calculated as a SHA1 + digest of <c>Data</c> or is a precalculated binary <c>Digest</c>.</p> + <p>A deprecated feature is having <c>DigestType = 'none'</c> + in which case <c>DataOrDigest</c> is a precalculated SHA1 + digest binary.</p> </desc> </func> diff --git a/lib/crypto/src/Makefile b/lib/crypto/src/Makefile index 0e886ce8bf..ddafb9168f 100644 --- a/lib/crypto/src/Makefile +++ b/lib/crypto/src/Makefile @@ -83,11 +83,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) \ - $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(APPUP_TARGET) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index d7aac27825..0089e79a4f 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-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 @@ -22,12 +22,19 @@ -module(crypto). -export([start/0, stop/0, info/0, info_lib/0, version/0]). +-export([hash/2, hash_init/1, hash_update/2, hash_final/1]). -export([md4/1, md4_init/0, md4_update/2, md4_final/1]). -export([md5/1, md5_init/0, md5_update/2, md5_final/1]). -export([sha/1, sha_init/0, sha_update/2, sha_final/1]). +-export([sha224/1, sha224_init/0, sha224_update/2, sha224_final/1]). -export([sha256/1, sha256_init/0, sha256_update/2, sha256_final/1]). +-export([sha384/1, sha384_init/0, sha384_update/2, sha384_final/1]). -export([sha512/1, sha512_init/0, sha512_update/2, sha512_final/1]). -export([md5_mac/2, md5_mac_96/2, sha_mac/2, sha_mac/3, sha_mac_96/2]). +-export([sha224_mac/2, sha224_mac/3]). +-export([sha256_mac/2, sha256_mac/3]). +-export([sha384_mac/2, sha384_mac/3]). +-export([sha512_mac/2, sha512_mac/3]). -export([hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]). -export([des_cbc_encrypt/3, des_cbc_decrypt/3, des_cbc_ivec/1]). -export([des_ecb_encrypt/2, des_ecb_decrypt/2]). @@ -64,10 +71,13 @@ -define(FUNC_LIST, [md4, md4_init, md4_update, md4_final, md5, md5_init, md5_update, md5_final, sha, sha_init, sha_update, sha_final, - sha256, sha256_init, sha256_update, sha256_final, - sha512, sha512_init, sha512_update, sha512_final, + sha224, sha224_init, sha224_update, sha224_final, + sha256, sha256_init, sha256_update, sha256_final, + sha384, sha384_init, sha384_update, sha384_final, + sha512, sha512_init, sha512_update, sha512_final, md5_mac, md5_mac_96, sha_mac, sha_mac_96, + sha224_mac, sha256_mac, sha384_mac, sha512_mac, sha_mac_init, sha_mac_update, sha_mac_final, des_cbc_encrypt, des_cbc_decrypt, des_cfb_encrypt, des_cfb_decrypt, @@ -95,8 +105,9 @@ aes_ctr_stream_init, aes_ctr_stream_encrypt, aes_ctr_stream_decrypt, info_lib]). --type rsa_digest_type() :: 'md5' | 'sha' | 'sha256' | 'sha384' | 'sha512'. +-type rsa_digest_type() :: 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512'. -type dss_digest_type() :: 'none' | 'sha'. +-type data_or_digest() :: binary() | {digest, binary()}. -type crypto_integer() :: binary() | integer(). -define(nif_stub,nif_stub_error(?LINE)). @@ -171,7 +182,7 @@ info_lib() -> ?nif_stub. %% (no version): Driver implementation %% 2.0 : NIF implementation, requires OTP R14 version() -> ?CRYPTO_VSN. - + %% Below Key and Data are binaries or IO-lists. IVec is a binary. %% Output is always a binary. Context is a binary. @@ -179,6 +190,45 @@ version() -> ?CRYPTO_VSN. %% MESSAGE DIGESTS %% +-spec hash(_, iodata()) -> binary(). +hash(md5, Data) -> md5(Data); +hash(md4, Data) -> md4(Data); +hash(sha, Data) -> sha(Data); +hash(sha224, Data) -> sha224(Data); +hash(sha256, Data) -> sha256(Data); +hash(sha384, Data) -> sha384(Data); +hash(sha512, Data) -> sha512(Data). + +-spec hash_init('md5'|'md4'|'sha'|'sha224'|'sha256'|'sha384'|'sha512') -> any(). + +hash_init(md5) -> {md5, md5_init()}; +hash_init(md4) -> {md4, md4_init()}; +hash_init(sha) -> {sha, sha_init()}; +hash_init(sha224) -> {sha224, sha224_init()}; +hash_init(sha256) -> {sha256, sha256_init()}; +hash_init(sha384) -> {sha384, sha384_init()}; +hash_init(sha512) -> {sha512, sha512_init()}. + +-spec hash_update(_, iodata()) -> any(). + +hash_update({md5,Context}, Data) -> {md5, md5_update(Context,Data)}; +hash_update({md4,Context}, Data) -> {md4, md4_update(Context,Data)}; +hash_update({sha,Context}, Data) -> {sha, sha_update(Context,Data)}; +hash_update({sha224,Context}, Data) -> {sha224, sha224_update(Context,Data)}; +hash_update({sha256,Context}, Data) -> {sha256, sha256_update(Context,Data)}; +hash_update({sha384,Context}, Data) -> {sha384, sha384_update(Context,Data)}; +hash_update({sha512,Context}, Data) -> {sha512, sha512_update(Context,Data)}. + +-spec hash_final(_) -> binary(). + +hash_final({md5,Context}) -> md5_final(Context); +hash_final({md4,Context}) -> md4_final(Context); +hash_final({sha,Context}) -> sha_final(Context); +hash_final({sha224,Context}) -> sha224_final(Context); +hash_final({sha256,Context}) -> sha256_final(Context); +hash_final({sha384,Context}) -> sha384_final(Context); +hash_final({sha512,Context}) -> sha512_final(Context). + %% %% MD5 %% @@ -220,6 +270,40 @@ sha_update(_Context, _Data) -> ?nif_stub. sha_final(_Context) -> ?nif_stub. % +%% SHA224 +%% +-spec sha224(iodata()) -> binary(). +-spec sha224_init() -> binary(). +-spec sha224_update(binary(), iodata()) -> binary(). +-spec sha224_final(binary()) -> binary(). + +sha224(Data) -> + case sha224_nif(Data) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha224_init() -> + case sha224_init_nif() of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha224_update(Context, Data) -> + case sha224_update_nif(Context, Data) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha224_final(Context) -> + case sha224_final_nif(Context) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha224_nif(_Data) -> ?nif_stub. +sha224_init_nif() -> ?nif_stub. +sha224_update_nif(_Context, _Data) -> ?nif_stub. +sha224_final_nif(_Context) -> ?nif_stub. + +% %% SHA256 %% -spec sha256(iodata()) -> binary(). @@ -254,6 +338,40 @@ sha256_update_nif(_Context, _Data) -> ?nif_stub. sha256_final_nif(_Context) -> ?nif_stub. % +%% SHA384 +%% +-spec sha384(iodata()) -> binary(). +-spec sha384_init() -> binary(). +-spec sha384_update(binary(), iodata()) -> binary(). +-spec sha384_final(binary()) -> binary(). + +sha384(Data) -> + case sha384_nif(Data) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha384_init() -> + case sha384_init_nif() of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha384_update(Context, Data) -> + case sha384_update_nif(Context, Data) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha384_final(Context) -> + case sha384_final_nif(Context) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha384_nif(_Data) -> ?nif_stub. +sha384_init_nif() -> ?nif_stub. +sha384_update_nif(_Context, _Data) -> ?nif_stub. +sha384_final_nif(_Context) -> ?nif_stub. + +% %% SHA512 %% -spec sha512(iodata()) -> binary(). @@ -336,6 +454,70 @@ sha_mac_96(Key, Data) -> sha_mac_n(_Key,_Data,_MacSz) -> ?nif_stub. %% +%% SHA224_MAC +%% +-spec sha224_mac(iodata(), iodata()) -> binary(). + +sha224_mac(Key, Data) -> + sha224_mac(Key, Data, 224 div 8). + +sha224_mac(Key, Data, Size) -> + case sha224_mac_nif(Key, Data, Size) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha224_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub. + +%% +%% SHA256_MAC +%% +-spec sha256_mac(iodata(), iodata()) -> binary(). + +sha256_mac(Key, Data) -> + sha256_mac(Key, Data, 256 div 8). + +sha256_mac(Key, Data, Size) -> + case sha256_mac_nif(Key, Data, Size) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha256_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub. + +%% +%% SHA384_MAC +%% +-spec sha384_mac(iodata(), iodata()) -> binary(). + +sha384_mac(Key, Data) -> + sha384_mac(Key, Data, 384 div 8). + +sha384_mac(Key, Data, Size) -> + case sha384_mac_nif(Key, Data, Size) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha384_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub. + +%% +%% SHA512_MAC +%% +-spec sha512_mac(iodata(), iodata()) -> binary(). + +sha512_mac(Key, Data) -> + sha512_mac(Key, Data, 512 div 8). + +sha512_mac(Key, Data, MacSz) -> + case sha512_mac_nif(Key, Data, MacSz) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha512_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub. + +%% %% CRYPTO FUNCTIONS %% @@ -576,10 +758,10 @@ mod_exp_nif(_Base,_Exp,_Mod) -> ?nif_stub. %% %% DSS, RSA - verify %% --spec dss_verify(binary(), binary(), [binary()]) -> boolean(). --spec dss_verify(dss_digest_type(), binary(), binary(), [binary()]) -> boolean(). --spec rsa_verify(binary(), binary(), [binary()]) -> boolean(). --spec rsa_verify(rsa_digest_type(), binary(), binary(), [binary()]) -> +-spec dss_verify(data_or_digest(), binary(), [binary()]) -> boolean(). +-spec dss_verify(dss_digest_type(), data_or_digest(), binary(), [binary()]) -> boolean(). +-spec rsa_verify(data_or_digest(), binary(), [binary()]) -> boolean(). +-spec rsa_verify(rsa_digest_type(), data_or_digest(), binary(), [binary()]) -> boolean(). %% Key = [P,Q,G,Y] P,Q,G=DSSParams Y=PublicKey @@ -590,8 +772,8 @@ dss_verify(_Type,_Data,_Signature,_Key) -> ?nif_stub. % Key = [E,N] E=PublicExponent N=PublicModulus rsa_verify(Data,Signature,Key) -> rsa_verify_nif(sha, Data,Signature,Key). -rsa_verify(Type, Data, Signature, Key) -> - case rsa_verify_nif(Type, Data, Signature, Key) of +rsa_verify(Type, DataOrDigest, Signature, Key) -> + case rsa_verify_nif(Type, DataOrDigest, Signature, Key) of notsup -> erlang:error(notsup); Bool -> Bool end. @@ -603,27 +785,27 @@ rsa_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub. %% DSS, RSA - sign %% %% Key = [P,Q,G,X] P,Q,G=DSSParams X=PrivateKey --spec dss_sign(binary(), [binary()]) -> binary(). --spec dss_sign(dss_digest_type(), binary(), [binary()]) -> binary(). --spec rsa_sign(binary(), [binary()]) -> binary(). --spec rsa_sign(rsa_digest_type(), binary(), [binary()]) -> binary(). - -dss_sign(Data,Key) -> - dss_sign(sha,Data,Key). -dss_sign(Type, Data, Key) -> - case dss_sign_nif(Type,Data,Key) of - error -> erlang:error(badkey, [Data, Key]); +-spec dss_sign(data_or_digest(), [binary()]) -> binary(). +-spec dss_sign(dss_digest_type(), data_or_digest(), [binary()]) -> binary(). +-spec rsa_sign(data_or_digest(), [binary()]) -> binary(). +-spec rsa_sign(rsa_digest_type(), data_or_digest(), [binary()]) -> binary(). + +dss_sign(DataOrDigest,Key) -> + dss_sign(sha,DataOrDigest,Key). +dss_sign(Type, DataOrDigest, Key) -> + case dss_sign_nif(Type,DataOrDigest,Key) of + error -> erlang:error(badkey, [DataOrDigest, Key]); Sign -> Sign end. dss_sign_nif(_Type,_Data,_Key) -> ?nif_stub. %% Key = [E,N,D] E=PublicExponent N=PublicModulus D=PrivateExponent -rsa_sign(Data,Key) -> - rsa_sign(sha, Data, Key). -rsa_sign(Type, Data, Key) -> - case rsa_sign_nif(Type,Data,Key) of - error -> erlang:error(badkey, [Type,Data,Key]); +rsa_sign(DataOrDigest,Key) -> + rsa_sign(sha, DataOrDigest, Key). +rsa_sign(Type, DataOrDigest, Key) -> + case rsa_sign_nif(Type,DataOrDigest,Key) of + error -> erlang:error(badkey, [Type,DataOrDigest,Key]); Sign -> Sign end. diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile index 3150bd472d..ec8136b455 100644 --- a/lib/crypto/test/Makefile +++ b/lib/crypto/test/Makefile @@ -75,9 +75,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: $(TEST_TARGET) - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 627c966dfb..1b5bc44dde 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-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 @@ -33,9 +33,12 @@ sha_update/1, hmac_update_sha/1, hmac_update_sha_n/1, + hmac_update_sha256/1, + hmac_update_sha512/1, hmac_update_md5/1, hmac_update_md5_io/1, hmac_update_md5_n/1, + hmac_rfc4231/1, sha256/1, sha256_update/1, sha512/1, @@ -61,7 +64,9 @@ rsa_verify_test/1, dsa_verify_test/1, rsa_sign_test/1, + rsa_sign_hash_test/1, dsa_sign_test/1, + dsa_sign_hash_test/1, rsa_encrypt_decrypt/1, dh/1, exor_test/1, @@ -82,13 +87,15 @@ groups() -> {rest, [], [md5, md5_update, md4, md4_update, md5_mac, md5_mac_io, sha, sha_update, - hmac_update_sha, hmac_update_sha_n, hmac_update_md5_n, - hmac_update_md5_io, hmac_update_md5, + hmac_update_sha, hmac_update_sha_n, hmac_update_sha256, hmac_update_sha512, + hmac_update_md5_n, hmac_update_md5_io, hmac_update_md5, + hmac_rfc4231, des_cbc, aes_cfb, aes_cbc, aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_ecb, rand_uniform_test, strong_rand_test, rsa_verify_test, dsa_verify_test, rsa_sign_test, - dsa_sign_test, rsa_encrypt_decrypt, dh, exor_test, + rsa_sign_hash_test, dsa_sign_test, dsa_sign_hash_test, + rsa_encrypt_decrypt, dh, exor_test, rc4_test, rc4_stream_test, mod_exp_test, blowfish_cfb64, smp]}]. @@ -335,6 +342,44 @@ hmac_update_sha(Config) when is_list(Config) -> ?line Mac = crypto:hmac_final(Ctx3), ?line Exp = crypto:sha_mac(Key, lists:flatten([Data, Data2])), ?line m(Exp, Mac). + +hmac_update_sha256(doc) -> + ["Generate an SHA256 HMAC using hmac_init, hmac_update, and hmac_final. " + "Expected values for examples are generated using crypto:sha256_mac." ]; +hmac_update_sha256(suite) -> + []; +hmac_update_sha256(Config) when is_list(Config) -> + ?line Key = hexstr2bin("00010203101112132021222330313233" + "04050607141516172425262734353637" + "08090a0b18191a1b28292a2b38393a3b" + "0c0d0e0f1c1d1e1f2c2d2e2f3c3d3e3f"), + ?line Data = "Sampl", + ?line Data2 = "e #1", + ?line Ctx = crypto:hmac_init(sha256, Key), + ?line Ctx2 = crypto:hmac_update(Ctx, Data), + ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), + ?line Mac = crypto:hmac_final(Ctx3), + ?line Exp = crypto:sha256_mac(Key, lists:flatten([Data, Data2])), + ?line m(Exp, Mac). + +hmac_update_sha512(doc) -> + ["Generate an SHA512 HMAC using hmac_init, hmac_update, and hmac_final. " + "Expected values for examples are generated using crypto:sha512_mac." ]; +hmac_update_sha512(suite) -> + []; +hmac_update_sha512(Config) when is_list(Config) -> + ?line Key = hexstr2bin("00010203101112132021222330313233" + "04050607141516172425262734353637" + "08090a0b18191a1b28292a2b38393a3b" + "0c0d0e0f1c1d1e1f2c2d2e2f3c3d3e3f"), + ?line Data = "Sampl", + ?line Data2 = "e #1", + ?line Ctx = crypto:hmac_init(sha512, Key), + ?line Ctx2 = crypto:hmac_update(Ctx, Data), + ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), + ?line Mac = crypto:hmac_final(Ctx3), + ?line Exp = crypto:sha512_mac(Key, lists:flatten([Data, Data2])), + ?line m(Exp, Mac). hmac_update_md5(doc) -> ["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. " @@ -354,7 +399,272 @@ hmac_update_md5(Config) when is_list(Config) -> ?line Mac2 = crypto:hmac_final(CtxD), ?line Exp2 = crypto:md5_mac(Key2, lists:flatten([Long1, Long2, Long3])), ?line m(Exp2, Mac2). + +hmac_rfc4231(doc) -> + ["Generate an HMAC using crypto:shaXXX_mac and hmac_init, hmac_update, and hmac_final. " + "Testvectors are take from RFC4231." ]; +hmac_rfc4231(suite) -> + []; +hmac_rfc4231(Config) when is_list(Config) -> + %% Test Case 1 + Case1Key = binary:copy(<<16#0b>>, 20), + Case1Data = <<"Hi There">>, + Case1Exp224 = hexstr2bin("896fb1128abbdf196832107cd49df33f" + "47b4b1169912ba4f53684b22"), + Case1Exp256 = hexstr2bin("b0344c61d8db38535ca8afceaf0bf12b" + "881dc200c9833da726e9376c2e32cff7"), + Case1Exp384 = hexstr2bin("afd03944d84895626b0825f4ab46907f" + "15f9dadbe4101ec682aa034c7cebc59c" + "faea9ea9076ede7f4af152e8b2fa9cb6"), + Case1Exp512 = hexstr2bin("87aa7cdea5ef619d4ff0b4241a1d6cb0" + "2379f4e2ce4ec2787ad0b30545e17cde" + "daa833b7d6b8a702038b274eaea3f4e4" + "be9d914eeb61f1702e696c203a126854"), + + ?line Case1Ctx224 = crypto:hmac_init(sha224, Case1Key), + ?line Case1Ctx224_2 = crypto:hmac_update(Case1Ctx224, Case1Data), + ?line Case1Mac224_1 = crypto:hmac_final(Case1Ctx224_2), + ?line Case1Mac224_2 = crypto:sha224_mac(Case1Key, Case1Data), + ?line m(Case1Exp224, Case1Mac224_1), + ?line m(Case1Exp224, Case1Mac224_2), + + ?line Case1Ctx256 = crypto:hmac_init(sha256, Case1Key), + ?line Case1Ctx256_2 = crypto:hmac_update(Case1Ctx256, Case1Data), + ?line Case1Mac256_1 = crypto:hmac_final(Case1Ctx256_2), + ?line Case1Mac256_2 = crypto:sha256_mac(Case1Key, Case1Data), + ?line m(Case1Exp256, Case1Mac256_1), + ?line m(Case1Exp256, Case1Mac256_2), + + ?line Case1Ctx384 = crypto:hmac_init(sha384, Case1Key), + ?line Case1Ctx384_2 = crypto:hmac_update(Case1Ctx384, Case1Data), + ?line Case1Mac384_1 = crypto:hmac_final(Case1Ctx384_2), + ?line Case1Mac384_2 = crypto:sha384_mac(Case1Key, Case1Data), + ?line m(Case1Exp384, Case1Mac384_1), + ?line m(Case1Exp384, Case1Mac384_2), + + ?line Case1Ctx512 = crypto:hmac_init(sha512, Case1Key), + ?line Case1Ctx512_2 = crypto:hmac_update(Case1Ctx512, Case1Data), + ?line Case1Mac512_1 = crypto:hmac_final(Case1Ctx512_2), + ?line Case1Mac512_2 = crypto:sha512_mac(Case1Key, Case1Data), + ?line m(Case1Exp512, Case1Mac512_1), + ?line m(Case1Exp512, Case1Mac512_2), + + %% Test Case 2 + Case2Key = <<"Jefe">>, + Case2Data = <<"what do ya want for nothing?">>, + Case2Exp224 = hexstr2bin("a30e01098bc6dbbf45690f3a7e9e6d0f" + "8bbea2a39e6148008fd05e44"), + Case2Exp256 = hexstr2bin("5bdcc146bf60754e6a042426089575c7" + "5a003f089d2739839dec58b964ec3843"), + Case2Exp384 = hexstr2bin("af45d2e376484031617f78d2b58a6b1b" + "9c7ef464f5a01b47e42ec3736322445e" + "8e2240ca5e69e2c78b3239ecfab21649"), + Case2Exp512 = hexstr2bin("164b7a7bfcf819e2e395fbe73b56e0a3" + "87bd64222e831fd610270cd7ea250554" + "9758bf75c05a994a6d034f65f8f0e6fd" + "caeab1a34d4a6b4b636e070a38bce737"), + + ?line Case2Ctx224 = crypto:hmac_init(sha224, Case2Key), + ?line Case2Ctx224_2 = crypto:hmac_update(Case2Ctx224, Case2Data), + ?line Case2Mac224_1 = crypto:hmac_final(Case2Ctx224_2), + ?line Case2Mac224_2 = crypto:sha224_mac(Case2Key, Case2Data), + ?line m(Case2Exp224, Case2Mac224_1), + ?line m(Case2Exp224, Case2Mac224_2), + + ?line Case2Ctx256 = crypto:hmac_init(sha256, Case2Key), + ?line Case2Ctx256_2 = crypto:hmac_update(Case2Ctx256, Case2Data), + ?line Case2Mac256_1 = crypto:hmac_final(Case2Ctx256_2), + ?line Case2Mac256_2 = crypto:sha256_mac(Case2Key, Case2Data), + ?line m(Case2Exp256, Case2Mac256_1), + ?line m(Case2Exp256, Case2Mac256_2), + + ?line Case2Ctx384 = crypto:hmac_init(sha384, Case2Key), + ?line Case2Ctx384_2 = crypto:hmac_update(Case2Ctx384, Case2Data), + ?line Case2Mac384_1 = crypto:hmac_final(Case2Ctx384_2), + ?line Case2Mac384_2 = crypto:sha384_mac(Case2Key, Case2Data), + ?line m(Case2Exp384, Case2Mac384_1), + ?line m(Case2Exp384, Case2Mac384_2), + + ?line Case2Ctx512 = crypto:hmac_init(sha512, Case2Key), + ?line Case2Ctx512_2 = crypto:hmac_update(Case2Ctx512, Case2Data), + ?line Case2Mac512_1 = crypto:hmac_final(Case2Ctx512_2), + ?line Case2Mac512_2 = crypto:sha512_mac(Case2Key, Case2Data), + ?line m(Case2Exp512, Case2Mac512_1), + ?line m(Case2Exp512, Case2Mac512_2), + + %% Test Case 3 + Case3Key = binary:copy(<<16#aa>>, 20), + Case3Data = binary:copy(<<16#dd>>, 50), + Case3Exp224 = hexstr2bin("7fb3cb3588c6c1f6ffa9694d7d6ad264" + "9365b0c1f65d69d1ec8333ea"), + Case3Exp256 = hexstr2bin("773ea91e36800e46854db8ebd09181a7" + "2959098b3ef8c122d9635514ced565fe"), + Case3Exp384 = hexstr2bin("88062608d3e6ad8a0aa2ace014c8a86f" + "0aa635d947ac9febe83ef4e55966144b" + "2a5ab39dc13814b94e3ab6e101a34f27"), + Case3Exp512 = hexstr2bin("fa73b0089d56a284efb0f0756c890be9" + "b1b5dbdd8ee81a3655f83e33b2279d39" + "bf3e848279a722c806b485a47e67c807" + "b946a337bee8942674278859e13292fb"), + + ?line Case3Ctx224 = crypto:hmac_init(sha224, Case3Key), + ?line Case3Ctx224_2 = crypto:hmac_update(Case3Ctx224, Case3Data), + ?line Case3Mac224_1 = crypto:hmac_final(Case3Ctx224_2), + ?line Case3Mac224_2 = crypto:sha224_mac(Case3Key, Case3Data), + ?line m(Case3Exp224, Case3Mac224_1), + ?line m(Case3Exp224, Case3Mac224_2), + + ?line Case3Ctx256 = crypto:hmac_init(sha256, Case3Key), + ?line Case3Ctx256_2 = crypto:hmac_update(Case3Ctx256, Case3Data), + ?line Case3Mac256_1 = crypto:hmac_final(Case3Ctx256_2), + ?line Case3Mac256_2 = crypto:sha256_mac(Case3Key, Case3Data), + ?line m(Case3Exp256, Case3Mac256_1), + ?line m(Case3Exp256, Case3Mac256_2), + + ?line Case3Ctx384 = crypto:hmac_init(sha384, Case3Key), + ?line Case3Ctx384_2 = crypto:hmac_update(Case3Ctx384, Case3Data), + ?line Case3Mac384_1 = crypto:hmac_final(Case3Ctx384_2), + ?line Case3Mac384_2 = crypto:sha384_mac(Case3Key, Case3Data), + ?line m(Case3Exp384, Case3Mac384_1), + ?line m(Case3Exp384, Case3Mac384_2), + + ?line Case3Ctx512 = crypto:hmac_init(sha512, Case3Key), + ?line Case3Ctx512_2 = crypto:hmac_update(Case3Ctx512, Case3Data), + ?line Case3Mac512_1 = crypto:hmac_final(Case3Ctx512_2), + ?line Case3Mac512_2 = crypto:sha512_mac(Case3Key, Case3Data), + ?line m(Case3Exp512, Case3Mac512_1), + ?line m(Case3Exp512, Case3Mac512_2), + + %% Test Case 4 + Case4Key = list_to_binary(lists:seq(1, 16#19)), + Case4Data = binary:copy(<<16#cd>>, 50), + Case4Exp224 = hexstr2bin("6c11506874013cac6a2abc1bb382627c" + "ec6a90d86efc012de7afec5a"), + Case4Exp256 = hexstr2bin("82558a389a443c0ea4cc819899f2083a" + "85f0faa3e578f8077a2e3ff46729665b"), + Case4Exp384 = hexstr2bin("3e8a69b7783c25851933ab6290af6ca7" + "7a9981480850009cc5577c6e1f573b4e" + "6801dd23c4a7d679ccf8a386c674cffb"), + Case4Exp512 = hexstr2bin("b0ba465637458c6990e5a8c5f61d4af7" + "e576d97ff94b872de76f8050361ee3db" + "a91ca5c11aa25eb4d679275cc5788063" + "a5f19741120c4f2de2adebeb10a298dd"), + + ?line Case4Ctx224 = crypto:hmac_init(sha224, Case4Key), + ?line Case4Ctx224_2 = crypto:hmac_update(Case4Ctx224, Case4Data), + ?line Case4Mac224_1 = crypto:hmac_final(Case4Ctx224_2), + ?line Case4Mac224_2 = crypto:sha224_mac(Case4Key, Case4Data), + ?line m(Case4Exp224, Case4Mac224_1), + ?line m(Case4Exp224, Case4Mac224_2), + + ?line Case4Ctx256 = crypto:hmac_init(sha256, Case4Key), + ?line Case4Ctx256_2 = crypto:hmac_update(Case4Ctx256, Case4Data), + ?line Case4Mac256_1 = crypto:hmac_final(Case4Ctx256_2), + ?line Case4Mac256_2 = crypto:sha256_mac(Case4Key, Case4Data), + ?line m(Case4Exp256, Case4Mac256_1), + ?line m(Case4Exp256, Case4Mac256_2), + + ?line Case4Ctx384 = crypto:hmac_init(sha384, Case4Key), + ?line Case4Ctx384_2 = crypto:hmac_update(Case4Ctx384, Case4Data), + ?line Case4Mac384_1 = crypto:hmac_final(Case4Ctx384_2), + ?line Case4Mac384_2 = crypto:sha384_mac(Case4Key, Case4Data), + ?line m(Case4Exp384, Case4Mac384_1), + ?line m(Case4Exp384, Case4Mac384_2), + + ?line Case4Ctx512 = crypto:hmac_init(sha512, Case4Key), + ?line Case4Ctx512_2 = crypto:hmac_update(Case4Ctx512, Case4Data), + ?line Case4Mac512_1 = crypto:hmac_final(Case4Ctx512_2), + ?line Case4Mac512_2 = crypto:sha512_mac(Case4Key, Case4Data), + ?line m(Case4Exp512, Case4Mac512_1), + ?line m(Case4Exp512, Case4Mac512_2), + + %% Test Case 6 + Case6Key = binary:copy(<<16#aa>>, 131), + Case6Data = <<"Test Using Larger Than Block-Size Key - Hash Key First">>, + Case6Exp224 = hexstr2bin("95e9a0db962095adaebe9b2d6f0dbce2" + "d499f112f2d2b7273fa6870e"), + Case6Exp256 = hexstr2bin("60e431591ee0b67f0d8a26aacbf5b77f" + "8e0bc6213728c5140546040f0ee37f54"), + Case6Exp384 = hexstr2bin("4ece084485813e9088d2c63a041bc5b4" + "4f9ef1012a2b588f3cd11f05033ac4c6" + "0c2ef6ab4030fe8296248df163f44952"), + Case6Exp512 = hexstr2bin("80b24263c7c1a3ebb71493c1dd7be8b4" + "9b46d1f41b4aeec1121b013783f8f352" + "6b56d037e05f2598bd0fd2215d6a1e52" + "95e64f73f63f0aec8b915a985d786598"), + + ?line Case6Ctx224 = crypto:hmac_init(sha224, Case6Key), + ?line Case6Ctx224_2 = crypto:hmac_update(Case6Ctx224, Case6Data), + ?line Case6Mac224_1 = crypto:hmac_final(Case6Ctx224_2), + ?line Case6Mac224_2 = crypto:sha224_mac(Case6Key, Case6Data), + ?line m(Case6Exp224, Case6Mac224_1), + ?line m(Case6Exp224, Case6Mac224_2), + + ?line Case6Ctx256 = crypto:hmac_init(sha256, Case6Key), + ?line Case6Ctx256_2 = crypto:hmac_update(Case6Ctx256, Case6Data), + ?line Case6Mac256_1 = crypto:hmac_final(Case6Ctx256_2), + ?line Case6Mac256_2 = crypto:sha256_mac(Case6Key, Case6Data), + ?line m(Case6Exp256, Case6Mac256_1), + ?line m(Case6Exp256, Case6Mac256_2), + + ?line Case6Ctx384 = crypto:hmac_init(sha384, Case6Key), + ?line Case6Ctx384_2 = crypto:hmac_update(Case6Ctx384, Case6Data), + ?line Case6Mac384_1 = crypto:hmac_final(Case6Ctx384_2), + ?line Case6Mac384_2 = crypto:sha384_mac(Case6Key, Case6Data), + ?line m(Case6Exp384, Case6Mac384_1), + ?line m(Case6Exp384, Case6Mac384_2), + + ?line Case6Ctx512 = crypto:hmac_init(sha512, Case6Key), + ?line Case6Ctx512_2 = crypto:hmac_update(Case6Ctx512, Case6Data), + ?line Case6Mac512_1 = crypto:hmac_final(Case6Ctx512_2), + ?line Case6Mac512_2 = crypto:sha512_mac(Case6Key, Case6Data), + ?line m(Case6Exp512, Case6Mac512_1), + ?line m(Case6Exp512, Case6Mac512_2), + %% Test Case 7 + Case7Key = binary:copy(<<16#aa>>, 131), + Case7Data = <<"This is a test using a larger than block-size key and a larger t", + "han block-size data. The key needs to be hashed before being use", + "d by the HMAC algorithm.">>, + Case7Exp224 = hexstr2bin("3a854166ac5d9f023f54d517d0b39dbd" + "946770db9c2b95c9f6f565d1"), + Case7Exp256 = hexstr2bin("9b09ffa71b942fcb27635fbcd5b0e944" + "bfdc63644f0713938a7f51535c3a35e2"), + Case7Exp384 = hexstr2bin("6617178e941f020d351e2f254e8fd32c" + "602420feb0b8fb9adccebb82461e99c5" + "a678cc31e799176d3860e6110c46523e"), + Case7Exp512 = hexstr2bin("e37b6a775dc87dbaa4dfa9f96e5e3ffd" + "debd71f8867289865df5a32d20cdc944" + "b6022cac3c4982b10d5eeb55c3e4de15" + "134676fb6de0446065c97440fa8c6a58"), + + ?line Case7Ctx224 = crypto:hmac_init(sha224, Case7Key), + ?line Case7Ctx224_2 = crypto:hmac_update(Case7Ctx224, Case7Data), + ?line Case7Mac224_1 = crypto:hmac_final(Case7Ctx224_2), + ?line Case7Mac224_2 = crypto:sha224_mac(Case7Key, Case7Data), + ?line m(Case7Exp224, Case7Mac224_1), + ?line m(Case7Exp224, Case7Mac224_2), + + ?line Case7Ctx256 = crypto:hmac_init(sha256, Case7Key), + ?line Case7Ctx256_2 = crypto:hmac_update(Case7Ctx256, Case7Data), + ?line Case7Mac256_1 = crypto:hmac_final(Case7Ctx256_2), + ?line Case7Mac256_2 = crypto:sha256_mac(Case7Key, Case7Data), + ?line m(Case7Exp256, Case7Mac256_1), + ?line m(Case7Exp256, Case7Mac256_2), + + ?line Case7Ctx384 = crypto:hmac_init(sha384, Case7Key), + ?line Case7Ctx384_2 = crypto:hmac_update(Case7Ctx384, Case7Data), + ?line Case7Mac384_1 = crypto:hmac_final(Case7Ctx384_2), + ?line Case7Mac384_2 = crypto:sha384_mac(Case7Key, Case7Data), + ?line m(Case7Exp384, Case7Mac384_1), + ?line m(Case7Exp384, Case7Mac384_2), + + ?line Case7Ctx512 = crypto:hmac_init(sha512, Case7Key), + ?line Case7Ctx512_2 = crypto:hmac_update(Case7Ctx512, Case7Data), + ?line Case7Mac512_1 = crypto:hmac_final(Case7Ctx512_2), + ?line Case7Mac512_2 = crypto:sha512_mac(Case7Key, Case7Data), + ?line m(Case7Exp512, Case7Mac512_1), + ?line m(Case7Exp512, Case7Mac512_2). hmac_update_md5_io(doc) -> ["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. " @@ -717,10 +1027,19 @@ aes_cfb(Config) when is_list(Config) -> ?line Key = hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"), ?line IVec = hexstr2bin("000102030405060708090a0b0c0d0e0f"), ?line Plain = hexstr2bin("6bc1bee22e409f96e93d7e117393172a"), - ?line Cipher = crypto:aes_cfb_128_encrypt(Key, IVec, Plain), - ?line m(Cipher, hexstr2bin("3b3fd92eb72dad20333449f8e83cfb4a")), - ?line m(Plain, - crypto:aes_cfb_128_decrypt(Key, IVec, Cipher)). + ?line Cipher = hexstr2bin("3b3fd92eb72dad20333449f8e83cfb4a"), + + %% Try all prefixes of plain and cipher. + aes_cfb_do(byte_size(Plain), Plain, Cipher, Key, IVec). + +aes_cfb_do(N, Plain, Cipher, Key, IVec) when N >= 0 -> + <<P:N/binary, _/binary>> = Plain, + <<C:N/binary, _/binary>> = Cipher, + ?line C = crypto:aes_cfb_128_encrypt(Key, IVec, P), + ?line P = crypto:aes_cfb_128_decrypt(Key, IVec, C), + aes_cfb_do(N-1, Plain, Cipher, Key, IVec); +aes_cfb_do(_, _, _, _, _) -> ok. + %% %% @@ -1207,6 +1526,33 @@ rsa_sign_test(Config) when is_list(Config) -> ok. +rsa_sign_hash_test(doc) -> + "rsa_sign_hash testing"; +rsa_sign_hash_test(suite) -> + []; +rsa_sign_hash_test(Config) when is_list(Config) -> + PubEx = 65537, + PrivEx = 7531712708607620783801185371644749935066152052780368689827275932079815492940396744378735701395659435842364793962992309884847527234216715366607660219930945, + Mod = 7919488123861148172698919999061127847747888703039837999377650217570191053151807772962118671509138346758471459464133273114654252861270845708312601272799123, + Msg = <<"7896345786348756234 Hejsan Svejsan, erlang crypto debugger" + "09812312908312378623487263487623412039812 huagasd">>, + + PrivKey = [crypto:mpint(PubEx), crypto:mpint(Mod), crypto:mpint(PrivEx)], + PubKey = [crypto:mpint(PubEx), crypto:mpint(Mod)], + MD5 = crypto:md5(sized_binary(Msg)), + SHA = crypto:sha(sized_binary(Msg)), + ?line Sig1 = crypto:rsa_sign(sha, {digest,SHA}, PrivKey), + ?line m(crypto:rsa_verify(sha, {digest,SHA}, sized_binary(Sig1),PubKey), true), + + ?line Sig2 = crypto:rsa_sign(md5, {digest,MD5}, PrivKey), + ?line m(crypto:rsa_verify(md5, {digest,MD5}, sized_binary(Sig2),PubKey), true), + + ?line m(Sig1 =:= Sig2, false), + ?line m(crypto:rsa_verify(md5, {digest,MD5}, sized_binary(Sig1),PubKey), false), + ?line m(crypto:rsa_verify(sha, {digest,SHA}, sized_binary(Sig2),PubKey), false), + + ok. + dsa_sign_test(doc) -> "dsa_sign testing"; dsa_sign_test(suite) -> @@ -1237,6 +1583,37 @@ dsa_sign_test(Config) when is_list(Config) -> ok. +dsa_sign_hash_test(doc) -> + "dsa_sign_hash testing"; +dsa_sign_hash_test(suite) -> + []; +dsa_sign_hash_test(Config) when is_list(Config) -> + Msg = <<"7896345786348756234 Hejsan Svejsan, erlang crypto debugger" + "09812312908312378623487263487623412039812 huagasd">>, + SHA = crypto:sha(sized_binary(Msg)), + + PubKey = _Y = 25854665488880835237281628794585130313500176551981812527054397586638455298000483144002221850980183404910190346416063318160497344811383498859129095184158800144312512447497510551471331451396405348497845813002058423110442376886564659959543650802132345311573634832461635601376738282831340827591903548964194832978, + PrivKey = _X = 441502407453038284293378221372000880210588566361, + ParamP = 109799869232806890760655301608454668257695818999841877165019612946154359052535682480084145133201304812979481136659521529774182959764860329095546511521488413513097576425638476458000255392402120367876345280670101492199681798674053929238558140260669578407351853803102625390950534052428162468100618240968893110797, + ParamQ = 1349199015905534965792122312016505075413456283393, + ParamG = 18320614775012672475365915366944922415598782131828709277168615511695849821411624805195787607930033958243224786899641459701930253094446221381818858674389863050420226114787005820357372837321561754462061849169568607689530279303056075793886577588606958623645901271866346406773590024901668622321064384483571751669, + + Params = [crypto:mpint(ParamP), crypto:mpint(ParamQ), crypto:mpint(ParamG)], + ?line Sig1 = crypto:dss_sign(sha, {digest,SHA}, Params ++ [crypto:mpint(PrivKey)]), + + ?line m(crypto:dss_verify(none, SHA, sized_binary(Sig1), + Params ++ [crypto:mpint(PubKey)]), true), + + ?line m(crypto:dss_verify(sized_binary(one_bit_wrong(Msg)), sized_binary(Sig1), + Params ++ [crypto:mpint(PubKey)]), false), + + ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(one_bit_wrong(Sig1)), + Params ++ [crypto:mpint(PubKey)]), false), + + %%?line Bad = crypto:dss_sign(sized_binary(Msg), [Params, crypto:mpint(PubKey)]), + + ok. + rsa_encrypt_decrypt(doc) -> ["Test rsa_public_encrypt and rsa_private_decrypt functions."]; @@ -1421,7 +1798,9 @@ worker_loop(N, Config) -> Funcs = { md5, md5_update, md5_mac, md5_mac_io, sha, sha_update, des_cbc, aes_cfb, aes_cbc, des_cbc_iter, rand_uniform_test, strong_rand_test, rsa_verify_test, exor_test, rc4_test, rc4_stream_test, mod_exp_test, - hmac_update_md5, hmac_update_sha, aes_ctr_stream }, + hmac_update_md5, hmac_update_sha, hmac_update_sha256, hmac_update_sha512, + hmac_rfc4231, + aes_ctr_stream }, F = element(random:uniform(size(Funcs)),Funcs), %%io:format("worker ~p calling ~p\n",[self(),F]), diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile index 1c0bbaf9d2..bc11ed7f46 100644 --- a/lib/debugger/doc/src/Makefile +++ b/lib/debugger/doc/src/Makefile @@ -110,12 +110,12 @@ debug opt: 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 - (/bin/cp -rf $(HTMLDIR) $(RELSYSDIR)/doc) - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc") + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/debugger/priv/Makefile b/lib/debugger/priv/Makefile index 1036a5666b..2f3002331b 100644 --- a/lib/debugger/priv/Makefile +++ b/lib/debugger/priv/Makefile @@ -54,8 +54,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(TOOLBAR_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(TOOLBAR_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/debugger/src/Makefile b/lib/debugger/src/Makefile index be9a2d13cb..1f9d6f2058 100644 --- a/lib/debugger/src/Makefile +++ b/lib/debugger/src/Makefile @@ -129,9 +129,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(TOOLBOX_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(TARGET_TOOLBOX_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(TOOLBOX_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(TARGET_TOOLBOX_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/debugger/test/Makefile b/lib/debugger/test/Makefile index 3dfbed31ff..bf948ce8b0 100644 --- a/lib/debugger/test/Makefile +++ b/lib/debugger/test/Makefile @@ -98,10 +98,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) debugger.spec debugger.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) debugger.spec debugger.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/debugger/test/bs_construct_SUITE.erl b/lib/debugger/test/bs_construct_SUITE.erl index cf943677be..e0bda7eac8 100644 --- a/lib/debugger/test/bs_construct_SUITE.erl +++ b/lib/debugger/test/bs_construct_SUITE.erl @@ -477,7 +477,6 @@ mem_leak(0, _) -> ok; mem_leak(N, B) -> ?line big_bin(B, <<23>>), ?line {'EXIT',{badarg,_}} = (catch big_bin(B, bad)), - maybe_gc(), mem_leak(N-1, B). big_bin(B1, B2) -> @@ -490,13 +489,6 @@ big_bin(B1, B2) -> make_bin(0, Acc) -> Acc; make_bin(N, Acc) -> make_bin(N-1, <<Acc/binary,Acc/binary>>). -maybe_gc() -> - case erlang:system_info(heap_type) of - shared -> erlang:garbage_collect(); - hybrid -> erlang:garbage_collect(); - private -> ok - end. - -define(COF(Int0), ?line (fun(Int) -> true = <<Int:32/float>> =:= <<(float(Int)):32/float>>, diff --git a/lib/dialyzer/doc/src/Makefile b/lib/dialyzer/doc/src/Makefile index 45b0ffa5ff..a827281cc5 100644 --- a/lib/dialyzer/doc/src/Makefile +++ b/lib/dialyzer/doc/src/Makefile @@ -100,13 +100,13 @@ 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/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile index 04f3b844c4..63cc1c98f1 100644 --- a/lib/dialyzer/src/Makefile +++ b/lib/dialyzer/src/Makefile @@ -63,7 +63,10 @@ MODULES = \ dialyzer_plt \ dialyzer_races \ dialyzer_succ_typings \ + dialyzer_timing \ dialyzer_typesig \ + dialyzer_coordinator \ + dialyzer_worker \ dialyzer_utils HRL_FILES= dialyzer.hrl dialyzer_gui_wx.hrl @@ -152,10 +155,10 @@ $(EBIN)/dialyzer_utils.beam: dialyzer.hrl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/src" $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(EXTRA_FILES) \ - $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(INSTALL_FILES) $(RELSYSDIR)/ebin + "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(INSTALL_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index 5e089d1773..105a174e31 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -2,7 +2,7 @@ %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%%% Copyright Ericsson AB 2006-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 @@ -110,6 +110,8 @@ -type label() :: non_neg_integer(). -type rep_mode() :: 'quiet' | 'normal' | 'verbose'. -type start_from() :: 'byte_code' | 'src_code'. +-type mfa_or_funlbl() :: label() | mfa(). +-type solver() :: 'v1' | 'v2'. %%-------------------------------------------------------------------- %% Record declarations used by various files @@ -126,11 +128,15 @@ use_contracts = true :: boolean(), race_detection = false :: boolean(), behaviours_chk = false :: boolean(), - callgraph_file = "" :: file:filename()}). + timing = false :: boolean() | 'debug', + timing_server :: dialyzer_timing:timing_server(), + callgraph_file = "" :: file:filename(), + solvers :: [solver()]}). -record(options, {files = [] :: [file:filename()], files_rec = [] :: [file:filename()], analysis_type = succ_typings :: anal_type1(), + timing = false :: boolean() | 'debug', defines = [] :: [dial_define()], from = byte_code :: start_from(), get_warnings = maybe :: boolean() | 'maybe', @@ -145,10 +151,20 @@ output_format = formatted :: format(), filename_opt = basename :: fopt(), callgraph_file = "" :: file:filename(), - check_plt = true :: boolean()}). + check_plt = true :: boolean(), + solvers = [] :: [solver()]}). -record(contract, {contracts = [] :: [contract_pair()], args = [] :: [erl_types:erl_type()], forms = [] :: [{_, _}]}). %%-------------------------------------------------------------------- + +-define(timing(Server, Msg, Var, Expr), + begin + dialyzer_timing:start_stamp(Server, Msg), + Var = Expr, + dialyzer_timing:end_stamp(Server), + Var + end). +-define(timing(Server, Msg, Expr),?timing(Server, Msg, _T, Expr)). diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 2c4622155a..c237d4e0e9 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -30,6 +30,15 @@ -export([start/3]). +-export([compile_init_result/0, + add_to_result/4, + start_compilation/2, + continue_compilation/2]). + +-export_type([compile_init_data/0, + one_file_result/0, + compile_result/0]). + -include("dialyzer.hrl"). -record(analysis_state, @@ -43,7 +52,9 @@ parent :: pid(), plt :: dialyzer_plt:plt(), start_from = byte_code :: start_from(), - use_contracts = true :: boolean() + use_contracts = true :: boolean(), + timing_server :: dialyzer_timing:timing_server(), + solvers :: [solver()] }). -record(server_state, {parent :: pid(), legal_warnings :: [dial_warn_tag()]}). @@ -55,12 +66,15 @@ -spec start(pid(), [dial_warn_tag()], #analysis{}) -> 'ok'. start(Parent, LegalWarnings, Analysis) -> + TimingServer = dialyzer_timing:init(Analysis#analysis.timing), RacesOn = ordsets:is_element(?WARN_RACE_CONDITION, LegalWarnings), - Analysis0 = Analysis#analysis{race_detection = RacesOn}, + Analysis0 = + Analysis#analysis{race_detection = RacesOn, timing_server = TimingServer}, Analysis1 = expand_files(Analysis0), Analysis2 = run_analysis(Analysis1), State = #server_state{parent = Parent, legal_warnings = LegalWarnings}, - loop(State, Analysis2, none). + loop(State, Analysis2, none), + dialyzer_timing:stop(TimingServer). run_analysis(Analysis) -> Self = self(), @@ -122,7 +136,9 @@ analysis_start(Parent, Analysis) -> plt = Plt, parent = Parent, start_from = Analysis#analysis.start_from, - use_contracts = Analysis#analysis.use_contracts + use_contracts = Analysis#analysis.use_contracts, + timing_server = Analysis#analysis.timing_server, + solvers = Analysis#analysis.solvers }, Files = ordsets:from_list(Analysis#analysis.files), {Callgraph, NoWarn, TmpCServer0} = compile_and_store(Files, State), @@ -146,7 +162,8 @@ analysis_start(Parent, Analysis) -> dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, TmpCServer1), TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2), - dialyzer_contracts:process_contract_remote_types(TmpCServer3) + ?timing(State#analysis_state.timing_server, "remote", + dialyzer_contracts:process_contract_remote_types(TmpCServer3)) catch throw:{error, _ErrorMsg} = Error -> exit(Error) end, @@ -167,6 +184,7 @@ analysis_start(Parent, Analysis) -> false -> Callgraph end, State3 = analyze_callgraph(NewCallgraph, State2#analysis_state{plt = Plt1}), + dialyzer_callgraph:dispose_race_server(NewCallgraph), rcv_and_send_ext_types(Parent), NonExports = sets:subtract(sets:from_list(AllNodes), Exports), NonExportsList = sets:to_list(NonExports), @@ -174,72 +192,67 @@ analysis_start(Parent, Analysis) -> send_codeserver_plt(Parent, CServer, State3#analysis_state.plt), send_analysis_done(Parent, Plt2, State3#analysis_state.doc_plt). -analyze_callgraph(Callgraph, State) -> - Codeserver = State#analysis_state.codeserver, - Parent = State#analysis_state.parent, - DocPlt = State#analysis_state.doc_plt, +analyze_callgraph(Callgraph, #analysis_state{codeserver = Codeserver, + doc_plt = DocPlt, + timing_server = TimingServer, + parent = Parent, + solvers = Solvers} = State) -> Plt = dialyzer_plt:insert_callbacks(State#analysis_state.plt, Codeserver), - Callgraph1 = dialyzer_callgraph:finalize(Callgraph), {NewPlt, NewDocPlt} = case State#analysis_state.analysis_type of plt_build -> - {dialyzer_succ_typings:analyze_callgraph(Callgraph1, Plt, - Codeserver, Parent), - DocPlt}; + NewPlt0 = + dialyzer_succ_typings:analyze_callgraph(Callgraph, Plt, Codeserver, + TimingServer, Solvers, Parent), + {NewPlt0, DocPlt}; succ_typings -> NoWarn = State#analysis_state.no_warn_unused, {Warnings, NewPlt0, NewDocPlt0} = - dialyzer_succ_typings:get_warnings(Callgraph1, Plt, DocPlt, - Codeserver, NoWarn, Parent), + dialyzer_succ_typings:get_warnings(Callgraph, Plt, DocPlt, Codeserver, + NoWarn, TimingServer, Solvers, Parent), send_warnings(State#analysis_state.parent, Warnings), {NewPlt0, NewDocPlt0} end, - dialyzer_callgraph:delete(Callgraph1), + dialyzer_callgraph:delete(Callgraph), State#analysis_state{plt = NewPlt, doc_plt = NewDocPlt}. %%-------------------------------------------------------------------- %% Build the callgraph and fill the codeserver. %%-------------------------------------------------------------------- +-record(compile_init,{ + callgraph :: dialyzer_callgraph:callgraph(), + codeserver :: dialyzer_codeserver:codeserver(), + defines = [] :: [dial_define()], + include_dirs = [] :: [file:filename()], + start_from = byte_code :: start_from(), + use_contracts = true :: boolean() + }). + +make_compile_init(#analysis_state{codeserver = Codeserver, + defines = Defs, + include_dirs = Dirs, + use_contracts = UseContracts, + start_from = StartFrom}, Callgraph) -> + #compile_init{callgraph = Callgraph, + codeserver = Codeserver, + defines = [{d, Macro, Val} || {Macro, Val} <- Defs], + include_dirs = [{i, D} || D <- Dirs], + use_contracts = UseContracts, + start_from = StartFrom}. + compile_and_store(Files, #analysis_state{codeserver = CServer, - defines = Defs, - include_dirs = Dirs, - parent = Parent, - use_contracts = UseContracts, - start_from = StartFrom - } = State) -> + timing_server = Timing, + parent = Parent} = State) -> send_log(Parent, "Reading files and computing callgraph... "), {T1, _} = statistics(runtime), - Includes = [{i, D} || D <- Dirs], - Defines = [{d, Macro, Val} || {Macro, Val} <- Defs], Callgraph = dialyzer_callgraph:new(), - Fun = case StartFrom of - src_code -> - fun(File, {TmpCG, TmpCServer, TmpFailed, TmpNoWarn, TmpMods}) -> - case compile_src(File, Includes, Defines, TmpCG, - TmpCServer, UseContracts) of - {error, Reason} -> - {TmpCG, TmpCServer, [{File, Reason}|TmpFailed], TmpNoWarn, - TmpMods}; - {ok, NewCG, NoWarn, NewCServer, Mod} -> - {NewCG, NewCServer, TmpFailed, NoWarn++TmpNoWarn, - [Mod|TmpMods]} - end - end; - byte_code -> - fun(File, {TmpCG, TmpCServer, TmpFailed, TmpNoWarn, TmpMods}) -> - case compile_byte(File, TmpCG, TmpCServer, UseContracts) of - {error, Reason} -> - {TmpCG, TmpCServer, [{File, Reason}|TmpFailed], TmpNoWarn, - TmpMods}; - {ok, NewCG, NoWarn, NewCServer, Mod} -> - {NewCG, NewCServer, TmpFailed, NoWarn++TmpNoWarn, - [Mod|TmpMods]} - end - end - end, - {NewCallgraph1, NewCServer, Failed, NoWarn, Modules} = - lists:foldl(Fun, {Callgraph, CServer, [], [], []}, Files), + CompileInit = make_compile_init(State, Callgraph), + {{Failed, NoWarn, Modules}, NextLabel} = + ?timing(Timing, "compile", _C1, + dialyzer_coordinator:parallel_job(compile, Files, + CompileInit, Timing)), + CServer2 = dialyzer_codeserver:set_next_core_label(NextLabel, CServer), case Failed =:= [] of true -> NewFiles = lists:zip(lists:reverse(Modules), Files), @@ -255,11 +268,56 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, {T2, _} = statistics(runtime), Msg1 = io_lib:format("done in ~.2f secs\nRemoving edges... ", [(T2-T1)/1000]), send_log(Parent, Msg1), - NewCallgraph2 = cleanup_callgraph(State, NewCServer, NewCallgraph1, Modules), + Callgraph = + ?timing(Timing, "clean", _C2, + cleanup_callgraph(State, CServer2, Callgraph, Modules)), {T3, _} = statistics(runtime), Msg2 = io_lib:format("done in ~.2f secs\n", [(T3-T2)/1000]), send_log(Parent, Msg2), - {NewCallgraph2, sets:from_list(NoWarn), NewCServer}. + {Callgraph, sets:from_list(NoWarn), CServer2}. + +-type compile_init_data() :: #compile_init{}. +-type error_reason() :: string(). +-type compile_result() :: {[{file:filename(), error_reason()}], [mfa()], + [module()]}. %%opaque +-type one_file_result() :: {error, error_reason()} | + {ok, [dialyzer_callgraph:callgraph_edge()], + [mfa_or_funlbl()], [mfa()], module()}. %%opaque +-type compile_mid_data() :: {module(), cerl:cerl(), [mfa()], + dialyzer_callgraph:callgraph(), + dialyzer_codeserver:codeserver()}. + +-spec compile_init_result() -> compile_result(). + +compile_init_result() -> {[], [], []}. + +-spec add_to_result(file:filename(), one_file_result(), compile_result(), + compile_init_data()) -> compile_result(). + +add_to_result(File, NewData, {Failed, NoWarn, Mods}, InitData) -> + case NewData of + {error, Reason} -> + {[{File, Reason}|Failed], NoWarn, Mods}; + {ok, V, E, NewNoWarn, Mod} -> + Callgraph = InitData#compile_init.callgraph, + dialyzer_callgraph:add_edges(E, V, Callgraph), + {Failed, NewNoWarn ++ NoWarn, [Mod|Mods]} + end. + +-spec start_compilation(file:filename(), compile_init_data()) -> + {error, error_reason()} |{ok, integer(), compile_mid_data()}. + +start_compilation(File, + #compile_init{callgraph = Callgraph, codeserver = Codeserver, + defines = Defines, include_dirs = IncludeD, + use_contracts = UseContracts, + start_from = StartFrom}) -> + case StartFrom of + src_code -> + compile_src(File, IncludeD, Defines, Callgraph, Codeserver, UseContracts); + byte_code -> + compile_byte(File, Callgraph, Codeserver, UseContracts) + end. cleanup_callgraph(#analysis_state{plt = InitPlt, parent = Parent, codeserver = CodeServer @@ -348,14 +406,18 @@ compile_common(File, AbstrCode, CompOpts, Callgraph, CServer, UseContracts) -> store_core(Mod, Core, NoWarn, Callgraph, CServer) -> Exp = get_exports_from_core(Core), - OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CServer), - NewExpTypes = get_exported_types_from_core(Core), - MergedExpTypes = sets:union(NewExpTypes, OldExpTypes), - CServer1 = dialyzer_codeserver:insert_exports(Exp, CServer), - CServer2 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, - CServer1), - {LabeledCore, CServer3} = label_core(Core, CServer2), - store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, CServer3, NoWarn). + ExpTypes = get_exported_types_from_core(Core), + CServer = dialyzer_codeserver:insert_exports(Exp, CServer), + CServer = dialyzer_codeserver:insert_temp_exported_types(ExpTypes, CServer), + CoreTree = cerl:from_records(Core), + {ok, cerl_trees:size(CoreTree), {Mod, CoreTree, NoWarn, Callgraph, CServer}}. + +-spec continue_compilation(integer(), compile_mid_data()) -> one_file_result(). + +continue_compilation(NextLabel, {Mod, CoreTree, NoWarn, Callgraph, CServer}) -> + {LabeledTree, _NewNextLabel} = cerl_trees:label(CoreTree, NextLabel), + LabeledCore = cerl:to_records(LabeledTree), + store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, NoWarn, CServer). abs_get_nowarn(Abs, M) -> Opts = lists:flatten([C || {attribute, _, compile, C} <- Abs]), @@ -388,18 +450,11 @@ get_exports_from_core(Core) -> M = cerl:atom_val(cerl:module_name(Tree)), [{M, F, A} || {F, A} <- Exports2]. -label_core(Core, CServer) -> - NextLabel = dialyzer_codeserver:get_next_core_label(CServer), +store_code_and_build_callgraph(Mod, Core, Callgraph, NoWarn, CServer) -> CoreTree = cerl:from_records(Core), - {LabeledTree, NewNextLabel} = cerl_trees:label(CoreTree, NextLabel), - {cerl:to_records(LabeledTree), - dialyzer_codeserver:set_next_core_label(NewNextLabel, CServer)}. - -store_code_and_build_callgraph(Mod, Core, Callgraph, CServer, NoWarn) -> - CoreTree = cerl:from_records(Core), - NewCallgraph = dialyzer_callgraph:scan_core_tree(CoreTree, Callgraph), - CServer2 = dialyzer_codeserver:insert(Mod, CoreTree, CServer), - {ok, NewCallgraph, NoWarn, CServer2, Mod}. + {Vertices, Edges} = dialyzer_callgraph:scan_core_tree(CoreTree, Callgraph), + CServer = dialyzer_codeserver:insert(Mod, CoreTree, CServer), + {ok, Vertices, Edges, NoWarn, Mod}. %%-------------------------------------------------------------------- %% Utilities @@ -465,9 +520,10 @@ rcv_and_send_ext_types(Parent) -> Self ! {Self, done}, case rcv_ext_types(Self, []) of [] -> ok; - ExtTypes -> Parent ! {Self, ext_types, ExtTypes} - end, - ok. + ExtTypes -> + Parent ! {Self, ext_types, ExtTypes}, + ok + end. rcv_ext_types(Self, ExtTypes) -> receive diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl index 127e906135..b84071b95c 100644 --- a/lib/dialyzer/src/dialyzer_behaviours.erl +++ b/lib/dialyzer/src/dialyzer_behaviours.erl @@ -30,7 +30,7 @@ -module(dialyzer_behaviours). --export([check_callbacks/4, get_behaviour_apis/1, +-export([check_callbacks/5, get_behaviour_apis/1, translate_behaviour_api_call/5, translatable_behaviours/1, translate_callgraph/3]). @@ -47,15 +47,16 @@ -record(state, {plt :: dialyzer_plt:plt(), codeserver :: dialyzer_codeserver:codeserver(), filename :: file:filename(), - behlines :: [{behaviour(), non_neg_integer()}]}). + behlines :: [{behaviour(), non_neg_integer()}], + records :: dict()}). %%-------------------------------------------------------------------- --spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}], +-spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}], dict(), dialyzer_plt:plt(), dialyzer_codeserver:codeserver()) -> [dial_warning()]. -check_callbacks(Module, Attrs, Plt, Codeserver) -> +check_callbacks(Module, Attrs, Records, Plt, Codeserver) -> {Behaviours, BehLines} = get_behaviours(Attrs), case Behaviours of [] -> []; @@ -64,7 +65,7 @@ check_callbacks(Module, Attrs, Plt, Codeserver) -> {_Var,Code} = dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver), File = get_file(cerl:get_ann(Code)), State = #state{plt = Plt, filename = File, behlines = BehLines, - codeserver = Codeserver}, + codeserver = Codeserver, records = Records}, Warnings = get_warnings(Module, Behaviours, State), [add_tag_file_line(Module, W, State) || W <- Warnings] end. @@ -92,24 +93,21 @@ get_warnings(Module, [Behaviour|Rest], State, Acc) -> check_behaviour(Module, Behaviour, #state{plt = Plt} = State, Acc) -> case dialyzer_plt:lookup_callbacks(Plt, Behaviour) of - [] -> [{callback_info_missing, [Behaviour]}|Acc]; - Callbacks -> check_all_callbacks(Module, Behaviour, Callbacks, State, Acc) + none -> [{callback_info_missing, [Behaviour]}|Acc]; + {value, Callbacks} -> + check_all_callbacks(Module, Behaviour, Callbacks, State, Acc) end. check_all_callbacks(_Module, _Behaviour, [], _State, Acc) -> Acc; check_all_callbacks(Module, Behaviour, [Cb|Rest], - #state{plt = Plt, codeserver = Codeserver} = State, Acc) -> + #state{plt = Plt, codeserver = Codeserver, + records = Records} = State, Acc) -> {{Behaviour, Function, Arity}, {{_BehFile, _BehLine}, Callback}} = Cb, CbMFA = {Module, Function, Arity}, CbReturnType = dialyzer_contracts:get_contract_return(Callback), CbArgTypes = dialyzer_contracts:get_contract_args(Callback), - Records = - case dict:find(Module, dialyzer_codeserver:get_records(Codeserver)) of - {ok, V} -> V; - error -> dict:new() - end, Acc0 = Acc, Acc1 = case dialyzer_plt:lookup(Plt, CbMFA) of @@ -282,8 +280,8 @@ translate_callgraph([{Behaviour,_}|Behaviours], Module, Callgraph) -> DirectCalls = [{From, {Module, Fun, Arity}} || {From, To} <- UsedCalls,{API, {Fun, Arity, _Ord}} <- Calls, To =:= API], - NewCallgraph = dialyzer_callgraph:add_edges(DirectCalls, Callgraph), - translate_callgraph(Behaviours, Module, NewCallgraph); + dialyzer_callgraph:add_edges(DirectCalls, Callgraph), + translate_callgraph(Behaviours, Module, Callgraph); translate_callgraph([], _Module, Callgraph) -> Callgraph. diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index effd619bde..64e0ee88af 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -28,6 +28,7 @@ -module(dialyzer_callgraph). -export([add_edges/2, + add_edges/3, all_nodes/1, delete/1, finalize/1, @@ -43,12 +44,15 @@ %% module_postorder/1, module_postorder_from_funs/2, new/0, + get_depends_on/2, + get_required_by/2, in_neighbours/2, renew_race_info/4, + renew_race_code/2, + renew_race_public_tables/2, reset_from_funs/2, scan_core_tree/2, strip_module_deps/2, - take_scc/1, remove_external/1, to_dot/2, to_ps/3]). @@ -57,15 +61,14 @@ get_race_code/1, get_race_detection/1, race_code_new/1, put_digraph/2, put_race_code/2, put_race_detection/2, put_named_tables/2, put_public_tables/2, put_behaviour_api_calls/2, - get_behaviour_api_calls/1]). + get_behaviour_api_calls/1, dispose_race_server/1, duplicate/1]). --export_type([callgraph/0, mfa_or_funlbl/0]). +-export_type([callgraph/0, mfa_or_funlbl/0, callgraph_edge/0]). -include("dialyzer.hrl"). %%---------------------------------------------------------------------- --type mfa_or_funlbl() :: label() | mfa(). -type scc() :: [mfa_or_funlbl()]. -type mfa_calls() :: [{mfa_or_funlbl(), mfa_or_funlbl()}]. @@ -78,9 +81,6 @@ %% digraph - A digraph representing the callgraph. %% Nodes are represented as MFAs or labels. %% esc - A set of all escaping functions as reported by dialyzer_dep. -%% postorder - A list of strongly connected components of the callgraph -%% sorted in a topological bottom-up order. -%% This is produced by calling finalize/1. %% name_map - A mapping from label to MFA. %% rev_name_map - A reverse mapping of the name_map. %% rec_var_map - A dict mapping from letrec bound labels to function names. @@ -91,29 +91,42 @@ %%----------------------------------------------------------------------------- -record(callgraph, {digraph = digraph:new() :: digraph(), - esc = sets:new() :: set(), - name_map = dict:new() :: dict(), - rev_name_map = dict:new() :: dict(), - postorder = [] :: [scc()], - rec_var_map = dict:new() :: dict(), - self_rec = sets:new() :: set(), - calls = dict:new() :: dict(), - race_code = dict:new() :: dict(), - public_tables = [] :: [label()], - named_tables = [] :: [string()], + active_digraph :: active_digraph(), + esc :: ets:tid(), + name_map :: ets:tid(), + rev_name_map :: ets:tid(), + rec_var_map :: ets:tid(), + self_rec :: ets:tid(), + calls :: ets:tid(), race_detection = false :: boolean(), - beh_api_calls = [] :: [{mfa(), mfa()}]}). + race_data_server = new_race_data_server() :: pid()}). + +-record(race_data_state, {race_code = dict:new() :: dict(), + public_tables = [] :: [label()], + named_tables = [] :: [string()], + beh_api_calls = [] :: [{mfa(), mfa()}]}). %% Exported Types -type callgraph() :: #callgraph{}. +-type active_digraph() :: {'d', digraph()} | {'e', ets:tid(), ets:tid()}. + %%---------------------------------------------------------------------- -spec new() -> callgraph(). new() -> - #callgraph{}. + [ETSEsc, ETSNameMap, ETSRevNameMap, ETSRecVarMap, ETSSelfRec, ETSCalls] = + [ets:new(N,[public, {read_concurrency, true}]) || + N <- [callgraph_esc, callgraph_name_map, callgraph_rev_name_map, + callgraph_rec_var_map, callgraph_self_rec, callgraph_calls]], + #callgraph{esc = ETSEsc, + name_map = ETSNameMap, + rev_name_map = ETSRevNameMap, + rec_var_map = ETSRecVarMap, + self_rec = ETSSelfRec, + calls = ETSCalls}. -spec delete(callgraph()) -> 'true'. @@ -129,32 +142,32 @@ all_nodes(#callgraph{digraph = DG}) -> lookup_rec_var(Label, #callgraph{rec_var_map = RecVarMap}) when is_integer(Label) -> - dict:find(Label, RecVarMap). + ets_lookup_dict(Label, RecVarMap). -spec lookup_call_site(label(), callgraph()) -> 'error' | {'ok', [_]}. % XXX: refine lookup_call_site(Label, #callgraph{calls = Calls}) when is_integer(Label) -> - dict:find(Label, Calls). + ets_lookup_dict(Label, Calls). -spec lookup_name(label(), callgraph()) -> 'error' | {'ok', mfa()}. lookup_name(Label, #callgraph{name_map = NameMap}) when is_integer(Label) -> - dict:find(Label, NameMap). + ets_lookup_dict(Label, NameMap). -spec lookup_label(mfa_or_funlbl(), callgraph()) -> 'error' | {'ok', integer()}. lookup_label({_,_,_} = MFA, #callgraph{rev_name_map = RevNameMap}) -> - dict:find(MFA, RevNameMap); + ets_lookup_dict(MFA, RevNameMap); lookup_label(Label, #callgraph{}) when is_integer(Label) -> {ok, Label}. -spec in_neighbours(mfa_or_funlbl(), callgraph()) -> 'none' | [mfa_or_funlbl(),...]. -in_neighbours(Label, #callgraph{digraph = Digraph, name_map = NameMap}) +in_neighbours(Label, #callgraph{digraph = Digraph} = CG) when is_integer(Label) -> - Name = case dict:find(Label, NameMap) of + Name = case lookup_name(Label, CG) of {ok, Val} -> Val; error -> Label end, @@ -165,34 +178,27 @@ in_neighbours({_, _, _} = MFA, #callgraph{digraph = Digraph}) -> -spec is_self_rec(mfa_or_funlbl(), callgraph()) -> boolean(). is_self_rec(MfaOrLabel, #callgraph{self_rec = SelfRecs}) -> - sets:is_element(MfaOrLabel, SelfRecs). + ets_lookup_set(MfaOrLabel, SelfRecs). -spec is_escaping(label(), callgraph()) -> boolean(). is_escaping(Label, #callgraph{esc = Esc}) when is_integer(Label) -> - sets:is_element(Label, Esc). + ets_lookup_set(Label, Esc). -type callgraph_edge() :: {mfa_or_funlbl(),mfa_or_funlbl()}. --spec add_edges([callgraph_edge()], callgraph()) -> callgraph(). +-spec add_edges([callgraph_edge()], callgraph()) -> ok. -add_edges([], CG) -> - CG; -add_edges(Edges, #callgraph{digraph = Digraph} = CG) -> - CG#callgraph{digraph = digraph_add_edges(Edges, Digraph)}. +add_edges([], _CG) -> + ok; +add_edges(Edges, #callgraph{digraph = Digraph}) -> + digraph_add_edges(Edges, Digraph). --spec add_edges([callgraph_edge()], [mfa_or_funlbl()], callgraph()) -> callgraph(). +-spec add_edges([callgraph_edge()], [mfa_or_funlbl()], callgraph()) -> ok. add_edges(Edges, MFAs, #callgraph{digraph = DG} = CG) -> - DG = digraph_confirm_vertices(MFAs, DG), + digraph_confirm_vertices(MFAs, DG), add_edges(Edges, CG). --spec take_scc(callgraph()) -> 'none' | {'ok', scc(), callgraph()}. - -take_scc(#callgraph{postorder = [SCC|SCCs]} = CG) -> - {ok, SCC, CG#callgraph{postorder = SCCs}}; -take_scc(#callgraph{postorder = []}) -> - none. - -spec remove_external(callgraph()) -> {callgraph(), [tuple()]}. remove_external(#callgraph{digraph = DG} = CG) -> @@ -221,13 +227,25 @@ find_non_local_calls([{Label1, Label2}|Left], Set) when is_integer(Label1), find_non_local_calls([], Set) -> sets:to_list(Set). --spec renew_race_info(callgraph(), dict(), [label()], [string()]) -> - callgraph(). +-spec get_depends_on(scc() | module(), callgraph()) -> [scc()]. + +get_depends_on(SCC, #callgraph{active_digraph = {'e', Out, _In}}) -> + case ets_lookup_dict(SCC, Out) of + {ok, Value} -> Value; + error -> [] + end; +get_depends_on(SCC, #callgraph{active_digraph = {'d', DG}}) -> + digraph:out_neighbours(DG, SCC). + +-spec get_required_by(scc() | module(), callgraph()) -> [scc()]. -renew_race_info(CG, RaceCode, PublicTables, NamedTables) -> - CG#callgraph{race_code = RaceCode, - public_tables = PublicTables, - named_tables = NamedTables}. +get_required_by(SCC, #callgraph{active_digraph = {'e', _Out, In}}) -> + case ets_lookup_dict(SCC, In) of + {ok, Value} -> Value; + error -> [] + end; +get_required_by(SCC, #callgraph{active_digraph = {'d', DG}}) -> + digraph:in_neighbours(DG, SCC). %%---------------------------------------------------------------------- %% Handling of modules & SCCs @@ -238,32 +256,37 @@ renew_race_info(CG, RaceCode, PublicTables, NamedTables) -> modules(#callgraph{digraph = DG}) -> ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]). --spec module_postorder(callgraph()) -> [[module()]]. +-spec module_postorder(callgraph()) -> {[module()], {'d', digraph()}}. module_postorder(#callgraph{digraph = DG}) -> - {MDG, _Nodes} = get_module_digraph_and_nodes(DG), - MDG1 = digraph_utils:condensation(MDG), - PostOrder = digraph_utils:postorder(MDG1), - PostOrder1 = sort_sccs_internally(PostOrder, MDG), - digraph:delete(MDG1), - digraph_delete(MDG), - PostOrder1. + Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)), + Nodes = sets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), + MDG = digraph:new([acyclic]), + digraph_confirm_vertices(sets:to_list(Nodes), MDG), + Foreach = fun({M1,M2}) -> digraph:add_edge(MDG, M1, M2) end, + lists:foreach(Foreach, sets:to_list(Edges)), + {digraph_utils:topsort(MDG), {'d', MDG}}. + +edge_fold({{M1,_,_},{M2,_,_}}, Set) -> + case M1 =/= M2 of + true -> sets:add_element({M1,M2},Set); + false -> Set + end; +edge_fold(_, Set) -> Set. -get_module_digraph_and_nodes(DG) -> - Edges = digraph_edges(DG), - Nodes = ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), - MDG = digraph:new(), - MDG = digraph_confirm_vertices(Nodes, MDG), - MDG = create_module_digraph(Edges, MDG), - {MDG, Nodes}. %% The module deps of a module are modules that depend on the module -spec module_deps(callgraph()) -> dict(). module_deps(#callgraph{digraph = DG}) -> - {MDG, Nodes} = get_module_digraph_and_nodes(DG), + Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)), + Nodes = sets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), + MDG = digraph:new(), + digraph_confirm_vertices(sets:to_list(Nodes), MDG), + Foreach = fun({M1,M2}) -> digraph:add_edge(MDG, M1, M2) end, + lists:foreach(Foreach, sets:to_list(Edges)), Deps = [{N, ordsets:from_list(digraph:in_neighbours(MDG, N))} - || N <- Nodes], + || N <- sets:to_list(Nodes)], digraph_delete(MDG), dict:from_list(Deps). @@ -276,52 +299,42 @@ strip_module_deps(ModDeps, StripSet) -> FilterFun2 = fun(_Key, ValSet) -> ValSet =/= [] end, dict:filter(FilterFun2, ModDeps1). -sort_sccs_internally(PO, MDG) -> - sort_sccs_internally(PO, MDG, []). - -sort_sccs_internally([SCC|SCCs], MDG, Acc) -> - case SCC of - [_, _, _ | _] -> % length(SCC) >= 3 - TmpDG = digraph_utils:subgraph(MDG, SCC), - NewSCC = digraph_utils:postorder(TmpDG), - digraph_delete(TmpDG), - sort_sccs_internally(SCCs, MDG, [NewSCC|Acc]); - _ -> - sort_sccs_internally(SCCs, MDG, [SCC|Acc]) - end; -sort_sccs_internally([], _MDG, Acc) -> - lists:reverse(Acc). - -create_module_digraph([{{M, _, _}, {M, _, _}}|Left], MDG) -> - create_module_digraph(Left, MDG); -create_module_digraph([{{M1, _, _}, {M2, _, _}}|Left], MDG) -> - create_module_digraph(Left, digraph_add_edge(M1, M2, MDG)); -create_module_digraph([{_, _}|Left], MDG) -> - create_module_digraph(Left, MDG); -create_module_digraph([], MDG) -> - MDG. - --spec finalize(callgraph()) -> callgraph(). +-spec finalize(callgraph()) -> {[scc()], callgraph()}. finalize(#callgraph{digraph = DG} = CG) -> - CG#callgraph{postorder = digraph_finalize(DG)}. + {ActiveDG, Postorder} = condensation(DG), + {Postorder, CG#callgraph{active_digraph = ActiveDG}}. --spec reset_from_funs([mfa_or_funlbl()], callgraph()) -> callgraph(). +-spec reset_from_funs([mfa_or_funlbl()], callgraph()) -> {[scc()], callgraph()}. -reset_from_funs(Funs, #callgraph{digraph = DG} = CG) -> +reset_from_funs(Funs, #callgraph{digraph = DG, active_digraph = ADG} = CG) -> + active_digraph_delete(ADG), SubGraph = digraph_reaching_subgraph(Funs, DG), - Postorder = digraph_finalize(SubGraph), + {NewActiveDG, Postorder} = condensation(SubGraph), digraph_delete(SubGraph), - CG#callgraph{postorder = Postorder}. + {Postorder, CG#callgraph{active_digraph = NewActiveDG}}. --spec module_postorder_from_funs([mfa_or_funlbl()], callgraph()) -> [[module()]]. +-spec module_postorder_from_funs([mfa_or_funlbl()], callgraph()) -> + {[module()], callgraph()}. -module_postorder_from_funs(Funs, #callgraph{digraph = DG} = CG) -> +module_postorder_from_funs(Funs, #callgraph{digraph = DG, + active_digraph = ADG} = CG) -> + active_digraph_delete(ADG), SubGraph = digraph_reaching_subgraph(Funs, DG), - PO = module_postorder(CG#callgraph{digraph = SubGraph}), + {PO, Active} = module_postorder(CG#callgraph{digraph = SubGraph}), digraph_delete(SubGraph), - PO. - + {PO, CG#callgraph{active_digraph = Active}}. + +ets_lookup_dict(Key, Table) -> + try ets:lookup_element(Table, Key, 2) of + Val -> {ok, Val} + catch + _:_ -> error + end. + +ets_lookup_set(Key, Table) -> + ets:lookup(Table, Key) =/= []. + %%---------------------------------------------------------------------- %% Core code %%---------------------------------------------------------------------- @@ -330,36 +343,37 @@ module_postorder_from_funs(Funs, #callgraph{digraph = DG} = CG) -> %% The set of labels in the tree must be disjoint from the set of %% labels already occuring in the callgraph. --spec scan_core_tree(cerl:c_module(), callgraph()) -> callgraph(). +-spec scan_core_tree(cerl:c_module(), callgraph()) -> + {[mfa_or_funlbl()], [callgraph_edge()]}. -scan_core_tree(Tree, #callgraph{calls = OldCalls, - esc = OldEsc, - name_map = OldNameMap, - rec_var_map = OldRecVarMap, - rev_name_map = OldRevNameMap, - self_rec = OldSelfRec} = CG) -> +scan_core_tree(Tree, #callgraph{calls = ETSCalls, + esc = ETSEsc, + name_map = ETSNameMap, + rec_var_map = ETSRecVarMap, + rev_name_map = ETSRevNameMap, + self_rec = ETSSelfRec}) -> %% Build name map and recursion variable maps. - {NewNameMap, NewRevNameMap, NewRecVarMap} = - build_maps(Tree, OldRecVarMap, OldNameMap, OldRevNameMap), - + build_maps(Tree, ETSRecVarMap, ETSNameMap, ETSRevNameMap), + %% First find the module-local dependencies. {Deps0, EscapingFuns, Calls} = dialyzer_dep:analyze(Tree), - NewCalls = dict:merge(fun(_Key, Val, Val) -> Val end, OldCalls, Calls), - NewEsc = sets:union(sets:from_list(EscapingFuns), OldEsc), + true = ets:insert(ETSCalls, dict:to_list(Calls)), + true = ets:insert(ETSEsc, [{E} || E <- EscapingFuns]), + LabelEdges = get_edges_from_deps(Deps0), %% Find the self recursive functions. Named functions get both the %% key and their name for convenience. SelfRecs0 = lists:foldl(fun({Key, Key}, Acc) -> - case dict:find(Key, NewNameMap) of + case ets_lookup_dict(Key, ETSNameMap) of error -> [Key|Acc]; {ok, Name} -> [Key, Name|Acc] end; (_, Acc) -> Acc end, [], LabelEdges), - SelfRecs = sets:union(sets:from_list(SelfRecs0), OldSelfRec), + true = ets:insert(ETSSelfRec, [{S} || S <- SelfRecs0]), - NamedEdges1 = name_edges(LabelEdges, NewNameMap), + NamedEdges1 = name_edges(LabelEdges, ETSNameMap), %% We need to scan for inter-module calls since these are not tracked %% by dialyzer_dep. Note that the caller is always recorded as the @@ -378,27 +392,25 @@ scan_core_tree(Tree, #callgraph{calls = OldCalls, NewNamedEdges1 = [E || {From, To} = E <- NamedEdges1, From =/= top, To =/= top], NamedEdges3 = NewNamedEdges1 ++ NewNamedEdges2, - CG1 = add_edges(NamedEdges3, Names3, CG), - CG1#callgraph{calls = NewCalls, - esc = NewEsc, - name_map = NewNameMap, - rec_var_map = NewRecVarMap, - rev_name_map = NewRevNameMap, - self_rec = SelfRecs}. - -build_maps(Tree, RecVarMap, NameMap, RevNameMap) -> + {Names3, NamedEdges3}. + +build_maps(Tree, ETSRecVarMap, ETSNameMap, ETSRevNameMap) -> %% We only care about the named (top level) functions. The anonymous %% functions will be analysed together with their parents. Defs = cerl:module_defs(Tree), Mod = cerl:atom_val(cerl:module_name(Tree)), - lists:foldl(fun({Var, Function}, {AccNameMap, AccRevNameMap, AccRecVarMap}) -> - FunName = cerl:fname_id(Var), - Arity = cerl:fname_arity(Var), - MFA = {Mod, FunName, Arity}, - {dict:store(get_label(Function), MFA, AccNameMap), - dict:store(MFA, get_label(Function), AccRevNameMap), - dict:store(get_label(Var), MFA, AccRecVarMap)} - end, {NameMap, RevNameMap, RecVarMap}, Defs). + Fun = + fun({Var, Function}) -> + FunName = cerl:fname_id(Var), + Arity = cerl:fname_arity(Var), + MFA = {Mod, FunName, Arity}, + FunLabel = get_label(Function), + VarLabel = get_label(Var), + true = ets:insert(ETSNameMap, {FunLabel, MFA}), + true = ets:insert(ETSRevNameMap, {MFA, FunLabel}), + true = ets:insert(ETSRecVarMap, {VarLabel, MFA}) + end, + lists:foreach(Fun, Defs). get_edges_from_deps(Deps) -> %% Convert the dependencies as produced by dialyzer_dep to a list of @@ -411,22 +423,22 @@ get_edges_from_deps(Deps) -> end, [], Deps), lists:flatten(Edges). -name_edges(Edges, NameMap) -> +name_edges(Edges, ETSNameMap) -> %% If a label is present in the name map it is renamed. Otherwise %% keep the label as the identity. MapFun = fun(X) -> - case dict:find(X, NameMap) of + case ets_lookup_dict(X, ETSNameMap) of error -> X; {ok, MFA} -> MFA end end, - name_edges(Edges, MapFun, NameMap, []). + name_edges(Edges, MapFun, []). -name_edges([{From, To}|Left], MapFun, NameMap, Acc) -> +name_edges([{From, To}|Left], MapFun, Acc) -> NewFrom = MapFun(From), NewTo = MapFun(To), - name_edges(Left, MapFun, NameMap, [{NewFrom, NewTo}|Acc]); -name_edges([], _MapFun, _NameMap, Acc) -> + name_edges(Left, MapFun, [{NewFrom, NewTo}|Acc]); +name_edges([], _MapFun, Acc) -> Acc. scan_core_funs(Tree) -> @@ -478,9 +490,10 @@ get_label(T) -> %%---------------------------------------------------------------------- digraph_add_edges([{From, To}|Left], DG) -> - digraph_add_edges(Left, digraph_add_edge(From, To, DG)); -digraph_add_edges([], DG) -> - DG. + digraph_add_edge(From, To, DG), + digraph_add_edges(Left, DG); +digraph_add_edges([], _DG) -> + ok. digraph_add_edge(From, To, DG) -> case digraph:vertex(DG, From) of @@ -492,13 +505,13 @@ digraph_add_edge(From, To, DG) -> {To, _} -> ok end, digraph:add_edge(DG, {From, To}, From, To, []), - DG. + ok. digraph_confirm_vertices([MFA|Left], DG) -> digraph:add_vertex(DG, MFA, confirmed), digraph_confirm_vertices(Left, DG); -digraph_confirm_vertices([], DG) -> - DG. +digraph_confirm_vertices([], _DG) -> + ok. digraph_remove_external(DG) -> Vertices = digraph:vertices(DG), @@ -522,6 +535,12 @@ remove_unconfirmed([], DG, Unconfirmed) -> digraph_delete(DG) -> digraph:delete(DG). +active_digraph_delete({'d', DG}) -> + digraph:delete(DG); +active_digraph_delete({'e', Out, In}) -> + ets:delete(Out), + ets:delete(In). + digraph_edges(DG) -> digraph:edges(DG). @@ -534,75 +553,6 @@ digraph_in_neighbours(V, DG) -> List -> List end. -%% Pick all the independent nodes (leaves) from one module. Then try -%% to stay within the module until no more independent nodes can be -%% chosen. Then pick a new module and so on. -%% -%% Note that an SCC that ranges over more than one module is -%% considered to belong to all modules to make sure that we do not -%% lose any nodes. - -digraph_postorder(Digraph) -> - %% Remove all self-edges for SCCs. - Edges = [digraph:edge(Digraph, E) || E <- digraph:edges(Digraph)], - SelfEdges = [E || {E, V, V, _} <- Edges], - true = digraph:del_edges(Digraph, SelfEdges), - %% Determine the first module outside of the loop. - Leaves = digraph_leaves(Digraph), - case Leaves =:= [] of - true -> []; - false -> - {Module, Taken} = take_sccs_from_fresh_module(Leaves), - true = digraph:del_vertices(Digraph, Taken), - digraph_postorder(Digraph, Module, [Taken]) - end. - -digraph_postorder(Digraph, LastModule, Acc) -> - Leaves = digraph_leaves(Digraph), - case Leaves =:= [] of - true -> lists:append(lists:reverse(Acc)); - false -> - case [SCC || SCC <- Leaves, scc_belongs_to_module(SCC, LastModule)] of - [] -> - {NewModule, NewTaken} = take_sccs_from_fresh_module(Leaves), - true = digraph:del_vertices(Digraph, NewTaken), - digraph_postorder(Digraph, NewModule, [NewTaken|Acc]); - NewTaken -> - true = digraph:del_vertices(Digraph, NewTaken), - digraph_postorder(Digraph, LastModule, [NewTaken|Acc]) - end - end. - -digraph_leaves(Digraph) -> - [V || V <- digraph:vertices(Digraph), digraph:out_degree(Digraph, V) =:= 0]. - -take_sccs_from_fresh_module(Leaves) -> - NewModule = find_module(hd(Leaves)), - {NewModule, - [SCC || SCC <- Leaves, scc_belongs_to_module(SCC, NewModule)]}. - --spec scc_belongs_to_module(scc(), module()) -> boolean(). - -scc_belongs_to_module([Label|Left], Module) when is_integer(Label) -> - scc_belongs_to_module(Left, Module); -scc_belongs_to_module([{M, _, _}|Left], Module) -> - if M =:= Module -> true; - true -> scc_belongs_to_module(Left, Module) - end; -scc_belongs_to_module([], _Module) -> - false. - --spec find_module(scc()) -> module(). - -find_module([{M, _, _}|_]) -> M; -find_module([Label|Left]) when is_integer(Label) -> find_module(Left). - -digraph_finalize(DG) -> - DG1 = digraph_utils:condensation(DG), - Postorder = digraph_postorder(DG1), - digraph:delete(DG1), - Postorder. - digraph_reaching_subgraph(Funs, DG) -> Vertices = digraph_utils:reaching(Funs, DG), digraph_utils:subgraph(DG, Vertices). @@ -611,20 +561,71 @@ digraph_reaching_subgraph(Funs, DG) -> %% Races %%---------------------------------------------------------------------- +-spec renew_race_info(callgraph(), dict(), [label()], [string()]) -> + callgraph(). + +renew_race_info(#callgraph{race_data_server = RaceDataServer} = CG, + RaceCode, PublicTables, NamedTables) -> + ok = race_data_server_cast( + {renew_race_info, {RaceCode, PublicTables, NamedTables}}, + RaceDataServer), + CG. + +renew_race_info({RaceCode, PublicTables, NamedTables}, + #race_data_state{} = State) -> + State#race_data_state{race_code = RaceCode, + public_tables = PublicTables, + named_tables = NamedTables}. + +-spec renew_race_code(dialyzer_races:races(), callgraph()) -> callgraph(). + +renew_race_code(Races, #callgraph{race_data_server = RaceDataServer} = CG) -> + Fun = dialyzer_races:get_curr_fun(Races), + FunArgs = dialyzer_races:get_curr_fun_args(Races), + Code = lists:reverse(dialyzer_races:get_race_list(Races)), + ok = race_data_server_cast( + {renew_race_code, {Fun, FunArgs, Code}}, + RaceDataServer), + CG. + +renew_race_code_handler({Fun, FunArgs, Code}, + #race_data_state{race_code = RaceCode} = State) -> + State#race_data_state{race_code = dict:store(Fun, [FunArgs, Code], RaceCode)}. + +-spec renew_race_public_tables(label(), callgraph()) -> callgraph(). + +renew_race_public_tables(VarLabel, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = + race_data_server_cast({renew_race_public_tables, VarLabel}, RaceDataServer), + CG. + +renew_race_public_tables_handler(VarLabel, + #race_data_state{public_tables = PT} + = State) -> + State#race_data_state{public_tables = ordsets:add_element(VarLabel, PT)}. + -spec cleanup(callgraph()) -> callgraph(). -cleanup(#callgraph{digraph = Digraph, - name_map = NameMap, - rev_name_map = RevNameMap, - public_tables = PublicTables, - named_tables = NamedTables, - race_code = RaceCode}) -> +cleanup(#callgraph{digraph = Digraph, + name_map = NameMap, + rev_name_map = RevNameMap, + race_data_server = RaceDataServer}) -> #callgraph{digraph = Digraph, - name_map = NameMap, - rev_name_map = RevNameMap, - public_tables = PublicTables, - named_tables = NamedTables, - race_code = RaceCode}. + name_map = NameMap, + rev_name_map = RevNameMap, + race_data_server = race_data_server_call(dup, RaceDataServer)}. + +-spec duplicate(callgraph()) -> callgraph(). + +duplicate(#callgraph{race_data_server = RaceDataServer} = Callgraph) -> + Callgraph#callgraph{ + race_data_server = race_data_server_call(dup, RaceDataServer)}. + +-spec dispose_race_server(callgraph()) -> ok. + +dispose_race_server(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_cast(stop, RaceDataServer). -spec get_digraph(callgraph()) -> digraph(). @@ -633,28 +634,34 @@ get_digraph(#callgraph{digraph = Digraph}) -> -spec get_named_tables(callgraph()) -> [string()]. -get_named_tables(#callgraph{named_tables = NamedTables}) -> - NamedTables. +get_named_tables(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_named_tables, RaceDataServer). -spec get_public_tables(callgraph()) -> [label()]. -get_public_tables(#callgraph{public_tables = PT}) -> - PT. +get_public_tables(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_public_tables, RaceDataServer). -spec get_race_code(callgraph()) -> dict(). -get_race_code(#callgraph{race_code = RaceCode}) -> - RaceCode. +get_race_code(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_race_code, RaceDataServer). -spec get_race_detection(callgraph()) -> boolean(). get_race_detection(#callgraph{race_detection = RD}) -> RD. +-spec get_behaviour_api_calls(callgraph()) -> [{mfa(), mfa()}]. + +get_behaviour_api_calls(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_behaviour_api_calls, RaceDataServer). + -spec race_code_new(callgraph()) -> callgraph(). -race_code_new(Callgraph) -> - Callgraph#callgraph{race_code = dict:new()}. +race_code_new(#callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast(race_code_new, RaceDataServer), + CG. -spec put_digraph(digraph(), callgraph()) -> callgraph(). @@ -663,8 +670,9 @@ put_digraph(Digraph, Callgraph) -> -spec put_race_code(dict(), callgraph()) -> callgraph(). -put_race_code(RaceCode, Callgraph) -> - Callgraph#callgraph{race_code = RaceCode}. +put_race_code(RaceCode, #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_race_code, RaceCode}, RaceDataServer), + CG. -spec put_race_detection(boolean(), callgraph()) -> callgraph(). @@ -673,13 +681,79 @@ put_race_detection(RaceDetection, Callgraph) -> -spec put_named_tables([string()], callgraph()) -> callgraph(). -put_named_tables(NamedTables, Callgraph) -> - Callgraph#callgraph{named_tables = NamedTables}. +put_named_tables(NamedTables, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_named_tables, NamedTables}, RaceDataServer), + CG. -spec put_public_tables([label()], callgraph()) -> callgraph(). -put_public_tables(PublicTables, Callgraph) -> - Callgraph#callgraph{public_tables = PublicTables}. +put_public_tables(PublicTables, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_public_tables, PublicTables}, RaceDataServer), + CG. + +-spec put_behaviour_api_calls([{mfa(), mfa()}], callgraph()) -> callgraph(). + +put_behaviour_api_calls(Calls, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_behaviour_api_calls, Calls}, RaceDataServer), + CG. + + +new_race_data_server() -> + spawn_link(fun() -> race_data_server_loop(#race_data_state{}) end). + +race_data_server_loop(State) -> + receive + {call, From, Ref, Query} -> + Reply = race_data_server_handle_call(Query, State), + From ! {Ref, Reply}, + race_data_server_loop(State); + {cast, stop} -> + ok; + {cast, Message} -> + NewState = race_data_server_handle_cast(Message, State), + race_data_server_loop(NewState) + end. + +race_data_server_call(Query, Server) -> + Ref = make_ref(), + Server ! {call, self(), Ref, Query}, + receive + {Ref, Reply} -> Reply + end. + +race_data_server_cast(Message, Server) -> + Server ! {cast, Message}, + ok. + +race_data_server_handle_cast(race_code_new, State) -> + State#race_data_state{race_code = dict:new()}; +race_data_server_handle_cast({Tag, Data}, State) -> + case Tag of + renew_race_info -> renew_race_info(Data, State); + renew_race_code -> renew_race_code_handler(Data, State); + renew_race_public_tables -> renew_race_public_tables_handler(Data, State); + put_race_code -> State#race_data_state{race_code = Data}; + put_public_tables -> State#race_data_state{public_tables = Data}; + put_named_tables -> State#race_data_state{named_tables = Data}; + put_behaviour_api_calls -> State#race_data_state{beh_api_calls = Data} + end. + +race_data_server_handle_call(Query, + #race_data_state{race_code = RaceCode, + public_tables = PublicTables, + named_tables = NamedTables, + beh_api_calls = BehApiCalls} + = State) -> + case Query of + dup -> spawn_link(fun() -> race_data_server_loop(State) end); + get_race_code -> RaceCode; + get_public_tables -> PublicTables; + get_named_tables -> NamedTables; + get_behaviour_api_calls -> BehApiCalls + end. %%============================================================================= %% Utilities for 'dot' @@ -695,7 +769,7 @@ to_dot(#callgraph{digraph = DG, esc = Esc} = CG, File) -> end end, Escaping = [{Fun(L), {color, red}} - || L <- sets:to_list(Esc), L =/= external], + || L <- [E || {E} <- ets:tab2list(Esc)], L =/= external], Vertices = digraph_edges(DG), hipe_dot:translate_list(Vertices, File, "CG", Escaping). @@ -708,14 +782,41 @@ to_ps(#callgraph{} = CG, File, Args) -> _ = os:cmd(Command), ok. -%------------------------------------------------------------------------------- - --spec put_behaviour_api_calls([{mfa(), mfa()}], callgraph()) -> callgraph(). - -put_behaviour_api_calls(Calls, Callgraph) -> - Callgraph#callgraph{beh_api_calls = Calls}. - --spec get_behaviour_api_calls(callgraph()) -> [{mfa(), mfa()}]. - -get_behaviour_api_calls(Callgraph) -> - Callgraph#callgraph.beh_api_calls. +condensation(G) -> + SCs = digraph_utils:strong_components(G), + V2I = ets:new(condensation_v2i, []), + I2C = ets:new(condensation_i2c, []), + I2I = ets:new(condensation_i2i, [bag]), + CFun = + fun(SC, N) -> + lists:foreach(fun(V) -> true = ets:insert(V2I, {V,N}) end, SC), + true = ets:insert(I2C, {N, SC}), + N + 1 + end, + lists:foldl(CFun, 1, SCs), + Fun1 = + fun({V1, V2}) -> + I1 = ets:lookup_element(V2I, V1, 2), + I2 = ets:lookup_element(V2I, V2, 2), + case I1 =:= I2 of + true -> true; + false -> ets:insert(I2I, {I1, I2}) + end + end, + lists:foreach(Fun1, digraph:edges(G)), + Fun3 = + fun({I1, I2}, {Out, In}) -> + SC1 = ets:lookup_element(I2C, I1, 2), + SC2 = ets:lookup_element(I2C, I2, 2), + {dict:append(SC1, SC2, Out), dict:append(SC2, SC1, In)} + end, + {OutDict, InDict} = ets:foldl(Fun3, {dict:new(), dict:new()}, I2I), + [OutETS, InETS] = + [ets:new(Name,[{read_concurrency, true}]) || + Name <- [callgraph_deps_out, callgraph_deps_in]], + ets:insert(OutETS, dict:to_list(OutDict)), + ets:insert(InETS, dict:to_list(InDict)), + ets:delete(V2I), + ets:delete(I2C), + ets:delete(I2I), + {{'e', OutETS, InETS}, SCs}. diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index 04a0db890f..6732d96b98 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -2,7 +2,7 @@ %%------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-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 @@ -188,6 +188,7 @@ plt_common(#options{init_plts = [InitPlt]} = Opts, RemoveFiles, AddFiles) -> ok -> case Opts#options.output_plt of none -> ok; + InitPlt -> ok; OutPlt -> {ok, Binary} = file:read_file(InitPlt), ok = file:write_file(OutPlt, Binary) @@ -393,10 +394,12 @@ do_analysis(Files, Options, Plt, PltInfo) -> defines = Options#options.defines, include_dirs = Options#options.include_dirs, files = Files, - start_from = Options#options.from, + start_from = Options#options.from, + timing = Options#options.timing, plt = Plt, use_contracts = Options#options.use_contracts, - callgraph_file = Options#options.callgraph_file}, + callgraph_file = Options#options.callgraph_file, + solvers = Options#options.solvers}, State3 = start_analysis(State2, InitAnalysis), {T1, _} = statistics(wall_clock), Return = cl_loop(State3), diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl index ff8fc39a5e..2ea3d3af5a 100644 --- a/lib/dialyzer/src/dialyzer_cl_parse.erl +++ b/lib/dialyzer/src/dialyzer_cl_parse.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-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 @@ -164,6 +164,13 @@ cl(["--src"|T]) -> cl(["--no_spec"|T]) -> put(dialyzer_options_use_contracts, false), cl(T); +cl(["--statistics"|T]) -> + put(dialyzer_timing, true), + cl(T); +cl(["--resources"|T]) -> + put(dialyzer_options_report_mode, quiet), + put(dialyzer_timing, debug), + cl(T); cl(["-v"|_]) -> io:format("Dialyzer version "++?VSN++"\n"), erlang:halt(?RET_NOTHING_SUSPICIOUS); @@ -191,6 +198,9 @@ cl(["--gui"|T]) -> cl(["--wx"|T]) -> put(dialyzer_options_mode, {gui, wx}), cl(T); +cl(["--solver",Solver|T]) -> % not documented + append_var(dialyzer_solvers, [list_to_atom(Solver)]), + cl(T); cl([H|_] = L) -> case filelib:is_file(H) orelse filelib:is_dir(H) of true -> @@ -250,6 +260,8 @@ init() -> put(dialyzer_output_format, formatted), put(dialyzer_filename_opt, basename), put(dialyzer_options_check_plt, DefaultOpts#options.check_plt), + put(dialyzer_timing, DefaultOpts#options.timing), + put(dialyzer_solvers, DefaultOpts#options.solvers), ok. append_defines([Def, Val]) -> @@ -290,6 +302,7 @@ cl_options() -> {filename_opt, get(dialyzer_filename_opt)}, {analysis_type, get(dialyzer_options_analysis_type)}, {get_warnings, get(dialyzer_options_get_warnings)}, + {timing, get(dialyzer_timing)}, {callgraph_file, get(dialyzer_callgraph_file)} |common_options()]. @@ -302,7 +315,8 @@ common_options() -> {report_mode, get(dialyzer_options_report_mode)}, {use_spec, get(dialyzer_options_use_contracts)}, {warnings, get(dialyzer_warnings)}, - {check_plt, get(dialyzer_options_check_plt)}]. + {check_plt, get(dialyzer_options_check_plt)}, + {solvers, get(dialyzer_solvers)}]. %%----------------------------------------------------------------------- @@ -351,7 +365,7 @@ help_message() -> [--apps applications] [-o outfile] [--build_plt] [--add_to_plt] [--remove_from_plt] [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] - [--no_native] [--fullpath] + [--no_native] [--fullpath] [--statistics] Options: files_or_dirs (for backwards compatibility also as: -c files_or_dirs) Use Dialyzer from the command line to detect defects in the @@ -418,6 +432,9 @@ Options: Make Dialyzer a bit more quiet. --verbose Make Dialyzer a bit more verbose. + --statistics + Prints information about the progress of execution (analysis phases, + time spent in each and size of the relative input). --build_plt The analysis starts from an empty plt and creates a new one from the files specified with -c and -r. Only works for beam files. diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl index f1e87affbd..9989118671 100644 --- a/lib/dialyzer/src/dialyzer_codeserver.erl +++ b/lib/dialyzer/src/dialyzer_codeserver.erl @@ -52,9 +52,7 @@ new/0, set_next_core_label/2, set_temp_records/2, - store_records/3, store_temp_records/3, - store_contracts/3, store_temp_contracts/4]). -export_type([codeserver/0]). @@ -63,85 +61,159 @@ %%-------------------------------------------------------------------- --record(codeserver, {table_pid :: pid(), - exported_types = sets:new() :: set(), % set(mfa()) - temp_exported_types = sets:new() :: set(), % set(mfa()) - exports = sets:new() :: set(), % set(mfa()) - next_core_label = 0 :: label(), - records = dict:new() :: dict(), - temp_records = dict:new() :: dict(), - contracts = dict:new() :: dict(), - callbacks = dict:new() :: dict(), - temp_contracts = dict:new() :: dict(), - temp_callbacks = dict:new() :: dict() +-type dict_ets() :: ets:tid(). +-type set_ets() :: ets:tid(). + +-record(codeserver, {next_core_label = 0 :: label(), + code :: dict_ets(), + exported_types :: set_ets(), % set(mfa()) + records :: dict_ets(), + contracts :: dict_ets(), + callbacks :: dict_ets(), + exports :: 'clean' | set_ets(), % set(mfa()) + temp_exported_types :: 'clean' | set_ets(), % set(mfa()) + temp_records :: 'clean' | dict_ets(), + temp_contracts :: 'clean' | dict_ets(), + temp_callbacks :: 'clean' | dict_ets() }). -opaque codeserver() :: #codeserver{}. %%-------------------------------------------------------------------- +ets_dict_find(Key, Table) -> + try ets:lookup_element(Table, Key, 2) of + Val -> {ok, Val} + catch + _:_ -> error + end. + +ets_dict_store(Key, Element, Table) -> + true = ets:insert(Table, {Key, Element}), + Table. + +ets_dict_store_dict(Dict, Table) -> + true = ets:insert(Table, dict:to_list(Dict)). + +ets_dict_to_dict(Table) -> + Fold = fun({Key,Value}, Dict) -> dict:store(Key, Value, Dict) end, + ets:foldl(Fold, dict:new(), Table). + +ets_set_is_element(Key, Table) -> + case ets:lookup(Table, Key) of + [] -> false; + _ -> true + end. + +ets_set_insert_set(Set, Table) -> + ets_set_insert_list(sets:to_list(Set), Table). + +ets_set_insert_list(List, Table) -> + true = ets:insert(Table, [{E} || E <- List]). + +ets_set_to_set(Table) -> + Fold = fun({E}, Set) -> sets:add_element(E, Set) end, + ets:foldl(Fold, sets:new(), Table). + +ets_read_concurrent_table(Name) -> + ets:new(Name,[{read_concurrency, true}]). + +%%-------------------------------------------------------------------- + -spec new() -> codeserver(). new() -> - #codeserver{table_pid = table__new()}. + CodeOptions = [compressed, public, {read_concurrency, true}], + Code = ets:new(dialyzer_codeserver_code, CodeOptions), + TempOptions = [public, {write_concurrency, true}], + [Exports, TempExportedTypes, TempRecords, TempContracts, TempCallbacks] = + [ets:new(Name, TempOptions) || + Name <- + [dialyzer_codeserver_exports, dialyzer_codeserver_temp_exported_types, + dialyzer_codeserver_temp_records, dialyzer_codeserver_temp_contracts, + dialyzer_codeserver_temp_callbacks]], + #codeserver{code = Code, + exports = Exports, + temp_exported_types = TempExportedTypes, + temp_records = TempRecords, + temp_contracts = TempContracts, + temp_callbacks = TempCallbacks}. -spec delete(codeserver()) -> 'ok'. -delete(#codeserver{table_pid = TablePid}) -> - table__delete(TablePid). +delete(#codeserver{code = Code, exported_types = ExportedTypes, + records = Records, contracts = Contracts, + callbacks = Callbacks}) -> + lists:foreach(fun ets:delete/1, + [Code, ExportedTypes, Records, Contracts, Callbacks]). -spec insert(atom(), cerl:c_module(), codeserver()) -> codeserver(). insert(Mod, ModCode, CS) -> - NewTablePid = table__insert(CS#codeserver.table_pid, Mod, ModCode), - CS#codeserver{table_pid = NewTablePid}. + Name = cerl:module_name(ModCode), + Exports = cerl:module_exports(ModCode), + Attrs = cerl:module_attrs(ModCode), + Defs = cerl:module_defs(ModCode), + As = cerl:get_ann(ModCode), + Funs = + [{{Mod, cerl:fname_id(Var), cerl:fname_arity(Var)}, + Val} || Val = {Var, _Fun} <- Defs], + Keys = [Key || {Key, _Value} <- Funs], + ModEntry = {Mod, {Name, Exports, Attrs, Keys, As}}, + true = ets:insert(CS#codeserver.code, [ModEntry|Funs]), + CS. + +-spec get_temp_exported_types(codeserver()) -> set(). + +get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) -> + ets_set_to_set(TempExpTypes). -spec insert_temp_exported_types(set(), codeserver()) -> codeserver(). insert_temp_exported_types(Set, CS) -> - CS#codeserver{temp_exported_types = Set}. + TempExportedTypes = CS#codeserver.temp_exported_types, + true = ets_set_insert_set(Set, TempExportedTypes), + CS. -spec insert_exports([mfa()], codeserver()) -> codeserver(). insert_exports(List, #codeserver{exports = Exports} = CS) -> - Set = sets:from_list(List), - NewExports = sets:union(Exports, Set), - CS#codeserver{exports = NewExports}. + true = ets_set_insert_list(List, Exports), + CS. -spec is_exported(mfa(), codeserver()) -> boolean(). is_exported(MFA, #codeserver{exports = Exports}) -> - sets:is_element(MFA, Exports). + ets_set_is_element(MFA, Exports). -spec get_exported_types(codeserver()) -> set(). % set(mfa()) get_exported_types(#codeserver{exported_types = ExpTypes}) -> - ExpTypes. - --spec get_temp_exported_types(codeserver()) -> set(). - -get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) -> - TempExpTypes. + ets_set_to_set(ExpTypes). -spec get_exports(codeserver()) -> set(). % set(mfa()) get_exports(#codeserver{exports = Exports}) -> - Exports. + ets_set_to_set(Exports). -spec finalize_exported_types(set(), codeserver()) -> codeserver(). finalize_exported_types(Set, CS) -> - CS#codeserver{exported_types = Set, temp_exported_types = sets:new()}. + ExportedTypes = ets_read_concurrent_table(dialyzer_codeserver_exported_types), + true = ets_set_insert_set(Set, ExportedTypes), + TempExpTypes = CS#codeserver.temp_exported_types, + true = ets:delete(TempExpTypes), + CS#codeserver{exported_types = ExportedTypes, temp_exported_types = clean}. -spec lookup_mod_code(atom(), codeserver()) -> cerl:c_module(). lookup_mod_code(Mod, CS) when is_atom(Mod) -> - table__lookup(CS#codeserver.table_pid, Mod). + table__lookup(CS#codeserver.code, Mod). -spec lookup_mfa_code(mfa(), codeserver()) -> {cerl:c_var(), cerl:c_fun()}. lookup_mfa_code({_M, _F, _A} = MFA, CS) -> - table__lookup(CS#codeserver.table_pid, MFA). + table__lookup(CS#codeserver.code, MFA). -spec get_next_core_label(codeserver()) -> label(). @@ -153,20 +225,10 @@ get_next_core_label(#codeserver{next_core_label = NCL}) -> set_next_core_label(NCL, CS) -> CS#codeserver{next_core_label = NCL}. --spec store_records(atom(), dict(), codeserver()) -> codeserver(). - -store_records(Mod, Dict, #codeserver{records = RecDict} = CS) - when is_atom(Mod) -> - case dict:size(Dict) =:= 0 of - true -> CS; - false -> CS#codeserver{records = dict:store(Mod, Dict, RecDict)} - end. - -spec lookup_mod_records(atom(), codeserver()) -> dict(). -lookup_mod_records(Mod, #codeserver{records = RecDict}) - when is_atom(Mod) -> - case dict:find(Mod, RecDict) of +lookup_mod_records(Mod, #codeserver{records = RecDict}) when is_atom(Mod) -> + case ets_dict_find(Mod, RecDict) of error -> dict:new(); {ok, Dict} -> Dict end. @@ -174,7 +236,7 @@ lookup_mod_records(Mod, #codeserver{records = RecDict}) -spec get_records(codeserver()) -> dict(). get_records(#codeserver{records = RecDict}) -> - RecDict. + ets_dict_to_dict(RecDict). -spec store_temp_records(atom(), dict(), codeserver()) -> codeserver(). @@ -182,59 +244,58 @@ store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS) when is_atom(Mod) -> case dict:size(Dict) =:= 0 of true -> CS; - false -> CS#codeserver{temp_records = dict:store(Mod, Dict, TempRecDict)} + false -> CS#codeserver{temp_records = ets_dict_store(Mod, Dict, TempRecDict)} end. -spec get_temp_records(codeserver()) -> dict(). get_temp_records(#codeserver{temp_records = TempRecDict}) -> - TempRecDict. + ets_dict_to_dict(TempRecDict). -spec set_temp_records(dict(), codeserver()) -> codeserver(). set_temp_records(Dict, CS) -> - CS#codeserver{temp_records = Dict}. + true = ets:delete(CS#codeserver.temp_records), + TempRecords = ets:new(dialyzer_codeserver_temp_records,[]), + true = ets_dict_store_dict(Dict, TempRecords), + CS#codeserver{temp_records = TempRecords}. -spec finalize_records(dict(), codeserver()) -> codeserver(). finalize_records(Dict, CS) -> - CS#codeserver{records = Dict, temp_records = dict:new()}. - --spec store_contracts(atom(), dict(), codeserver()) -> codeserver(). - -store_contracts(Mod, Dict, #codeserver{contracts = C} = CS) when is_atom(Mod) -> - case dict:size(Dict) =:= 0 of - true -> CS; - false -> CS#codeserver{contracts = dict:store(Mod, Dict, C)} - end. + true = ets:delete(CS#codeserver.temp_records), + Records = ets_read_concurrent_table(dialyzer_codeserver_records), + true = ets_dict_store_dict(Dict, Records), + CS#codeserver{records = Records, temp_records = clean}. -spec lookup_mod_contracts(atom(), codeserver()) -> dict(). lookup_mod_contracts(Mod, #codeserver{contracts = ContDict}) when is_atom(Mod) -> - case dict:find(Mod, ContDict) of + case ets_dict_find(Mod, ContDict) of error -> dict:new(); - {ok, Dict} -> Dict + {ok, Keys} -> + dict:from_list([get_contract_pair(Key, ContDict)|| Key <- Keys]) end. +get_contract_pair(Key, ContDict) -> + {Key, ets:lookup_element(ContDict, Key, 2)}. + -spec lookup_mfa_contract(mfa(), codeserver()) -> 'error' | {'ok', dialyzer_contracts:file_contract()}. -lookup_mfa_contract({M,_F,_A} = MFA, #codeserver{contracts = ContDict}) -> - case dict:find(M, ContDict) of - error -> error; - {ok, Dict} -> dict:find(MFA, Dict) - end. +lookup_mfa_contract(MFA, #codeserver{contracts = ContDict}) -> + ets_dict_find(MFA, ContDict). -spec get_contracts(codeserver()) -> dict(). get_contracts(#codeserver{contracts = ContDict}) -> - ContDict. + ets_dict_to_dict(ContDict). --spec get_callbacks(codeserver()) -> dict(). +-spec get_callbacks(codeserver()) -> list(). get_callbacks(#codeserver{callbacks = CallbDict}) -> - CallbDict. + ets:tab2list(CallbDict). -spec store_temp_contracts(atom(), dict(), dict(), codeserver()) -> codeserver(). @@ -246,91 +307,44 @@ store_temp_contracts(Mod, SpecDict, CallbackDict, CS1 = case dict:size(SpecDict) =:= 0 of true -> CS; - false -> CS#codeserver{temp_contracts = dict:store(Mod, SpecDict, Cn)} + false -> + CS#codeserver{temp_contracts = ets_dict_store(Mod, SpecDict, Cn)} end, case dict:size(CallbackDict) =:= 0 of true -> CS1; - false -> CS1#codeserver{temp_callbacks = dict:store(Mod, CallbackDict, Cb)} + false -> + CS1#codeserver{temp_callbacks = ets_dict_store(Mod, CallbackDict, Cb)} end. -spec get_temp_contracts(codeserver()) -> {dict(), dict()}. get_temp_contracts(#codeserver{temp_contracts = TempContDict, temp_callbacks = TempCallDict}) -> - {TempContDict, TempCallDict}. + {ets_dict_to_dict(TempContDict), ets_dict_to_dict(TempCallDict)}. -spec finalize_contracts(dict(), dict(), codeserver()) -> codeserver(). -finalize_contracts(CnDict, CbDict, CS) -> - CS#codeserver{contracts = CnDict, - callbacks = CbDict, - temp_contracts = dict:new(), - temp_callbacks = dict:new() - }. - -table__new() -> - spawn_link(fun() -> table__loop(none, dict:new()) end). - -table__delete(TablePid) -> - TablePid ! stop, - ok. - -table__lookup(TablePid, Key) -> - TablePid ! {self(), lookup, Key}, - receive - {TablePid, Key, Ans} -> Ans - end. - -table__insert(TablePid, Key, Val) -> - TablePid ! {insert, [{Key, term_to_binary(Val, [compressed])}]}, - TablePid. - -table__loop(Cached, Map) -> - receive - stop -> ok; - {Pid, lookup, {M, F, A} = MFA} -> - {NewCached, Ans} = - case Cached of - {M, Tree} -> - Val = find_fun(F, A, Tree), - {Cached, Val}; - _ -> - Tree = fetch_and_expand(M, Map), - Val = find_fun(F, A, Tree), - {{M, Tree}, Val} - end, - Pid ! {self(), MFA, Ans}, - table__loop(NewCached, Map); - {Pid, lookup, Mod} when is_atom(Mod) -> - Ans = case Cached of - {Mod, Tree} -> Tree; - _ -> fetch_and_expand(Mod, Map) - end, - Pid ! {self(), Mod, Ans}, - table__loop({Mod, Ans}, Map); - {insert, List} -> - NewMap = lists:foldl(fun({Key, Val}, AccMap) -> - dict:store(Key, Val, AccMap) - end, Map, List), - table__loop(Cached, NewMap) - end. - -fetch_and_expand(Mod, Map) -> - try - Bin = dict:fetch(Mod, Map), - binary_to_term(Bin) - catch - _:_ -> - S = atom_to_list(Mod), - Msg = "found no module named '" ++ S ++ "' in the analyzed files", - exit({error, Msg}) - end. - -find_fun(F, A, Tree) -> - Pred = - fun({Var, _Fun}) -> - (cerl:fname_id(Var) =/= F) orelse - (cerl:fname_arity(Var) =/= A) - end, - [Val|_] = lists:dropwhile(Pred, cerl:module_defs(Tree)), - Val. +finalize_contracts(SpecDict, CallbackDict, CS) -> + Contracts = ets_read_concurrent_table(dialyzer_codeserver_contracts), + Callbacks = ets_read_concurrent_table(dialyzer_codeserver_callbacks), + Contracts = dict:fold(fun decompose_spec_dict/3, Contracts, SpecDict), + Callbacks = dict:fold(fun decompose_cb_dict/3, Callbacks, CallbackDict), + CS#codeserver{contracts = Contracts, callbacks = Callbacks, + temp_contracts = clean, temp_callbacks = clean}. + +decompose_spec_dict(Mod, Dict, Table) -> + Keys = dict:fetch_keys(Dict), + true = ets:insert(Table, dict:to_list(Dict)), + true = ets:insert(Table, {Mod, Keys}), + Table. + +decompose_cb_dict(_Mod, Dict, Table) -> + true = ets:insert(Table, dict:to_list(Dict)), + Table. + +table__lookup(TablePid, M) when is_atom(M) -> + {Name, Exports, Attrs, Keys, As} = ets:lookup_element(TablePid, M, 2), + Defs = [table__lookup(TablePid, Key) || Key <- Keys], + cerl:ann_c_module(As, Name, Exports, Attrs, Defs); +table__lookup(TablePid, MFA) -> + ets:lookup_element(TablePid, MFA, 2). diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index 2b78b736ab..76f23d00b9 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -344,7 +344,7 @@ insert_constraints([{subtype, Type1, Type2}|Left], Dict) -> false -> %% A lot of things should change to add supertypes throw({error, io_lib:format("First argument of is_subtype constraint " - "must be a type variable\n", [])}) + "must be a type variable: ~p\n", [Type1])}) end; insert_constraints([], Dict) -> Dict. @@ -385,9 +385,8 @@ contract_from_form([{type, _L1, bounded_fun, RecDict, FileLine, TypeAcc, FormAcc) -> TypeFun = fun(ExpTypes, AllRecords) -> - Constr1 = [constraint_from_form(C, RecDict, ExpTypes, AllRecords) - || C <- Constr], - VarDict = insert_constraints(Constr1, dict:new()), + {Constr1, VarDict} = + process_constraints(Constr, RecDict, ExpTypes, AllRecords), Type = erl_types:t_from_form(Form, RecDict, VarDict), NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords), {NewType, Constr1} @@ -398,18 +397,66 @@ contract_from_form([{type, _L1, bounded_fun, contract_from_form([], _RecDict, _FileLine, TypeAcc, FormAcc) -> {lists:reverse(TypeAcc), lists:reverse(FormAcc)}. -constraint_from_form({type, _, constraint, [{atom, _, is_subtype}, - [Type1, Type2]]}, RecDict, - ExpTypes, AllRecords) -> - T1 = erl_types:t_from_form(Type1, RecDict), - T2 = erl_types:t_from_form(Type2, RecDict), - T3 = erl_types:t_solve_remote(T1, ExpTypes, AllRecords), - T4 = erl_types:t_solve_remote(T2, ExpTypes, AllRecords), - {subtype, T3, T4}; -constraint_from_form({type, _, constraint, [{atom,_,Name}, List]}, _RecDict, - _ExpTypes, _AllRecords) -> - N = length(List), - throw({error, io_lib:format("Unsupported type guard ~w/~w\n", [Name, N])}). +process_constraints(Constrs, RecDict, ExpTypes, AllRecords) -> + Init = initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords), + constraints_fixpoint(Init, RecDict, ExpTypes, AllRecords). + +initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords) -> + initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords, []). + +initialize_constraints([], _RecDict, _ExpTypes, _AllRecords, Acc) -> + Acc; +initialize_constraints([Constr|Rest], RecDict, ExpTypes, AllRecords, Acc) -> + case Constr of + {type, _, constraint, [{atom, _, is_subtype}, [Type1, Type2]]} -> + T1 = final_form(Type1, RecDict, ExpTypes, AllRecords, dict:new()), + Entry = {T1, Type2}, + initialize_constraints(Rest, RecDict, ExpTypes, AllRecords, [Entry|Acc]); + {type, _, constraint, [{atom,_,Name}, List]} -> + N = length(List), + throw({error, + io_lib:format("Unsupported type guard ~w/~w\n", [Name, N])}) + end. + +constraints_fixpoint(Constrs, RecDict, ExpTypes, AllRecords) -> + VarDict = + constraints_to_dict(Constrs, RecDict, ExpTypes, AllRecords, dict:new()), + constraints_fixpoint(VarDict, Constrs, RecDict, ExpTypes, AllRecords). + +constraints_fixpoint(OldVarDict, Constrs, RecDict, ExpTypes, AllRecords) -> + NewVarDict = + constraints_to_dict(Constrs, RecDict, ExpTypes, AllRecords, OldVarDict), + case NewVarDict of + OldVarDict -> + DictFold = + fun(Key, Value, Acc) -> + [{subtype, erl_types:t_var(Key), Value}|Acc] + end, + FinalConstrs = dict:fold(DictFold, [], NewVarDict), + {FinalConstrs, NewVarDict}; + _Other -> + constraints_fixpoint(NewVarDict, Constrs, RecDict, ExpTypes, AllRecords) + end. + +-define(TYPE_LIMIT, 4). + +final_form(Form, RecDict, ExpTypes, AllRecords, VarDict) -> + T1 = erl_types:t_from_form(Form, RecDict, VarDict), + T2 = erl_types:t_solve_remote(T1, ExpTypes, AllRecords), + erl_types:t_limit(T2, ?TYPE_LIMIT). + +constraints_to_dict(Constrs, RecDict, ExpTypes, AllRecords, VarDict) -> + Subtypes = + constraints_to_subs(Constrs, RecDict, ExpTypes, AllRecords, VarDict, []), + insert_constraints(Subtypes, dict:new()). + +constraints_to_subs([], _RecDict, _ExpTypes, _AllRecords, _VarDict, Acc) -> + Acc; +constraints_to_subs([C|Rest], RecDict, ExpTypes, AllRecords, VarDict, Acc) -> + {T1, Form2} = C, + T2 = final_form(Form2, RecDict, ExpTypes, AllRecords, VarDict), + NewAcc = [{subtype, T1, T2}|Acc], + constraints_to_subs(Rest, RecDict, ExpTypes, AllRecords, VarDict, NewAcc). %% Gets the most general domain of a list of domains of all %% the overloaded contracts diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl new file mode 100644 index 0000000000..28dcf098af --- /dev/null +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -0,0 +1,251 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2011. 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% +%% + +%%%------------------------------------------------------------------- +%%% File : dialyzer_coordinator.erl +%%% Authors : Stavros Aronis <[email protected]> +%%%------------------------------------------------------------------- + +-module(dialyzer_coordinator). + +%%% Export for dialyzer main process +-export([parallel_job/4]). + +%%% Exports for all possible workers +-export([wait_activation/0, job_done/3]). + +%%% Exports for the typesig and dataflow analysis workers +-export([sccs_to_pids/2, request_activation/1]). + +%%% Exports for the compilation workers +-export([get_next_label/2]). + +-export_type([coordinator/0, mode/0, init_data/0, result/0]). + +%%-------------------------------------------------------------------- + +-type collector() :: pid(). +-type regulator() :: pid(). +-type scc_to_pid() :: ets:tid() | 'unused'. + +-type coordinator() :: {collector(), regulator(), scc_to_pid()}. %%opaque +-type timing() :: dialyzer_timing:timing_server(). + +-type scc() :: [mfa_or_funlbl()]. +-type mode() :: 'typesig' | 'dataflow' | 'compile' | 'warnings'. + +-type compile_jobs() :: [file:filename()]. +-type typesig_jobs() :: [scc()]. +-type dataflow_jobs() :: [module()]. +-type warnings_jobs() :: [module()]. + +-type compile_init_data() :: dialyzer_analysis_callgraph:compile_init_data(). +-type typesig_init_data() :: dialyzer_succ_typings:typesig_init_data(). +-type dataflow_init_data() :: dialyzer_succ_typings:dataflow_init_data(). +-type warnings_init_data() :: dialyzer_succ_typings:warnings_init_data(). + +-type compile_result() :: dialyzer_analysis_callgraph:compile_result(). +-type typesig_result() :: [mfa_or_funlbl()]. +-type dataflow_result() :: [mfa_or_funlbl()]. +-type warnings_result() :: [dial_warning()]. + +-type init_data() :: compile_init_data() | typesig_init_data() | + dataflow_init_data() | warnings_init_data(). + +-type result() :: compile_result() | typesig_result() | + dataflow_result() | warnings_result(). + +-type job() :: scc() | module() | file:filename(). +-type job_result() :: dialyzer_analysis_callgraph:one_file_result() | + typesig_result() | dataflow_result() | warnings_result(). + +-record(state, {mode :: mode(), + active = 0 :: integer(), + result :: result(), + next_label = 0 :: integer(), + init_data :: init_data(), + regulator :: regulator(), + scc_to_pid :: scc_to_pid() + }). + +-include("dialyzer.hrl"). + +%%-------------------------------------------------------------------- + +-spec parallel_job('compile', compile_jobs(), compile_init_data(), timing()) -> + {compile_result(), integer()}; + ('typesig', typesig_jobs(), typesig_init_data(), timing()) -> + typesig_result(); + ('dataflow', dataflow_jobs(), dataflow_init_data(), + timing()) -> dataflow_result(); + ('warnings', warnings_jobs(), warnings_init_data(), + timing()) -> warnings_result(). + +parallel_job(Mode, Jobs, InitData, Timing) -> + State = spawn_jobs(Mode, Jobs, InitData, Timing), + collect_result(State). + +spawn_jobs(Mode, Jobs, InitData, Timing) -> + Collector = self(), + Regulator = spawn_regulator(), + TypesigOrDataflow = (Mode =:= 'typesig') orelse (Mode =:= 'dataflow'), + SCCtoPID = + case TypesigOrDataflow of + true -> ets:new(scc_to_pid, [{read_concurrency, true}]); + false -> unused + end, + Coordinator = {Collector, Regulator, SCCtoPID}, + Fold = + fun(Job, Count) -> + Pid = dialyzer_worker:launch(Mode, Job, InitData, Coordinator), + case TypesigOrDataflow of + true -> true = ets:insert(SCCtoPID, {Job, Pid}), ok; + false -> request_activation(Regulator, Pid) + end, + Count + 1 + end, + JobCount = lists:foldl(Fold, 0, Jobs), + Unit = + case Mode of + 'typesig' -> "SCCs"; + _ -> "modules" + end, + dialyzer_timing:send_size_info(Timing, JobCount, Unit), + InitResult = + case Mode of + 'compile' -> dialyzer_analysis_callgraph:compile_init_result(); + _ -> [] + end, + #state{mode = Mode, active = JobCount, result = InitResult, next_label = 0, + init_data = InitData, regulator = Regulator, scc_to_pid = SCCtoPID}. + +collect_result(#state{mode = Mode, active = Active, result = Result, + next_label = NextLabel, init_data = InitData, + regulator = Regulator, scc_to_pid = SCCtoPID} = State) -> + receive + {next_label_request, Estimation, Pid} -> + Pid ! {next_label_reply, NextLabel}, + collect_result(State#state{next_label = NextLabel + Estimation}); + {done, Job, Data} -> + NewResult = update_result(Mode, InitData, Job, Data, Result), + case Active of + 1 -> + kill_regulator(Regulator), + case Mode of + 'compile' -> + {NewResult, NextLabel}; + X when X =:= 'typesig'; X =:= 'dataflow' -> + ets:delete(SCCtoPID), + NewResult; + 'warnings' -> + NewResult + end; + N -> + collect_result(State#state{result = NewResult, active = N - 1}) + end + end. + +update_result(Mode, InitData, Job, Data, Result) -> + case Mode of + 'compile' -> + dialyzer_analysis_callgraph:add_to_result(Job, Data, Result, + InitData); + X when X =:= 'typesig'; X =:= 'dataflow' -> + dialyzer_succ_typings:lookup_names(Data, InitData) ++ Result; + 'warnings' -> + Data ++ Result + end. + +-spec sccs_to_pids([scc() | module()], coordinator()) -> + {[dialyzer_worker:worker()], [scc() | module()]}. + +sccs_to_pids(SCCs, {_Collector, _Regulator, SCCtoPID}) -> + Fold = + fun(SCC, {Pids, Unknown}) -> + try ets:lookup_element(SCCtoPID, SCC, 2) of + Result -> {[Result|Pids], Unknown} + catch + _:_ -> {Pids, [SCC|Unknown]} + end + end, + lists:foldl(Fold, {[], []}, SCCs). + +-spec job_done(job(), job_result(), coordinator()) -> ok. + +job_done(Job, Result, {Collector, Regulator, _SCCtoPID}) -> + Regulator ! done, + Collector ! {done, Job, Result}, + ok. + +-spec get_next_label(integer(), coordinator()) -> integer(). + +get_next_label(EstimatedSize, {Collector, _Regulator, _SCCtoPID}) -> + Collector ! {next_label_request, EstimatedSize, self()}, + receive + {next_label_reply, NextLabel} -> NextLabel + end. + +-spec wait_activation() -> ok. + +wait_activation() -> + receive activate -> ok end. + +activate_pid(Pid) -> + Pid ! activate. + +-spec request_activation(coordinator()) -> ok. + +request_activation({_Collector, Regulator, _SCCtoPID}) -> + Regulator ! {req, self()}, + wait_activation(). + +request_activation(Regulator, Pid) -> + Regulator ! {req, Pid}, + ok. + +spawn_regulator() -> + InitTickets = dialyzer_utils:parallelism(), + spawn_link(fun() -> regulator_loop(InitTickets, queue:new()) end). + +regulator_loop(Tickets, Queue) -> + receive + {req, Pid} -> + case Tickets of + 0 -> + regulator_loop(0, queue:in(Pid, Queue)); + N -> + activate_pid(Pid), + regulator_loop(N-1, Queue) + end; + done -> + {Waiting, NewQueue} = queue:out(Queue), + NewTickets = + case Waiting of + empty -> Tickets + 1; + {value, Pid} -> + activate_pid(Pid), + Tickets + end, + regulator_loop(NewTickets, NewQueue); + stop -> ok + end. + +kill_regulator(Regulator) -> + Regulator ! stop. diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index bd375b04fa..7131633da1 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -32,13 +32,11 @@ %% Data structure interfaces. -export([state__add_warning/2, state__cleanup/1, + state__duplicate/1, dispose_state/1, state__get_callgraph/1, state__get_races/1, state__get_records/1, state__put_callgraph/2, state__put_races/2, state__records_only/1]). -%% Debug and test interfaces. --export([get_top_level_signatures/2, pp/1]). - -export_type([state/0]). -include("dialyzer.hrl"). @@ -67,7 +65,7 @@ %%-define(DEBUG, true). %%-define(DEBUG_PP, true). -%%-define(DOT, true). +%%-define(DEBUG_TIME, true). -ifdef(DEBUG). -import(erl_types, [t_to_string/1]). @@ -111,7 +109,7 @@ -spec get_warnings(cerl:c_module(), dialyzer_plt:plt(), dialyzer_callgraph:callgraph(), dict(), set()) -> - {[dial_warning()], dict(), dict(), [label()], [string()]}. + {[dial_warning()], dict()}. get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) -> State1 = analyze_module(Tree, Plt, Callgraph, Records, true), @@ -119,145 +117,14 @@ get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) -> State3 = state__renew_warnings(state__get_warnings(State2, NoWarnUnused), State2), State4 = state__get_race_warnings(State3), - Callgraph1 = State2#state.callgraph, - {State4#state.warnings, state__all_fun_types(State4), - dialyzer_callgraph:get_race_code(Callgraph1), - dialyzer_callgraph:get_public_tables(Callgraph1), - dialyzer_callgraph:get_named_tables(Callgraph1)}. + {State4#state.warnings, state__all_fun_types(State4)}. -spec get_fun_types(cerl:c_module(), dialyzer_plt:plt(), - dialyzer_callgraph:callgraph(), dict()) -> - {dict(), dict(), [label()], [string()]}. + dialyzer_callgraph:callgraph(), dict()) -> dict(). get_fun_types(Tree, Plt, Callgraph, Records) -> State = analyze_module(Tree, Plt, Callgraph, Records, false), - Callgraph1 = State#state.callgraph, - {state__all_fun_types(State), - dialyzer_callgraph:get_race_code(Callgraph1), - dialyzer_callgraph:get_public_tables(Callgraph1), - dialyzer_callgraph:get_named_tables(Callgraph1)}. - -%%-------------------------------------------------------------------- - --spec pp(file:filename()) -> 'ok'. - -pp(File) -> - {ok, Code} = dialyzer_utils:get_core_from_src(File, [no_copt]), - Plt = get_def_plt(), - AnnTree = annotate_module(Code, Plt), - io:put_chars(cerl_prettypr:format(AnnTree, [{hook, cerl_typean:pp_hook()}])), - io:nl(). - -%%-------------------------------------------------------------------- -%% This is used in the testsuite. - --spec get_top_level_signatures(cerl:c_module(), dict()) -> - [{{atom(), arity()}, erl_types:erl_type()}]. - -get_top_level_signatures(Code, Records) -> - {Tree, _} = cerl_trees:label(cerl:from_records(Code)), - Callgraph0 = dialyzer_callgraph:new(), - Callgraph1 = dialyzer_callgraph:scan_core_tree(Tree, Callgraph0), - {Callgraph2, _} = dialyzer_callgraph:remove_external(Callgraph1), - Callgraph = dialyzer_callgraph:finalize(Callgraph2), - to_dot(Callgraph), - Plt = get_def_plt(), - FunTypes = get_fun_types(Tree, Plt, Callgraph, Records), - FunTypes1 = lists:foldl(fun({V, F}, Acc) -> - Label = get_label(F), - case dict:find(Label, Acc) of - error -> - Arity = cerl:fname_arity(V), - Type = t_fun(lists:duplicate(Arity, - t_none()), - t_none()), - dict:store(Label, Type, Acc); - {ok, _} -> Acc - end - end, FunTypes, cerl:module_defs(Tree)), - dialyzer_callgraph:delete(Callgraph), - Sigs = [{{cerl:fname_id(V), cerl:fname_arity(V)}, - dict:fetch(get_label(F), FunTypes1)} - || {V, F} <- cerl:module_defs(Tree)], - ordsets:from_list(Sigs). - -get_def_plt() -> - try - dialyzer_plt:from_file(dialyzer_plt:get_default_plt()) - catch - throw:{dialyzer_error, _} -> dialyzer_plt:new() - end. - -%%% =========================================================================== -%%% -%%% Annotate all top level funs. -%%% -%%% =========================================================================== - -annotate_module(Code, Plt) -> - {Tree, _} = cerl_trees:label(cerl:from_records(Code)), - Callgraph0 = dialyzer_callgraph:new(), - Callgraph1 = dialyzer_callgraph:scan_core_tree(Tree, Callgraph0), - {Callgraph2, _} = dialyzer_callgraph:remove_external(Callgraph1), - Callgraph = dialyzer_callgraph:finalize(Callgraph2), - State = analyze_module(Tree, Plt, Callgraph), - Res = annotate(Tree, State), - dialyzer_callgraph:delete(Callgraph), - Res. - -annotate(Tree, State) -> - case cerl:subtrees(Tree) of - [] -> set_type(Tree, State); - List -> - NewSubTrees = [[annotate(Subtree, State) || Subtree <- Group] - || Group <- List], - NewTree = cerl:update_tree(Tree, NewSubTrees), - set_type(NewTree, State) - end. - -set_type(Tree, State) -> - case cerl:type(Tree) of - 'fun' -> - Type = state__fun_type(Tree, State), - case t_is_any(Type) of - true -> - cerl:set_ann(Tree, delete_ann(typesig, cerl:get_ann(Tree))); - false -> - cerl:set_ann(Tree, append_ann(typesig, Type, cerl:get_ann(Tree))) - end; - apply -> - case state__find_apply_return(Tree, State) of - unknown -> Tree; - ReturnType -> - case t_is_any(ReturnType) of - true -> - cerl:set_ann(Tree, delete_ann(type, cerl:get_ann(Tree))); - false -> - cerl:set_ann(Tree, append_ann(type, ReturnType, - cerl:get_ann(Tree))) - end - end; - _ -> - Tree - end. - -append_ann(Tag, Val, [X | Xs]) -> - if tuple_size(X) >= 1, element(1, X) =:= Tag -> - append_ann(Tag, Val, Xs); - true -> - [X | append_ann(Tag, Val, Xs)] - end; -append_ann(Tag, Val, []) -> - [{Tag, Val}]. - -delete_ann(Tag, [X | Xs]) -> - if tuple_size(X) >= 1, element(1, X) =:= Tag -> - delete_ann(Tag, Xs); - true -> - [X | delete_ann(Tag, Xs)] - end; -delete_ann(_, []) -> - []. + state__all_fun_types(State). %%% =========================================================================== %%% @@ -265,56 +132,46 @@ delete_ann(_, []) -> %%% %%% =========================================================================== -analyze_module(Tree, Plt, Callgraph) -> - analyze_module(Tree, Plt, Callgraph, dict:new(), false). - analyze_module(Tree, Plt, Callgraph, Records, GetWarnings) -> debug_pp(Tree, false), Module = cerl:atom_val(cerl:module_name(Tree)), RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceCode = dialyzer_callgraph:get_race_code(Callgraph), BehaviourTranslations = case RaceDetection of true -> dialyzer_behaviours:translatable_behaviours(Tree); false -> [] end, TopFun = cerl:ann_c_fun([{label, top}], [], Tree), - State = state__new(dialyzer_callgraph:race_code_new(Callgraph), - TopFun, Plt, Module, Records, BehaviourTranslations), + State = + state__new(Callgraph, TopFun, Plt, Module, Records, BehaviourTranslations), State1 = state__race_analysis(not GetWarnings, State), State2 = analyze_loop(State1), case GetWarnings of true -> State3 = state__set_warning_mode(State2), State4 = analyze_loop(State3), - State5 = state__restore_race_code(RaceCode, State4), %% EXPERIMENTAL: Turn all behaviour API calls into calls to the %% respective callback module's functions. case BehaviourTranslations of - [] -> dialyzer_races:race(State5); + [] -> dialyzer_races:race(State4); Behaviours -> - Callgraph2 = State5#state.callgraph, - Digraph = dialyzer_callgraph:get_digraph(Callgraph2), + Digraph = dialyzer_callgraph:get_digraph(State4#state.callgraph), TranslatedCallgraph = dialyzer_behaviours:translate_callgraph(Behaviours, Module, - Callgraph2), + Callgraph), St = - dialyzer_races:race(State5#state{callgraph = TranslatedCallgraph}), - Callgraph3 = dialyzer_callgraph:put_digraph(Digraph, - St#state.callgraph), - St#state{callgraph = Callgraph3} + dialyzer_races:race(State4#state{callgraph = TranslatedCallgraph}), + FinalCallgraph = dialyzer_callgraph:put_digraph(Digraph, + St#state.callgraph), + St#state{callgraph = FinalCallgraph} end; false -> - Callgraph1 = State2#state.callgraph, - RaceCode1 = dialyzer_callgraph:get_race_code(Callgraph1), - state__restore_race_code( - dict:merge(fun (_K, V1, _V2) -> V1 end, - RaceCode, RaceCode1), State2) + State2 end. -analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> +analyze_loop(State) -> case state__get_work(State) of none -> State; {Fun, NewState1} -> @@ -340,10 +197,9 @@ analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> Map1 = enter_type_lists(Vars, ArgTypes, Map), Body = cerl:fun_body(Fun), FunLabel = get_label(Fun), - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceAnalysis = dialyzer_races:get_race_analysis(Races), + IsRaceAnalysisEnabled = is_race_analysis_enabled(State), NewState3 = - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> NewState2 = state__renew_curr_fun( state__lookup_name(FunLabel, NewState1), FunLabel, @@ -357,17 +213,8 @@ analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> [state__lookup_name(get_label(Fun), State), t_to_string(t_fun(ArgTypes, BodyType))]), NewState5 = - case RaceDetection andalso RaceAnalysis of - true -> - Races1 = NewState4#state.races, - Code = lists:reverse(dialyzer_races:get_race_list(Races1)), - Callgraph1 = - renew_code(dialyzer_races:get_curr_fun(Races1), - dialyzer_races:get_curr_fun_args(Races1), - Code, - state__warning_mode(NewState4), - NewState4#state.callgraph), - NewState4#state{callgraph = Callgraph1}; + case IsRaceAnalysisEnabled of + true -> renew_race_code(NewState4); false -> NewState4 end, NewState6 = @@ -582,9 +429,7 @@ handle_apply_or_call([{local, external}|Left], Args, ArgTypes, Map, Tree, State, ArgTypes, t_any()); handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left], Args, ArgTypes, Map, Tree, - #state{callgraph = Callgraph, races = Races, - opaques = Opaques} = State, - AccArgTypes, AccRet) -> + #state{opaques = Opaques} = State, AccArgTypes, AccRet) -> Any = t_any(), AnyArgs = [Any || _ <- Args], GenSig = {AnyArgs, fun(_) -> t_any() end}, @@ -680,8 +525,7 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left], ?debug("ContrRet: ~s\n", [erl_types:t_to_string(CRange(TmpArgTypes))]), ?debug("SigRet: ~s\n", [erl_types:t_to_string(SigRange)]), State1 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> Ann = cerl:get_ann(Tree), File = get_file(Ann), @@ -1047,20 +891,17 @@ handle_call(Tree, Map, State) -> %%---------------------------------------- -handle_case(Tree, Map, #state{callgraph = Callgraph} = State) -> +handle_case(Tree, Map, State) -> Arg = cerl:case_arg(Tree), Clauses = filter_match_fail(cerl:case_clauses(Tree)), {State1, Map1, ArgType} = SMA = traverse(Arg, Map, State), case t_is_none_or_unit(ArgType) of true -> SMA; false -> - Races = State1#state.races, State2 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State1), state__renew_race_list([beg_case|RaceList], RaceListSize + 1, State1); false -> State1 @@ -1094,9 +935,8 @@ handle_cons(Tree, Map, State) -> %%---------------------------------------- -handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> - RaceAnalysis = dialyzer_races:get_race_analysis(Races), - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), +handle_let(Tree, Map, State) -> + IsRaceAnalysisEnabled = is_race_analysis_enabled(State), Arg = cerl:let_arg(Tree), Vars = cerl:let_vars(Tree), {Map0, State0} = @@ -1104,10 +944,9 @@ handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> true -> [Var] = Vars, {enter_subst(Var, Arg, Map), - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list( [dialyzer_races:let_tag_new(Var, Arg)|RaceList], RaceListSize + 1, State); @@ -1117,9 +956,8 @@ handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> end, Body = cerl:let_body(Tree), {State1, Map1, ArgTypes} = SMA = traverse(Arg, Map0, State0), - Callgraph1 = State1#state.callgraph, - Callgraph2 = - case RaceDetection andalso RaceAnalysis andalso cerl:is_c_call(Arg) of + State2 = + case IsRaceAnalysisEnabled andalso cerl:is_c_call(Arg) of true -> Mod = cerl:call_module(Arg), Name = cerl:call_name(Arg), @@ -1127,16 +965,11 @@ handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> cerl:concrete(Mod) =:= ets andalso cerl:is_literal(Name) andalso cerl:concrete(Name) =:= new of - true -> - NewTable = dialyzer_races:get_new_table(State1#state.races), - renew_public_tables(Vars, NewTable, - state__warning_mode(State1), - Callgraph1); - false -> Callgraph1 + true -> renew_race_public_tables(Vars, State1); + false -> State1 end; - false -> Callgraph1 + false -> State1 end, - State2 = State1#state{callgraph = Callgraph2}, case t_is_none_or_unit(ArgTypes) of true -> SMA; false -> @@ -1167,16 +1000,13 @@ handle_module(Tree, Map, State) -> %%---------------------------------------- -handle_receive(Tree, Map, - #state{callgraph = Callgraph, races = Races} = State) -> +handle_receive(Tree, Map, State) -> Clauses = filter_match_fail(cerl:receive_clauses(Tree)), Timeout = cerl:receive_timeout(Tree), State1 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list([beg_case|RaceList], RaceListSize + 1, State); false -> State @@ -1299,16 +1129,13 @@ handle_tuple(Tree, Map, State) -> %%---------------------------------------- %% Clauses %% -handle_clauses([C|Left], Arg, ArgType, OrigArgType, - #state{callgraph = Callgraph, races = Races} = State, - CaseTypes, MapIn, Acc, ClauseAcc) -> - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceAnalysis = dialyzer_races:get_race_analysis(Races), +handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn, + Acc, ClauseAcc) -> + IsRaceAnalysisEnabled = is_race_analysis_enabled(State), State1 = - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list( [dialyzer_races:beg_clause_new(Arg, cerl:clause_pats(C), cerl:clause_guard(C))| @@ -1319,11 +1146,9 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, {State2, ClauseMap, BodyType, NewArgType} = do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1), {NewClauseAcc, State3} = - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> - Races1 = State2#state.races, - RaceList1 = dialyzer_races:get_race_list(Races1), - RaceListSize1 = dialyzer_races:get_race_list_size(Races1), + {RaceList1, RaceListSize1} = get_race_list_and_size(State2), EndClause = dialyzer_races:end_clause_new(Arg, cerl:clause_pats(C), cerl:clause_guard(C)), {[EndClause|ClauseAcc], @@ -1338,30 +1163,25 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, end, handle_clauses(Left, Arg, NewArgType, OrigArgType, State3, NewCaseTypes, MapIn, NewAcc, NewClauseAcc); -handle_clauses([], _Arg, _ArgType, _OrigArgType, - #state{callgraph = Callgraph, races = Races} = State, - CaseTypes, _MapIn, Acc, ClauseAcc) -> +handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc, + ClauseAcc) -> State1 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list( - [dialyzer_races:end_case_new(ClauseAcc)| - dialyzer_races:get_race_list(Races)], - dialyzer_races:get_race_list_size(Races) + 1, State); + [dialyzer_races:end_case_new(ClauseAcc)|RaceList], + RaceListSize + 1, State); false -> State end, {lists:reverse(Acc), State1, t_sup(CaseTypes)}. -do_clause(C, Arg, ArgType0, OrigArgType, Map, - #state{callgraph = Callgraph, races = Races} = State) -> +do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> Pats = cerl:clause_pats(C), Guard = cerl:clause_guard(C), Body = cerl:clause_body(C), - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceAnalysis = dialyzer_races:get_race_analysis(Races), State1 = - case RaceDetection andalso RaceAnalysis of + case is_race_analysis_enabled(State) of true -> state__renew_fun_args(Pats, State); false -> State @@ -2648,14 +2468,18 @@ join_maps(Maps, MapOut) -> Keys = ordsets:from_list(dict:fetch_keys(Dict) ++ dict:fetch_keys(Subst)), join_maps(Keys, Maps, MapOut). -join_maps([Key|Left], Maps, MapOut) -> +join_maps(Keys, Maps, MapOut) -> + KTs = join_maps_collect(Keys, Maps, MapOut), + lists:foldl(fun({K, T}, M) -> enter_type(K, T, M) end, MapOut, KTs). + +join_maps_collect([Key|Left], Maps, MapOut) -> Type = join_maps_one_key(Maps, Key, t_none()), case t_is_equal(lookup_type(Key, MapOut), Type) of - true -> join_maps(Left, Maps, MapOut); - false -> join_maps(Left, Maps, enter_type(Key, Type, MapOut)) + true -> join_maps_collect(Left, Maps, MapOut); + false -> [{Key, Type} | join_maps_collect(Left, Maps, MapOut)] end; -join_maps([], _Maps, MapOut) -> - MapOut. +join_maps_collect([], _Maps, _MapOut) -> + []. join_maps_one_key([Map|Left], Key, AccType) -> case t_is_any(AccType) of @@ -2914,10 +2738,6 @@ state__set_warning_mode(#state{tree_map = TreeMap, fun_tab = FunTab, fun_tab = FunTab, warning_mode = true, races = dialyzer_races:put_race_analysis(true, Races)}. -state__restore_race_code(RaceCode, #state{callgraph = Callgraph} = State) -> - State#state{callgraph = dialyzer_callgraph:put_race_code(RaceCode, - Callgraph)}. - state__race_analysis(Analysis, #state{races = Races} = State) -> State#state{races = dialyzer_races:put_race_analysis(Analysis, Races)}. @@ -3260,21 +3080,6 @@ state__fun_info(Fun, #state{callgraph = CG, fun_tab = FunTab, plt = PLT}) -> ?debug("LocalRet: ~s\n", [t_to_string(LocalRet)]), {Fun, Sig, Contract, LocalRet}. -state__find_apply_return(Tree, #state{callgraph = Callgraph} = State) -> - Apply = get_label(Tree), - case dialyzer_callgraph:lookup_call_site(Apply, Callgraph) of - error -> - unknown; - {ok, List} -> - case lists:member(external, List) of - true -> t_any(); - false -> - FunTypes = [state__fun_type(F, State) || F <- List], - Returns = [t_fun_range(F) || F <- FunTypes], - t_sup(Returns) - end - end. - forward_args(Fun, ArgTypes, #state{work = Work, fun_tab = FunTab} = State) -> {OldArgTypes, OldOut, Fixpoint} = case dict:find(Fun, FunTab) of @@ -3305,6 +3110,16 @@ state__cleanup(#state{callgraph = Callgraph, races = dialyzer_races:cleanup(Races), records = Records}. +-spec state__duplicate(state()) -> state(). + +state__duplicate(#state{callgraph = Callgraph} = State) -> + State#state{callgraph = dialyzer_callgraph:duplicate(Callgraph)}. + +-spec dispose_state(state()) -> ok. + +dispose_state(#state{callgraph = Callgraph}) -> + dialyzer_callgraph:dispose_race_server(Callgraph). + -spec state__get_callgraph(state()) -> dialyzer_callgraph:callgraph(). state__get_callgraph(#state{callgraph = Callgraph}) -> @@ -3342,26 +3157,36 @@ state__records_only(#state{records = Records}) -> %%% %%% =========================================================================== -renew_code(Fun, FunArgs, Code, WarningMode, Callgraph) -> +is_race_analysis_enabled(#state{races = Races, callgraph = Callgraph}) -> + RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), + RaceAnalysis = dialyzer_races:get_race_analysis(Races), + RaceDetection andalso RaceAnalysis. + +get_race_list_and_size(#state{races = Races}) -> + dialyzer_races:get_race_list_and_size(Races). + +renew_race_code(#state{races = Races, callgraph = Callgraph, + warning_mode = WarningMode} = State) -> case WarningMode of - true -> Callgraph; + true -> State; false -> - RaceCode = dialyzer_callgraph:get_race_code(Callgraph), - dialyzer_callgraph:put_race_code( - dict:store(Fun, [FunArgs, Code], RaceCode), Callgraph) + NewCallgraph = dialyzer_callgraph:renew_race_code(Races, Callgraph), + State#state{callgraph = NewCallgraph} end. -renew_public_tables([Var], Table, WarningMode, Callgraph) -> +renew_race_public_tables([Var], #state{races = Races, callgraph = Callgraph, + warning_mode = WarningMode} = State) -> case WarningMode of - true -> Callgraph; + true -> State; false -> + Table = dialyzer_races:get_new_table(Races), case Table of - no_t -> Callgraph; - _Other -> - VarLabel = get_label(Var), - PTables = dialyzer_callgraph:get_public_tables(Callgraph), - dialyzer_callgraph:put_public_tables( - lists:usort([VarLabel|PTables]), Callgraph) + no_t -> State; + _Other -> + VarLabel = get_label(Var), + NewCallgraph = + dialyzer_callgraph:renew_race_public_tables(VarLabel, Callgraph), + State#state{callgraph = NewCallgraph} end end. @@ -3688,17 +3513,3 @@ strip_annotations(Tree) -> debug_pp(_Tree, _UseHook) -> ok. -endif. - -%%---------------------------------------------------------------------------- - --spec to_dot(dialyzer_callgraph:callgraph()) -> 'ok'. - --ifdef(DOT). -to_dot(CG) -> - dialyzer_callgraph:to_dot(CG). --else. -to_dot(_CG) -> - ok. --endif. - -%%---------------------------------------------------------------------------- diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl index b26aa695bd..c6f7c56227 100644 --- a/lib/dialyzer/src/dialyzer_gui_wx.erl +++ b/lib/dialyzer/src/dialyzer_gui_wx.erl @@ -192,48 +192,50 @@ create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) -> RunButtons = wxBoxSizer:new(?wxHORIZONTAL), Buttons = wxFlexGridSizer:new(3), - wxSizer:add(ChooseButtons, DeleteButton, ?BorderOpt), - wxSizer:add(ChooseButtons, DeleteAllButton, ?BorderOpt), - wxSizer:add(ChooseItem, Lab1, Center), - wxSizer:add(ChooseItem, ChosenBox, Opts), - wxSizer:add(ChooseItem, ChooseButtons, ?BorderOpt), - wxSizer:add(FileTypeItem, OptionsLabel), - wxSizer:add(FileTypeItem, FileType, [{border, 5}, {flag, ?wxALL}]), - wxSizer:add(LogItem, LogLabel, Center), - wxSizer:add(LogItem, LogBox, Opts3), - wxSizer:add(LogItem, ClearLogButton, ?BorderOpt), - wxSizer:add(FileItem, FileLabel), - wxSizer:add(FileItem, FilePicker), - wxSizer:add(DirItem, DirLabel), - wxSizer:add(DirItem, DirPicker), - wxSizer:add(AddDirButtons, AddDirButton, ?BorderOpt), - wxSizer:add(AddDirButtons, AddRecButton, ?BorderOpt), - wxSizer:add(FileDirItem, FileItem), - wxSizer:add(FileDirItem, AddButton, ?BorderOpt), - wxSizer:add(FileDirItem, DirItem, ?BorderOpt), - wxSizer:add(FileDirItem, AddDirButtons, ?BorderOpt), - wxSizer:add(WarnButtons, ExplainWarnButton, ?BorderOpt), - wxSizer:add(WarnButtons, ClearWarningsButton, ?BorderOpt), - wxSizer:add(RunButtons, RunButton, ?BorderOpt), - wxSizer:add(RunButtons, StopButton, ?BorderOpt), - wxSizer:add(Buttons, WarnButtons), - wxSizer:add(Buttons, wxStaticText:new(Frame, ?LABEL7, ""), [{flag, ?wxEXPAND}]), - wxSizer:add(Buttons, RunButtons), - wxFlexGridSizer:addGrowableCol(Buttons, 1), - wxSizer:add(WarningsItem, WarningsLabel, Center), - wxSizer:add(WarningsItem, WarningsBox, Opts3), - wxSizer:add(WarningsItem, Buttons, [{flag, ?wxEXPAND bor ?wxALL},?Border]), - - wxSizer:add(Left, ChooseItem, Opts), - wxSizer:add(Left, FileDirItem, [{proportion, 1}, {border, 60}, {flag, ?wxTOP}]), - wxSizer:add(RightUp, FileTypeItem, ?BorderOpt), - wxSizer:add(RightUp, LogItem, Opts3), - wxSizer:add(Right, RightUp, Opts3), - wxSizer:add(Right, WarningsItem, Opts3), - wxSizer:add(Top, Left, Opts), - wxSizer:add(Top, Right, Opts3), - - wxSizer:add(All, Top, Opts), + _ = wxSizer:add(ChooseButtons, DeleteButton, ?BorderOpt), + _ = wxSizer:add(ChooseButtons, DeleteAllButton, ?BorderOpt), + _ = wxSizer:add(ChooseItem, Lab1, Center), + _ = wxSizer:add(ChooseItem, ChosenBox, Opts), + _ = wxSizer:add(ChooseItem, ChooseButtons, ?BorderOpt), + _ = wxSizer:add(FileTypeItem, OptionsLabel), + _ = wxSizer:add(FileTypeItem, FileType, [{border, 5}, {flag, ?wxALL}]), + _ = wxSizer:add(LogItem, LogLabel, Center), + _ = wxSizer:add(LogItem, LogBox, Opts3), + _ = wxSizer:add(LogItem, ClearLogButton, ?BorderOpt), + _ = wxSizer:add(FileItem, FileLabel), + _ = wxSizer:add(FileItem, FilePicker), + _ = wxSizer:add(DirItem, DirLabel), + _ = wxSizer:add(DirItem, DirPicker), + _ = wxSizer:add(AddDirButtons, AddDirButton, ?BorderOpt), + _ = wxSizer:add(AddDirButtons, AddRecButton, ?BorderOpt), + _ = wxSizer:add(FileDirItem, FileItem), + _ = wxSizer:add(FileDirItem, AddButton, ?BorderOpt), + _ = wxSizer:add(FileDirItem, DirItem, ?BorderOpt), + _ = wxSizer:add(FileDirItem, AddDirButtons, ?BorderOpt), + _ = wxSizer:add(WarnButtons, ExplainWarnButton, ?BorderOpt), + _ = wxSizer:add(WarnButtons, ClearWarningsButton, ?BorderOpt), + _ = wxSizer:add(RunButtons, RunButton, ?BorderOpt), + _ = wxSizer:add(RunButtons, StopButton, ?BorderOpt), + _ = wxSizer:add(Buttons, WarnButtons), + _ = wxSizer:add(Buttons, wxStaticText:new(Frame, ?LABEL7, ""), + [{flag, ?wxEXPAND}]), + _ = wxSizer:add(Buttons, RunButtons), + _ = wxFlexGridSizer:addGrowableCol(Buttons, 1), + _ = wxSizer:add(WarningsItem, WarningsLabel, Center), + _ = wxSizer:add(WarningsItem, WarningsBox, Opts3), + _ = wxSizer:add(WarningsItem, Buttons, + [{flag, ?wxEXPAND bor ?wxALL}, ?Border]), + _ = wxSizer:add(Left, ChooseItem, Opts), + _ = wxSizer:add(Left, FileDirItem, + [{proportion, 1}, {border, 60}, {flag, ?wxTOP}]), + _ = wxSizer:add(RightUp, FileTypeItem, ?BorderOpt), + _ = wxSizer:add(RightUp, LogItem, Opts3), + _ = wxSizer:add(Right, RightUp, Opts3), + _ = wxSizer:add(Right, WarningsItem, Opts3), + _ = wxSizer:add(Top, Left, Opts), + _ = wxSizer:add(Top, Right, Opts3), + + _ = wxSizer:add(All, Top, Opts), wxWindow:setSizer(Frame, All), wxWindow:setSizeHints(Frame, {1150,600}), wxWindow:show(Frame), @@ -294,91 +296,67 @@ create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) -> createFileMenu() -> FileMenu = wxMenu:new(), - wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_WARNINGS}, - {text, "Save &Warnings"}])), - wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_LOG}, - {text, "Save &Log"}])), - wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_QUIT}, - {text, "E&xit\tAlt-X"}])), + _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_WARNINGS}, + {text, "Save &Warnings"}])), + _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_LOG}, + {text, "Save &Log"}])), + _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_QUIT}, + {text, "E&xit\tAlt-X"}])), FileMenu. createWarningsMenu() -> WarningsMenu = wxMenu:new(), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_MATCH_FAILURES, - "Match failures"), - wxMenu:check(WarningsMenu, ?menuID_WARN_MATCH_FAILURES, true), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_FAIL_FUN_CALLS, - "Failing function calls"), - wxMenu:check(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS, true), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_BAD_FUN, - "Bad fun applications"), - wxMenu:check(WarningsMenu, ?menuID_WARN_BAD_FUN, true), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_OPAQUE, - "Opaqueness violations"), - wxMenu:check(WarningsMenu, ?menuID_WARN_OPAQUE, true), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_LIST_CONSTR, - "Improper list constructions"), - wxMenu:check(WarningsMenu, ?menuID_WARN_LIST_CONSTR, true), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_UNUSED_FUN, - "Unused functions"), - wxMenu:check(WarningsMenu, ?menuID_WARN_UNUSED_FUN, true), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_ERROR_HANDLING_FUN, - "Error handling functions"), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_NO_RETURN_FUN, - "Functions of no return"), - wxMenu:check(WarningsMenu, ?menuID_WARN_NO_RETURN_FUN, true), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_UNEXPORTED_FUN, - "Call to unexported function"), - wxMenu:check(WarningsMenu, ?menuID_WARN_UNEXPORTED_FUN, true), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_RACE_CONDITIONS, - "Possible race conditions"), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_WRONG_CONTRACTS, - "Wrong contracts"), - wxMenu:check(WarningsMenu, ?menuID_WARN_WRONG_CONTRACTS, true), - wxMenu:appendCheckItem(WarningsMenu, - ?menuID_WARN_CONTRACT_SYNTAX, - "Wrong contract syntax"), - wxMenu:check(WarningsMenu, ?menuID_WARN_CONTRACT_SYNTAX, true), + addCheckedItem(WarningsMenu, ?menuID_WARN_MATCH_FAILURES, "Match failures"), + addCheckedItem(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS, + "Failing function calls"), + addCheckedItem(WarningsMenu, ?menuID_WARN_BAD_FUN, "Bad fun applications"), + addCheckedItem(WarningsMenu, ?menuID_WARN_OPAQUE, "Opaqueness violations"), + addCheckedItem(WarningsMenu, ?menuID_WARN_LIST_CONSTR, + "Improper list constructions"), + addCheckedItem(WarningsMenu, ?menuID_WARN_UNUSED_FUN, "Unused functions"), + _ = wxMenu:appendCheckItem(WarningsMenu, ?menuID_WARN_ERROR_HANDLING_FUN, + "Error handling functions"), + addCheckedItem(WarningsMenu, ?menuID_WARN_NO_RETURN_FUN, + "Functions of no return"), + addCheckedItem(WarningsMenu, ?menuID_WARN_UNEXPORTED_FUN, + "Call to unexported function"), + _ = wxMenu:appendCheckItem(WarningsMenu, ?menuID_WARN_RACE_CONDITIONS, + "Possible race conditions"), + addCheckedItem(WarningsMenu, ?menuID_WARN_WRONG_CONTRACTS, "Wrong contracts"), + addCheckedItem(WarningsMenu, ?menuID_WARN_CONTRACT_SYNTAX, + "Wrong contract syntax"), WarningsMenu. +addCheckedItem(Menu, ItemId, Str) -> + _ = wxMenu:appendCheckItem(Menu, ItemId, Str), + wxMenu:check(Menu, ItemId, true). + createPltMenu() -> PltMenu = wxMenu:new(), - wxMenu:appendCheckItem(PltMenu, - ?menuID_PLT_INIT_EMPTY, - "Init with empty PLT"), - wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SHOW_CONTENTS}, - {text, "Show contents"}])), - wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SEARCH_CONTENTS}, - {text, "Search contents"}])), + _ = wxMenu:appendCheckItem(PltMenu, ?menuID_PLT_INIT_EMPTY, + "Init with empty PLT"), + _ = wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SHOW_CONTENTS}, + {text, "Show contents"}])), + _ = wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SEARCH_CONTENTS}, + {text, "Search contents"}])), PltMenu. createOptionsMenu() -> OptsMenu = wxMenu:new(), - wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_MACRO}, - {text, "Manage Macro Definitions"}])), - wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_INCLUDE_DIR}, - {text, "Manage Include Directories"}])), + _ = wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_MACRO}, + {text, "Manage Macro Definitions"}])), + _ = wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_INCLUDE_DIR}, + {text, "Manage Include Directories"}])), OptsMenu. createHelpMenu() -> HelpMenu = wxMenu:new(), - wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_MANUAL}, - {text, "Manual"}])), - wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_WARNING_OPTIONS}, - {text, "Warning Options"}])), - wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_ABOUT}, - {text, "About"}])), + _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_MANUAL}, + {text, "Manual"}])), + _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_WARNING_OPTIONS}, + {text, "Warning Options"}])), + _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_ABOUT}, + {text, "About"}])), HelpMenu. %% ---------------------------------------------------------------- @@ -583,20 +561,20 @@ search_doc_plt(#gui_state{gui = Wx} = State) -> ArLayout = wxBoxSizer:new(?wxVERTICAL), Buttons = wxBoxSizer:new(?wxHORIZONTAL), - wxSizer:add(ModLayout, ModLabel, ?BorderOpt), - wxSizer:add(ModLayout,ModText, ?BorderOpt), - wxSizer:add(FunLayout, FunLabel, ?BorderOpt), - wxSizer:add(FunLayout,FunText, ?BorderOpt), - wxSizer:add(ArLayout, ArLabel, ?BorderOpt), - wxSizer:add(ArLayout,ArText, ?BorderOpt), - wxSizer:add(Buttons, SearchButton, ?BorderOpt), - wxSizer:add(Buttons,Cancel, ?BorderOpt), - - wxSizer:add(Top, ModLayout), - wxSizer:add(Top, FunLayout), - wxSizer:add(Top, ArLayout), - wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]), - wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]), + _ = wxSizer:add(ModLayout, ModLabel, ?BorderOpt), + _ = wxSizer:add(ModLayout, ModText, ?BorderOpt), + _ = wxSizer:add(FunLayout, FunLabel, ?BorderOpt), + _ = wxSizer:add(FunLayout,FunText, ?BorderOpt), + _ = wxSizer:add(ArLayout, ArLabel, ?BorderOpt), + _ = wxSizer:add(ArLayout,ArText, ?BorderOpt), + _ = wxSizer:add(Buttons, SearchButton, ?BorderOpt), + _ = wxSizer:add(Buttons,Cancel, ?BorderOpt), + + _ = wxSizer:add(Top, ModLayout), + _ = wxSizer:add(Top, FunLayout), + _ = wxSizer:add(Top, ArLayout), + _ = wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]), + _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]), wxFrame:connect(Dialog, close_window), wxWindow:setSizer(Dialog, Layout), wxFrame:show(Dialog), @@ -654,14 +632,15 @@ error_sms(State, Message) -> output_sms(State, ?DIALYZER_ERROR_TITLE, Message, error). output_sms(#gui_state{frame = Frame}, Title, Message, Type) -> - case Type of - error -> - MessageWin = wxMessageDialog:new(Frame,Message,[{caption, Title},{style, ?wxOK bor ?wxICON_ERROR}]); - info -> - MessageWin = wxMessageDialog:new(Frame,Message,[{caption, Title},{style, ?wxOK bor ?wxICON_INFORMATION}]) - end, + Style = case Type of + error -> ?wxOK bor ?wxICON_ERROR; + info -> ?wxOK bor ?wxICON_INFORMATION + end, + Options = [{caption, Title}, {style, Style}], + MessageWin = wxMessageDialog:new(Frame, Message, Options), wxWindow:setSizeHints(MessageWin, {350,100}), - wxDialog:showModal(MessageWin). + wxDialog:showModal(MessageWin), + ok. free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) -> Contents = lists:flatten(Contents0), @@ -685,8 +664,9 @@ free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) -> wxButton:connect(Ok, command_button_clicked), Layout = wxBoxSizer:new(?wxVERTICAL), - wxSizer:add(Layout, Editor, ?BorderOpt), - wxSizer:add(Layout, Ok, [{flag, ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL}, ?Border]), + _ = wxSizer:add(Layout, Editor, ?BorderOpt), + Flag = ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL, + _ = wxSizer:add(Layout, Ok, [{flag, Flag}, ?Border]), wxWindow:setSizer(Win, Layout), wxWindow:show(Win), show_info_loop(Frame, Win). @@ -696,7 +676,7 @@ show_info_loop(Frame, Win) -> #wx{id = ?Message_Ok, event = #wxCommand{type = command_button_clicked}} -> wxWindow:destroy(Win); #wx{id = ?Message, event = #wxClose{type = close_window}} -> - wxWindow:destroy(Win); + wxWindow:destroy(Win); #wx{event = #wxClose{type = close_window}} -> wxWindow:destroy(Frame) end. @@ -921,13 +901,14 @@ save_file(#gui_state{frame = Frame, warnings_box = WBox, log = Log} = State, Typ {message, Message}, {style,?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]), case wxFileDialog:showModal(FileDialog) of - ?wxID_OK -> Path = wxFileDialog:getPath(FileDialog), - case wxTextCtrl:saveFile(Box,[{file,Path}]) of - true -> ok; - false -> error_sms(State,"Could not write to file:\n" ++ Path) - end; + ?wxID_OK -> + Path = wxFileDialog:getPath(FileDialog), + case wxTextCtrl:saveFile(Box,[{file,Path}]) of + true -> ok; + false -> error_sms(State, "Could not write to file:\n" ++ Path) + end; ?wxID_CANCEL -> wxWindow:destroy(FileDialog); - _ -> error_sms(State,"Could not write to file:\n") + _ -> error_sms(State, "Could not write to file:\n") end end. @@ -961,16 +942,16 @@ include_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) -> Buttons = wxBoxSizer:new(?wxHORIZONTAL), Buttons1 = wxBoxSizer:new(?wxHORIZONTAL), - wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), - wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), - wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), - wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), - wxSizer:add(Buttons, DeleteButton, ?BorderOpt), - wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt), - wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), - wxSizer:add(Buttons1, Ok, ?BorderOpt), - wxSizer:add(Buttons1,Cancel, ?BorderOpt), - wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]), + _ = wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), + _ = wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), + _ = wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), + _ = wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), + _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt), + _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt), + _ = wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), + _ = wxSizer:add(Buttons1, Ok, ?BorderOpt), + _ = wxSizer:add(Buttons1,Cancel, ?BorderOpt), + _ = wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]), wxFrame:connect(Dialog, close_window), wxWindow:setSizer(Dialog, Layout), @@ -1058,21 +1039,21 @@ macro_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) -> Buttons = wxBoxSizer:new(?wxHORIZONTAL), Buttons1 = wxBoxSizer:new(?wxHORIZONTAL), - wxSizer:add(MacroItem, MacroLabel, ?BorderOpt), - wxSizer:add(MacroItem, MacroText, ?BorderOpt), - wxSizer:add(TermItem, TermLabel, ?BorderOpt), - wxSizer:add(TermItem, TermText, ?BorderOpt), - wxSizer:add(Item, MacroItem), - wxSizer:add(Item, TermItem), - wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), - wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), - wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), - wxSizer:add(Buttons, DeleteButton, ?BorderOpt), - wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt), - wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), - wxSizer:add(Buttons1, Ok, ?BorderOpt), - wxSizer:add(Buttons1, Cancel, ?BorderOpt), - wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]), + _ = wxSizer:add(MacroItem, MacroLabel, ?BorderOpt), + _ = wxSizer:add(MacroItem, MacroText, ?BorderOpt), + _ = wxSizer:add(TermItem, TermLabel, ?BorderOpt), + _ = wxSizer:add(TermItem, TermText, ?BorderOpt), + _ = wxSizer:add(Item, MacroItem), + _ = wxSizer:add(Item, TermItem), + _ = wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), + _ = wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), + _ = wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), + _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt), + _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt), + _ = wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), + _ = wxSizer:add(Buttons1, Ok, ?BorderOpt), + _ = wxSizer:add(Buttons1, Cancel, ?BorderOpt), + _ = wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]), wxFrame:connect(Dialog, close_window), wxWindow:setSizer(Dialog, Layout), @@ -1216,13 +1197,14 @@ show_explanation(#gui_state{gui = Wx} = State, Explanation) -> wxButton:connect(Ok, command_button_clicked), Layout = wxBoxSizer:new(?wxVERTICAL), Buttons = wxBoxSizer:new(?wxHORIZONTAL), - wxSizer:add(Buttons, ExplButton, ?BorderOpt), - wxSizer:add(Buttons, Ok, ?BorderOpt), - wxSizer:add(Layout, Editor,[{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), - wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]), + _ = wxSizer:add(Buttons, ExplButton, ?BorderOpt), + _ = wxSizer:add(Buttons, Ok, ?BorderOpt), + _ = wxSizer:add(Layout, Editor, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), + _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]), wxWindow:setSizer(Win, Layout), wxWindow:show(Win), - show_explanation_loop(State#gui_state{explanation_box = Editor}, Win, Explanation) + NewState = State#gui_state{explanation_box = Editor}, + show_explanation_loop(NewState, Win, Explanation) end. show_explanation_loop(#gui_state{frame = Frame, expl_pid = ExplPid} = State, Win, Explanation) -> diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl index 866650a0b2..06672e595f 100644 --- a/lib/dialyzer/src/dialyzer_options.erl +++ b/lib/dialyzer/src/dialyzer_options.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-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 @@ -194,6 +194,11 @@ build_options([{OptionName, Value} = Term|Rest], Options) -> callgraph_file -> assert_filename(Value), build_options(Rest, Options#options{callgraph_file = Value}); + timing -> + build_options(Rest, Options#options{timing = Value}); + solvers -> + assert_solvers(Value), + build_options(Rest, Options#options{solvers = Value}); _ -> bad_option("Unknown dialyzer command line option", Term) end; @@ -255,6 +260,15 @@ is_plt_mode(plt_remove) -> true; is_plt_mode(plt_check) -> true; is_plt_mode(succ_typings) -> false. +assert_solvers([]) -> + ok; +assert_solvers([v1|Terms]) -> + assert_solvers(Terms); +assert_solvers([v2|Terms]) -> + assert_solvers(Terms); +assert_solvers([Term|_]) -> + bad_option("Illegal value for solver", Term). + -spec build_warnings([atom()], [dial_warning()]) -> [dial_warning()]. build_warnings([Opt|Opts], Warnings) -> diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index 06eaadad9c..5f64099210 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -55,7 +55,10 @@ plt_and_info_from_file/1, get_specs/1, get_specs/4, - to_file/4]). + to_file/4, + get_mini_plt/1, + restore_full_plt/2 + ]). %% Debug utilities -export([pp_non_returning/0, pp_mod/1]). @@ -82,7 +85,13 @@ contracts = table_new() :: dict(), callbacks = table_new() :: dict(), exported_types = sets:new() :: set()}). --opaque plt() :: #plt{}. + +-record(mini_plt, {info :: ets:tid(), + contracts :: ets:tid(), + callbacks :: ets:tid() + }). + +-opaque plt() :: #plt{} | #mini_plt{}. -include("dialyzer.hrl"). @@ -132,51 +141,48 @@ delete_list(#plt{info = Info, types = Types, -spec insert_contract_list(plt(), dialyzer_contracts:plt_contracts()) -> plt(). -insert_contract_list(#plt{contracts = Contracts} = PLT, List) -> - PLT#plt{contracts = table_insert_list(Contracts, List)}. +insert_contract_list(#mini_plt{contracts = Contracts} = PLT, List) -> + true = ets:insert(Contracts, List), + PLT. -spec insert_callbacks(plt(), dialyzer_codeserver:codeserver()) -> plt(). insert_callbacks(#plt{callbacks = Callbacks} = Plt, Codeserver) -> - FunPreferNew = fun(_Key, _Val1, Val2) -> Val2 end, - FunDictMerger = - fun(_Key, Value, AccIn) -> dict:merge(FunPreferNew, Value, AccIn) end, - MergedCallbacks = dict:fold(FunDictMerger, dict:new(), - dialyzer_codeserver:get_callbacks(Codeserver)), - List = dict:to_list(MergedCallbacks), + List = dialyzer_codeserver:get_callbacks(Codeserver), Plt#plt{callbacks = table_insert_list(Callbacks, List)}. -spec lookup_contract(plt(), mfa_patt()) -> 'none' | {'value', #contract{}}. -lookup_contract(#plt{contracts = Contracts}, +lookup_contract(#mini_plt{contracts = ETSContracts}, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> - table_lookup(Contracts, MFA). + ets_table_lookup(ETSContracts, MFA). -spec lookup_callbacks(plt(), module()) -> - [{mfa(), {{Filename::string(), Line::pos_integer()}, #contract{}}}]. + 'none' | {'value', [{mfa(), {{Filename::string(), + Line::pos_integer()}, + #contract{}}}]}. -lookup_callbacks(#plt{callbacks = Callbacks}, Mod) when is_atom(Mod) -> - FunModFilter = - fun({M, _F, _A}, _Val) -> M =:= Mod; - ( _Key, _Val) -> false - end, - ModCallbacks = dict:filter(FunModFilter, Callbacks), - dict:to_list(ModCallbacks). +lookup_callbacks(#mini_plt{callbacks = ETSCallbacks}, Mod) when is_atom(Mod) -> + ets_table_lookup(ETSCallbacks, Mod). -type ret_args_types() :: {erl_types:erl_type(), [erl_types:erl_type()]}. -spec insert_list(plt(), [{mfa() | integer(), ret_args_types()}]) -> plt(). -insert_list(#plt{info = Info} = PLT, List) -> - PLT#plt{info = table_insert_list(Info, List)}. +insert_list(#mini_plt{info = Info} = PLT, List) -> + true = ets:insert(Info, List), + PLT. -spec lookup(plt(), integer() | mfa_patt()) -> 'none' | {'value', ret_args_types()}. -lookup(#plt{info = Info}, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> - table_lookup(Info, MFA); -lookup(#plt{info = Info}, Label) when is_integer(Label) -> - table_lookup(Info, Label). +lookup(Plt, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> + lookup_1(Plt, MFA); +lookup(Plt, Label) when is_integer(Label) -> + lookup_1(Plt, Label). + +lookup_1(#mini_plt{info = Info}, MFAorLabel) -> + ets_table_lookup(Info, MFAorLabel). -spec insert_types(plt(), dict()) -> plt(). @@ -503,6 +509,34 @@ init_md5_list_1([], DiffList, Acc) -> init_md5_list_1(Md5List, [], Acc) -> {ok, lists:reverse(Acc, Md5List)}. +-spec get_mini_plt(plt()) -> plt(). + +get_mini_plt(#plt{info = Info, contracts = Contracts, callbacks = Callbacks}) -> + [ETSInfo, ETSContracts, ETSCallbacks] = + [ets:new(Name, [public]) || Name <- [plt_info, plt_contracts, plt_callbacks]], + CallbackList = dict:to_list(Callbacks), + CallbacksByModule = + [{M, [Cb || {{M1,_,_},_} = Cb <- CallbackList, M1 =:= M]} || + M <- lists:usort([M || {{M,_,_},_} <- CallbackList])], + [true, true] = + [ets:insert(ETS, dict:to_list(Data)) || + {ETS, Data} <- [{ETSInfo, Info}, {ETSContracts, Contracts}]], + true = ets:insert(ETSCallbacks, CallbacksByModule), + #mini_plt{info = ETSInfo, contracts = ETSContracts, callbacks = ETSCallbacks}; +get_mini_plt(undefined) -> + undefined. + +-spec restore_full_plt(plt(), plt()) -> plt(). + +restore_full_plt(#mini_plt{info = ETSInfo, contracts = ETSContracts}, Plt) -> + Info = dict:from_list(ets:tab2list(ETSInfo)), + Contracts = dict:from_list(ets:tab2list(ETSContracts)), + ets:delete(ETSContracts), + ets:delete(ETSInfo), + Plt#plt{info = Info, contracts = Contracts}; +restore_full_plt(undefined, undefined) -> + undefined. + %%--------------------------------------------------------------------------- %% Edoc @@ -595,6 +629,13 @@ table_lookup(Plt, Obj) -> {ok, Val} -> {value, Val} end. +ets_table_lookup(Plt, Obj) -> + try ets:lookup_element(Plt, Obj, 2) of + Val -> {value, Val} + catch + _:_ -> none + end. + table_lookup_module(Plt, Mod) -> List = dict:fold(fun(Key, Val, Acc) -> case Key of diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl index cc635b0eef..cdb9f25999 100644 --- a/lib/dialyzer/src/dialyzer_races.erl +++ b/lib/dialyzer/src/dialyzer_races.erl @@ -36,6 +36,7 @@ -export([beg_clause_new/3, cleanup/1, end_case_new/1, end_clause_new/3, get_curr_fun/1, get_curr_fun_args/1, get_new_table/1, get_race_analysis/1, get_race_list/1, get_race_list_size/1, + get_race_list_and_size/1, let_tag_new/2, new/0, put_curr_fun/3, put_fun_args/2, put_race_analysis/2, put_race_list/3]). @@ -346,6 +347,7 @@ fixup_race_list(RaceWarnTag, WarnVarArgs, State) -> DepList2 = fixup_race_list_helper(NewParents, Calls, CurrFun, WarnVarArgs, RaceWarnTag, NewState), + dialyzer_dataflow:dispose_state(CleanState), lists:usort(cleanup_dep_calls(DepList1 ++ DepList2)). fixup_race_list_helper(Parents, Calls, CurrFun, WarnVarArgs, RaceWarnTag, @@ -380,13 +382,15 @@ fixup_race_forward_pullout(CurrFun, CurrFunLabel, Calls, Code, RaceList, InitFun, WarnVarArgs, RaceWarnTag, RaceVarMap, FunDefVars, FunCallVars, FunArgTypes, NestingLevel, State) -> + TState = dialyzer_dataflow:state__duplicate(State), {DepList, NewCurrFun, NewCurrFunLabel, NewCalls, NewCode, NewRaceList, NewRaceVarMap, NewFunDefVars, NewFunCallVars, NewFunArgTypes, NewNestingLevel} = fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList, InitFun, WarnVarArgs, RaceWarnTag, RaceVarMap, FunDefVars, FunCallVars, FunArgTypes, NestingLevel, - cleanup_race_code(State)), + cleanup_race_code(TState)), + dialyzer_dataflow:dispose_state(TState), case NewCode of [] -> DepList; [#fun_call{caller = NewCurrFun, callee = Call, arg_types = FunTypes, @@ -2434,6 +2438,12 @@ get_race_list(#races{race_list = RaceList}) -> get_race_list_size(#races{race_list_size = RaceListSize}) -> RaceListSize. +-spec get_race_list_and_size(races()) -> {code(), non_neg_integer()}. + +get_race_list_and_size(#races{race_list = RaceList, + race_list_size = RaceListSize}) -> + {RaceList, RaceListSize}. + -spec let_tag_new(var_to_map1(), var_to_map1()) -> #let_tag{}. let_tag_new(Var, Arg) -> diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 5982603b7b..84379642bf 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -28,15 +28,22 @@ -module(dialyzer_succ_typings). -export([analyze_callgraph/3, - analyze_callgraph/4, - get_warnings/6]). + analyze_callgraph/6, + get_warnings/8 + ]). -%% These are only intended as debug functions. --export([doit/1, - get_top_level_signatures/3]). +-export([ + find_succ_types_for_scc/2, + refine_one_module/2, + find_required_by/2, + find_depends_on/2, + collect_warnings/2, + lookup_names/2 + ]). + +-export_type([typesig_init_data/0, dataflow_init_data/0, warnings_init_data/0]). %%-define(DEBUG, true). -%%-define(DEBUG_PP, true). -ifdef(DEBUG). -define(debug(X__, Y__), io:format(X__, Y__)). @@ -54,11 +61,21 @@ %% State record -- local to this module -type parent() :: 'none' | pid(). +-type typesig_init_data() :: term(). +-type dataflow_init_data() :: term(). +-type warnings_init_data() :: term(). + +-type fixpoint_init_data() :: typesig_init_data() | dataflow_init_data(). + +-type scc() :: [mfa_or_funlbl()] | [module()]. + -record(st, {callgraph :: dialyzer_callgraph:callgraph(), codeserver :: dialyzer_codeserver:codeserver(), no_warn_unused :: set(), parent = none :: parent(), + timing_server :: dialyzer_timing:timing_server(), + solvers :: [solver()], plt :: dialyzer_plt:plt()}). %%-------------------------------------------------------------------- @@ -68,60 +85,90 @@ dialyzer_plt:plt(). analyze_callgraph(Callgraph, Plt, Codeserver) -> - analyze_callgraph(Callgraph, Plt, Codeserver, none). + analyze_callgraph(Callgraph, Plt, Codeserver, none, [], none). -spec analyze_callgraph(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(), - dialyzer_codeserver:codeserver(), parent()) -> + dialyzer_codeserver:codeserver(), + dialyzer_timing:timing_server(), + [solver()], parent()) -> dialyzer_plt:plt(). -analyze_callgraph(Callgraph, Plt, Codeserver, Parent) -> - State = #st{callgraph = Callgraph, plt = Plt, - codeserver = Codeserver, parent = Parent}, - NewState = get_refined_success_typings(State), - NewState#st.plt. +analyze_callgraph(Callgraph, Plt, Codeserver, TimingServer, Solvers, Parent) -> + NewState = + init_state_and_get_success_typings(Callgraph, Plt, Codeserver, + TimingServer, Solvers, Parent), + dialyzer_plt:restore_full_plt(NewState#st.plt, Plt). %%-------------------------------------------------------------------- -get_refined_success_typings(State) -> - case find_succ_typings(State) of +init_state_and_get_success_typings(Callgraph, Plt, Codeserver, + TimingServer, Solvers, Parent) -> + {SCCs, Callgraph1} = + ?timing(TimingServer, "order", dialyzer_callgraph:finalize(Callgraph)), + State = #st{callgraph = Callgraph1, plt = dialyzer_plt:get_mini_plt(Plt), + codeserver = Codeserver, parent = Parent, + timing_server = TimingServer, solvers = Solvers}, + get_refined_success_typings(SCCs, State). + +get_refined_success_typings(SCCs, #st{callgraph = Callgraph, + timing_server = TimingServer} = State) -> + case find_succ_typings(SCCs, State) of {fixpoint, State1} -> State1; {not_fixpoint, NotFixpoint1, State1} -> - Callgraph = State1#st.callgraph, - NotFixpoint2 = [lookup_name(F, Callgraph) || F <- NotFixpoint1], - ModulePostorder = - dialyzer_callgraph:module_postorder_from_funs(NotFixpoint2, Callgraph), - case refine_succ_typings(ModulePostorder, State1) of + {ModulePostorder, ModCallgraph} = + ?timing( + TimingServer, "order", _C1, + dialyzer_callgraph:module_postorder_from_funs(NotFixpoint1, + Callgraph)), + ModState = State1#st{callgraph = ModCallgraph}, + case refine_succ_typings(ModulePostorder, ModState) of {fixpoint, State2} -> State2; - {not_fixpoint, NotFixpoint3, State2} -> - Callgraph1 = State2#st.callgraph, + {not_fixpoint, NotFixpoint2, State2} -> %% Need to reset the callgraph. - NotFixpoint4 = [lookup_name(F, Callgraph1) || F <- NotFixpoint3], - Callgraph2 = dialyzer_callgraph:reset_from_funs(NotFixpoint4, - Callgraph1), - get_refined_success_typings(State2#st{callgraph = Callgraph2}) + {NewSCCs, Callgraph2} = + ?timing(TimingServer, "order", _C2, + dialyzer_callgraph:reset_from_funs(NotFixpoint2, + ModCallgraph)), + NewState = State2#st{callgraph = Callgraph2}, + get_refined_success_typings(NewSCCs, NewState) end end. -type doc_plt() :: 'undefined' | dialyzer_plt:plt(). -spec get_warnings(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(), doc_plt(), dialyzer_codeserver:codeserver(), set(), - pid()) -> + dialyzer_timing:timing_server(), [solver()], pid()) -> {[dial_warning()], dialyzer_plt:plt(), doc_plt()}. -get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) -> - InitState = #st{callgraph = Callgraph, codeserver = Codeserver, - no_warn_unused = NoWarnUnused, parent = Parent, plt = Plt}, - NewState = get_refined_success_typings(InitState), +get_warnings(Callgraph, Plt, DocPlt, Codeserver, + NoWarnUnused, TimingServer, Solvers, Parent) -> + InitState = + init_state_and_get_success_typings(Callgraph, Plt, Codeserver, + TimingServer, Solvers, Parent), + NewState = InitState#st{no_warn_unused = NoWarnUnused}, Mods = dialyzer_callgraph:modules(NewState#st.callgraph), - CWarns = dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver, - NewState#st.plt), - get_warnings_from_modules(Mods, NewState, DocPlt, CWarns). - -get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) -> - send_log(State#st.parent, io_lib:format("Getting warnings for ~w\n", [M])), + MiniPlt = NewState#st.plt, + CWarns = + dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver, MiniPlt), + MiniDocPlt = dialyzer_plt:get_mini_plt(DocPlt), + ModWarns = + ?timing(TimingServer, "warning", + get_warnings_from_modules(Mods, NewState, MiniDocPlt)), + {postprocess_warnings(CWarns ++ ModWarns, Codeserver), + dialyzer_plt:restore_full_plt(MiniPlt, Plt), + dialyzer_plt:restore_full_plt(MiniDocPlt, DocPlt)}. + +get_warnings_from_modules(Mods, State, DocPlt) -> #st{callgraph = Callgraph, codeserver = Codeserver, - no_warn_unused = NoWarnUnused, plt = Plt} = State, + no_warn_unused = NoWarnUnused, plt = Plt, + timing_server = TimingServer} = State, + Init = {Codeserver, Callgraph, NoWarnUnused, Plt, DocPlt}, + dialyzer_coordinator:parallel_job(warnings, Mods, Init, TimingServer). + +-spec collect_warnings(module(), warnings_init_data()) -> [dial_warning()]. + +collect_warnings(M, {Codeserver, Callgraph, NoWarnUnused, Plt, DocPlt}) -> ModCode = dialyzer_codeserver:lookup_mod_code(M, Codeserver), Records = dialyzer_codeserver:lookup_mod_records(M, Codeserver), Contracts = dialyzer_codeserver:lookup_mod_contracts(M, Codeserver), @@ -129,28 +176,27 @@ get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) -> %% Check if there are contracts for functions that do not exist Warnings1 = dialyzer_contracts:contracts_without_fun(Contracts, AllFuns, Callgraph), - {RawWarnings2, FunTypes, RaceCode, PublicTables, NamedTables} = - dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, Records, NoWarnUnused), - {NewAcc, Warnings2} = postprocess_dataflow_warns(RawWarnings2, State, Acc), + {Warnings2, FunTypes} = + dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, + Records, NoWarnUnused), Attrs = cerl:module_attrs(ModCode), - Warnings3 = dialyzer_behaviours:check_callbacks(M, Attrs, Plt, Codeserver), - NewDocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt), - NewCallgraph = - dialyzer_callgraph:renew_race_info(Callgraph, RaceCode, PublicTables, - NamedTables), - State1 = st__renew_state_calls(NewCallgraph, State), - get_warnings_from_modules(Ms, State1, NewDocPlt, - [Warnings1, Warnings2, Warnings3|NewAcc]); -get_warnings_from_modules([], #st{plt = Plt}, DocPlt, Acc) -> - {lists:flatten(Acc), Plt, DocPlt}. - -postprocess_dataflow_warns(RawWarnings, State, WarnAcc) -> - postprocess_dataflow_warns(RawWarnings, State, WarnAcc, []). - -postprocess_dataflow_warns([], _State, WAcc, Acc) -> - {WAcc, lists:reverse(Acc)}; + Warnings3 = + dialyzer_behaviours:check_callbacks(M, Attrs, Records, Plt, Codeserver), + DocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt), + lists:flatten([Warnings1, Warnings2, Warnings3]). + +postprocess_warnings(RawWarnings, Codeserver) -> + Pred = + fun({?WARN_CONTRACT_RANGE, _, _}) -> true; + (_) -> false + end, + {CRWarns, NonCRWarns} = lists:partition(Pred, RawWarnings), + postprocess_dataflow_warns(CRWarns, Codeserver, NonCRWarns, []). + +postprocess_dataflow_warns([], _Callgraph, WAcc, Acc) -> + lists:reverse(Acc, WAcc); postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest], - #st{codeserver = Codeserver} = State, WAcc, Acc) -> + Codeserver, WAcc, Acc) -> {contract_range, [Contract, M, F, A, ArgStrings, CRet]} = Msg, case dialyzer_codeserver:lookup_mfa_contract({M,F,A}, Codeserver) of {ok, {{ContrF, _ContrL} = FileLine, _C}} -> @@ -163,87 +209,66 @@ postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest], (_) -> true end, FilterWAcc = lists:filter(Filter, WAcc), - postprocess_dataflow_warns(Rest, State, FilterWAcc, [W|Acc]); + postprocess_dataflow_warns(Rest, Codeserver, FilterWAcc, [W|Acc]); false -> - postprocess_dataflow_warns(Rest, State, WAcc, Acc) + postprocess_dataflow_warns(Rest, Codeserver, WAcc, Acc) end; error -> %% The contract is not in a module that is currently under analysis. %% We display the warning in the file/line of the call. NewMsg = {contract_range, [Contract, M, F, ArgStrings, CallL, CRet]}, W = {?WARN_CONTRACT_RANGE, {CallF, CallL}, NewMsg}, - postprocess_dataflow_warns(Rest, State, WAcc, [W|Acc]) - end; -postprocess_dataflow_warns([W|Rest], State, Wacc, Acc) -> - postprocess_dataflow_warns(Rest, State, Wacc, [W|Acc]). + postprocess_dataflow_warns(Rest, Codeserver, WAcc, [W|Acc]) + end. -refine_succ_typings(ModulePostorder, State) -> - ?debug("Module postorder: ~p\n", [ModulePostorder]), - refine_succ_typings(ModulePostorder, State, []). - -refine_succ_typings([SCC|SCCs], State, Fixpoint) -> - Msg = io_lib:format("Dataflow of one SCC: ~w\n", [SCC]), - send_log(State#st.parent, Msg), - ?debug("~s\n", [Msg]), - {NewState, FixpointFromScc} = - case SCC of - [M] -> refine_one_module(M, State); - [_|_] -> refine_one_scc(SCC, State) - end, - NewFixpoint = ordsets:union(Fixpoint, FixpointFromScc), - refine_succ_typings(SCCs, NewState, NewFixpoint); -refine_succ_typings([], State, Fixpoint) -> - case Fixpoint =:= [] of +refine_succ_typings(Modules, #st{codeserver = Codeserver, + callgraph = Callgraph, + plt = Plt, + timing_server = Timing, + solvers = Solvers} = State) -> + ?debug("Module postorder: ~p\n", [Modules]), + Init = {Codeserver, Callgraph, Plt, Solvers}, + NotFixpoint = + ?timing(Timing, "refine", + dialyzer_coordinator:parallel_job(dataflow, Modules, Init, Timing)), + ?debug("==================== Dataflow done ====================\n\n", []), + case NotFixpoint =:= [] of true -> {fixpoint, State}; - false -> {not_fixpoint, Fixpoint, State} + false -> {not_fixpoint, NotFixpoint, State} end. --spec refine_one_module(module(), #st{}) -> {#st{}, [label()]}. % ordset +-spec find_depends_on(scc() | module(), fixpoint_init_data()) -> [scc()]. -refine_one_module(M, State) -> - #st{callgraph = Callgraph, codeserver = CodeServer, plt = PLT} = State, +find_depends_on(SCC, {_Codeserver, Callgraph, _Plt, _Solvers}) -> + dialyzer_callgraph:get_depends_on(SCC, Callgraph). + +-spec find_required_by(scc() | module(), fixpoint_init_data()) -> [scc()]. + +find_required_by(SCC, {_Codeserver, Callgraph, _Plt, _Solvers}) -> + dialyzer_callgraph:get_required_by(SCC, Callgraph). + +-spec lookup_names([label()], fixpoint_init_data()) -> [mfa_or_funlbl()]. + +lookup_names(Labels, {_Codeserver, Callgraph, _Plt, _Solvers}) -> + [lookup_name(F, Callgraph) || F <- Labels]. + +-spec refine_one_module(module(), dataflow_init_data()) -> [label()]. % ordset + +refine_one_module(M, {CodeServer, Callgraph, Plt, _Solvers}) -> ModCode = dialyzer_codeserver:lookup_mod_code(M, CodeServer), AllFuns = collect_fun_info([ModCode]), - FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, PLT), Records = dialyzer_codeserver:lookup_mod_records(M, CodeServer), - {NewFunTypes, RaceCode, PublicTables, NamedTables} = - dialyzer_dataflow:get_fun_types(ModCode, PLT, Callgraph, Records), - NewCallgraph = - dialyzer_callgraph:renew_race_info(Callgraph, RaceCode, PublicTables, - NamedTables), + FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt), + NewFunTypes = + dialyzer_dataflow:get_fun_types(ModCode, Plt, Callgraph, Records), case reached_fixpoint(FunTypes, NewFunTypes) of - true -> - State1 = st__renew_state_calls(NewCallgraph, State), - {State1, ordsets:new()}; + true -> []; {false, NotFixpoint} -> ?debug("Not fixpoint\n", []), - NewState = insert_into_plt(dict:from_list(NotFixpoint), State), - NewState1 = st__renew_state_calls(NewCallgraph, NewState), - {NewState1, ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint])} + Plt = insert_into_plt(dict:from_list(NotFixpoint), Callgraph, Plt), + [FunLbl || {FunLbl,_Type} <- NotFixpoint] end. -st__renew_state_calls(Callgraph, State) -> - State#st{callgraph = Callgraph}. - -refine_one_scc(SCC, State) -> - refine_one_scc(SCC, State, []). - -refine_one_scc(SCC, State, AccFixpoint) -> - {NewState, FixpointFromScc} = refine_mods_in_scc(SCC, State, []), - case FixpointFromScc =:= [] of - true -> {NewState, AccFixpoint}; - false -> - NewAccFixpoint = ordsets:union(AccFixpoint, FixpointFromScc), - refine_one_scc(SCC, NewState, NewAccFixpoint) - end. - -refine_mods_in_scc([Mod|Mods], State, Fixpoint) -> - {NewState, FixpointFromModule} = refine_one_module(Mod, State), - NewFixpoint = ordsets:union(FixpointFromModule, Fixpoint), - refine_mods_in_scc(Mods, NewState, NewFixpoint); -refine_mods_in_scc([], State, Fixpoint) -> - {State, Fixpoint}. - reached_fixpoint(OldTypes, NewTypes) -> reached_fixpoint(OldTypes, NewTypes, false). @@ -299,31 +324,22 @@ compare_types_1([], [], _Strict, NotFixpoint) -> false -> {false, NotFixpoint} end. -find_succ_typings(State) -> - find_succ_typings(State, []). - -find_succ_typings(#st{callgraph = Callgraph, parent = Parent} = State, - NotFixpoint) -> - case dialyzer_callgraph:take_scc(Callgraph) of - {ok, SCC, NewCallgraph} -> - Msg = io_lib:format("Typesig analysis for SCC: ~w\n", [format_scc(SCC)]), - ?debug("~s", [Msg]), - send_log(Parent, Msg), - {NewState, NewNotFixpoint1} = - analyze_scc(SCC, State#st{callgraph = NewCallgraph}), - NewNotFixpoint2 = ordsets:union(NewNotFixpoint1, NotFixpoint), - find_succ_typings(NewState, NewNotFixpoint2); - none -> - ?debug("==================== Typesig done ====================\n\n", []), - case NotFixpoint =:= [] of - true -> {fixpoint, State}; - false -> {not_fixpoint, NotFixpoint, State} - end +find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, + plt = Plt, timing_server = Timing, + solvers = Solvers} = State) -> + Init = {Codeserver, Callgraph, Plt, Solvers}, + NotFixpoint = + ?timing(Timing, "typesig", + dialyzer_coordinator:parallel_job(typesig, SCCs, Init, Timing)), + ?debug("==================== Typesig done ====================\n\n", []), + case NotFixpoint =:= [] of + true -> {fixpoint, State}; + false -> {not_fixpoint, NotFixpoint, State} end. -analyze_scc(SCC, #st{codeserver = Codeserver, - callgraph = Callgraph, - plt = Plt} = State) -> +-spec find_succ_types_for_scc(scc(), typesig_init_data()) -> [mfa_or_funlbl()]. + +find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt, Solvers}) -> SCC_Info = [{MFA, dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver), dialyzer_codeserver:lookup_mod_records(M, Codeserver)} @@ -332,26 +348,18 @@ analyze_scc(SCC, #st{codeserver = Codeserver, || {_, _, _} = MFA <- SCC], Contracts2 = [{MFA, Contract} || {MFA, {ok, Contract}} <- Contracts1], Contracts3 = orddict:from_list(Contracts2), - NextLabel = dialyzer_codeserver:get_next_core_label(Codeserver), - {SuccTypes, PltContracts, NotFixpoint} = - find_succ_types_for_scc(SCC_Info, Contracts3, NextLabel, Callgraph, Plt), - State1 = insert_into_plt(SuccTypes, State), - ContrPlt = dialyzer_plt:insert_contract_list(State1#st.plt, PltContracts), - {State1#st{plt = ContrPlt}, NotFixpoint}. - -find_succ_types_for_scc(SCC_Info, Contracts, NextLabel, Callgraph, Plt) -> - %% Assume that the PLT contains the current propagated types + Label = dialyzer_codeserver:get_next_core_label(Codeserver), AllFuns = collect_fun_info([Fun || {_MFA, {_Var, Fun}, _Rec} <- SCC_Info]), PropTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt), - FunTypes = dialyzer_typesig:analyze_scc(SCC_Info, NextLabel, - Callgraph, Plt, PropTypes), + %% Assume that the PLT contains the current propagated types + FunTypes = dialyzer_typesig:analyze_scc(SCC_Info, Label, Callgraph, + Plt, PropTypes, Solvers), AllFunSet = sets:from_list([X || {X, _} <- AllFuns]), - FilteredFunTypes = dict:filter(fun(X, _) -> - sets:is_element(X, AllFunSet) - end, FunTypes), + FilteredFunTypes = + dict:filter(fun(X, _) -> sets:is_element(X, AllFunSet) end, FunTypes), %% Check contracts - PltContracts = dialyzer_contracts:check_contracts(Contracts, Callgraph, - FilteredFunTypes), + PltContracts = + dialyzer_contracts:check_contracts(Contracts3, Callgraph, FilteredFunTypes), ContractFixpoint = lists:all(fun({MFA, _C}) -> %% Check the non-deleted PLT @@ -360,14 +368,14 @@ find_succ_types_for_scc(SCC_Info, Contracts, NextLabel, Callgraph, Plt) -> {value, _} -> true end end, PltContracts), + Plt = insert_into_plt(FilteredFunTypes, Callgraph, Plt), + Plt = dialyzer_plt:insert_contract_list(Plt, PltContracts), case (ContractFixpoint andalso reached_fixpoint_strict(PropTypes, FilteredFunTypes)) of - true -> - {FilteredFunTypes, PltContracts, []}; + true -> []; false -> ?debug("Not fixpoint for: ~w\n", [AllFuns]), - {FilteredFunTypes, PltContracts, - ordsets:from_list([Fun || {Fun, _Arity} <- AllFuns])} + [Fun || {Fun, _Arity} <- AllFuns] end. get_fun_types_from_plt(FunList, Callgraph, Plt) -> @@ -407,10 +415,10 @@ insert_into_doc_plt(FunTypes, Callgraph, DocPlt) -> SuccTypes = format_succ_types(FunTypes, Callgraph), dialyzer_plt:insert_list(DocPlt, SuccTypes). -insert_into_plt(SuccTypes0, #st{callgraph = Callgraph, plt = Plt} = State) -> +insert_into_plt(SuccTypes0, Callgraph, Plt) -> SuccTypes = format_succ_types(SuccTypes0, Callgraph), debug_pp_succ_typings(SuccTypes), - State#st{plt = dialyzer_plt:insert_list(Plt, SuccTypes)}. + dialyzer_plt:insert_list(Plt, SuccTypes). format_succ_types(SuccTypes, Callgraph) -> format_succ_types(dict:to_list(SuccTypes), Callgraph, []). @@ -445,131 +453,3 @@ lookup_name(F, CG) -> error -> F; {ok, Name} -> Name end. - -send_log(none, _Msg) -> - ok; -send_log(Parent, Msg) -> - Parent ! {self(), log, lists:flatten(Msg)}, - ok. - -format_scc(SCC) -> - [MFA || {_M, _F, _A} = MFA <- SCC]. - -%% ============================================================================ -%% -%% Debug interface. -%% -%% ============================================================================ - --spec doit(atom() | file:filename()) -> 'ok'. - -doit(Module) -> - {ok, AbstrCode} = dialyzer_utils:get_abstract_code_from_src(Module), - {ok, Code} = dialyzer_utils:get_core_from_abstract_code(AbstrCode), - {ok, Records} = dialyzer_utils:get_record_and_type_info(AbstrCode), - %% contract typing info in dictionary format - {ok, Contracts, _Callbacks} = - dialyzer_utils:get_spec_info(cerl:concrete(cerl:module_name(Code)), - AbstrCode, Records), - Sigs0 = get_top_level_signatures(Code, Records, Contracts), - M = if is_atom(Module) -> - list_to_atom(filename:basename(atom_to_list(Module))); - is_list(Module) -> - list_to_atom(filename:basename(Module)) - end, - Sigs1 = [{{M, F, A}, Type} || {{F, A}, Type} <- Sigs0], - Sigs = ordsets:from_list(Sigs1), - io:format("==================== Final result ====================\n\n", []), - pp_signatures(Sigs, Records), - ok. - --spec get_top_level_signatures(cerl:c_module(), dict(), dict()) -> - [{{atom(), arity()}, erl_types:erl_type()}]. - -get_top_level_signatures(Code, Records, Contracts) -> - Tree = cerl:from_records(Code), - {LabeledTree, NextLabel} = cerl_trees:label(Tree), - Plt = get_def_plt(), - ModuleName = cerl:atom_val(cerl:module_name(LabeledTree)), - Plt1 = dialyzer_plt:delete_module(Plt, ModuleName), - Plt2 = analyze_module(LabeledTree, NextLabel, Plt1, Records, Contracts), - M = cerl:concrete(cerl:module_name(Tree)), - Functions = [{M, cerl:fname_id(V), cerl:fname_arity(V)} - || {V, _F} <- cerl:module_defs(LabeledTree)], - %% First contracts check - AllContracts = dict:fetch_keys(Contracts), - ErrorContracts = AllContracts -- Functions, - lists:foreach(fun(C) -> - io:format("Contract for non-existing function: ~w\n",[C]) - end, ErrorContracts), - Types = [{MFA, dialyzer_plt:lookup(Plt2, MFA)} || MFA <- Functions], - Sigs = [{{F, A}, erl_types:t_fun(ArgT, RetT)} - || {{_M, F, A}, {value, {RetT, ArgT}}} <- Types], - ordsets:from_list(Sigs). - -get_def_plt() -> - try - dialyzer_plt:from_file(dialyzer_plt:get_default_plt()) - catch - error:no_such_file -> dialyzer_plt:new(); - throw:{dialyzer_error, _} -> dialyzer_plt:new() - end. - -pp_signatures([{{_, module_info, 0}, _}|Left], Records) -> - pp_signatures(Left, Records); -pp_signatures([{{_, module_info, 1}, _}|Left], Records) -> - pp_signatures(Left, Records); -pp_signatures([{{M, F, _A}, Type}|Left], Records) -> - TypeString = - case cerl:is_literal(Type) of -%% Commented out so that dialyzer does not complain -%% false -> -%% "fun(" ++ String = erl_types:t_to_string(Type, Records), -%% string:substr(String, 1, length(String)-1); - true -> - io_lib:format("~w", [cerl:concrete(Type)]) - end, - io:format("~w:~w~s\n", [M, F, TypeString]), - pp_signatures(Left, Records); -pp_signatures([], _Records) -> - ok. - --ifdef(DEBUG_PP). -debug_pp(Tree, _Map) -> - Tree1 = strip_annotations(Tree), - io:put_chars(cerl_prettypr:format(Tree1)), - io:nl(). - -strip_annotations(Tree) -> - cerl_trees:map(fun(T) -> - case cerl:is_literal(T) orelse cerl:is_c_values(T) of - true -> cerl:set_ann(T, []); - false -> - Label = cerl_trees:get_label(T), - cerl:set_ann(T, [{'label', Label}]) - end - end, Tree). --else. -debug_pp(_Tree, _Map) -> - ok. --endif. % DEBUG_PP - -%% -%% Analysis of a single module -%% -analyze_module(LabeledTree, NextLbl, Plt, Records, Contracts) -> - debug_pp(LabeledTree, dict:new()), - CallGraph1 = dialyzer_callgraph:new(), - CallGraph2 = dialyzer_callgraph:scan_core_tree(LabeledTree, CallGraph1), - {CallGraph3, _Ext} = dialyzer_callgraph:remove_external(CallGraph2), - CallGraph4 = dialyzer_callgraph:finalize(CallGraph3), - CodeServer1 = dialyzer_codeserver:new(), - Mod = cerl:concrete(cerl:module_name(LabeledTree)), - CodeServer2 = dialyzer_codeserver:insert(Mod, LabeledTree, CodeServer1), - CodeServer3 = dialyzer_codeserver:set_next_core_label(NextLbl, CodeServer2), - CodeServer4 = dialyzer_codeserver:store_records(Mod, Records, CodeServer3), - CodeServer5 = dialyzer_codeserver:store_contracts(Mod, Contracts, CodeServer4), - Res = analyze_callgraph(CallGraph4, Plt, CodeServer5), - dialyzer_callgraph:delete(CallGraph4), - dialyzer_codeserver:delete(CodeServer5), - Res. diff --git a/lib/dialyzer/src/dialyzer_timing.erl b/lib/dialyzer/src/dialyzer_timing.erl new file mode 100644 index 0000000000..b1a4bdc07c --- /dev/null +++ b/lib/dialyzer/src/dialyzer_timing.erl @@ -0,0 +1,133 @@ +%% -*- erlang-indent-level: 2 -*- +%%------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-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 +%% 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% +%% + +%%%------------------------------------------------------------------- +%%% File : dialyzer_timing.erl +%%% Authors : Stavros Aronis <[email protected]> +%%% Description : Timing reports for Dialyzer +%%%------------------------------------------------------------------- + +-module(dialyzer_timing). + +-export([init/1, start_stamp/2, send_size_info/3, end_stamp/1, stop/1]). + +-export_type([timing_server/0]). + +-type timing_server() :: pid() | 'none'. + +-spec init(boolean() | 'debug') -> timing_server(). + +init(Active) -> + case Active of + true -> + io:format("\n"), + spawn_link(fun() -> loop(now(), 0, "") end); + debug -> + io:format("\n"), + spawn_link(fun() -> debug_loop("") end); + false -> none + end. + +loop(LastNow, Size, Unit) -> + receive + {stamp, Msg, Now} -> + io:format(" ~-10s (+~4.2fs):", [Msg, diff(Now, LastNow)]), + loop(Now, 0, ""); + {stamp, Now} -> + SizeStr = + case Size of + 0 -> ""; + _ -> + Data = io_lib:format("~p ~s",[Size, Unit]), + io_lib:format(" (~12s)",[Data]) + end, + io:format("~7.2fs~s\n", [diff(Now, LastNow), SizeStr]), + loop(Now, 0, ""); + {size, NewSize, NewUnit} -> + loop(LastNow, NewSize, NewUnit); + {Pid, stop, Now} -> + io:format(" ~-9s (+~5.2fs)\n", ["",diff(Now, LastNow)]), + Pid ! ok; + {Pid, stop} -> + Pid ! ok + end. + +debug_loop(Phase) -> + receive + Message -> + {Runtime,_} = statistics(wall_clock), + Procs = erlang:system_info(process_count), + ProcMem = erlang:memory(total), + Status = io_lib:format("~12w ~6w ~20w", [Runtime, Procs, ProcMem]), + case Message of + {stamp, Msg, _Now} -> + io:format("~s ~s_start\n", [Status, Msg]), + debug_loop(Msg); + {stamp, _Now} -> + io:format("~s ~s_stop\n", [Status, Phase]), + debug_loop(""); + {Pid, stop, _Now} -> + Pid ! ok; + {Pid, stop} -> + Pid ! ok; + _ -> + debug_loop(Phase) + end + after + 50 -> + {Runtime,_} = statistics(wall_clock), + Procs = erlang:system_info(process_count), + ProcMem = erlang:memory(total), + Status = io_lib:format("~12w ~6w ~20w", [Runtime, Procs, ProcMem]), + io:format("~s\n", [Status]), + debug_loop(Phase) + end. + + +-spec start_stamp(timing_server(), string()) -> ok. + +start_stamp(none, _) -> ok; +start_stamp(Pid, Msg) -> + Pid ! {stamp, Msg, now()}, + ok. + +-spec end_stamp(timing_server()) -> ok. + +end_stamp(none) -> ok; +end_stamp(Pid) -> + Pid ! {stamp, now()}, + ok. + +-spec send_size_info(timing_server(), integer(), string()) -> ok. + +send_size_info(none, _, _) -> ok; +send_size_info(Pid, Size, Unit) -> + Pid ! {size, Size, Unit}, + ok. + +-spec stop(timing_server()) -> ok. + +stop(none) -> ok; +stop(Pid) -> + Pid ! {self(), stop, now()}, + receive ok -> ok end. + +diff(T2, T1) -> + timer:now_diff(T2,T1) / 1000000. diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index d0d27740f3..0df003a035 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -28,7 +28,7 @@ -module(dialyzer_typesig). --export([analyze_scc/5]). +-export([analyze_scc/6]). -export([get_safe_underapprox/2]). -import(erl_types, @@ -78,6 +78,8 @@ -record(constraint_list, {type :: 'conj' | 'disj', list :: [constr()], deps :: [dep()], + masks :: [{dep(),[non_neg_integer()]}] | + {'d',dict()}, id :: {'list', dep()}}). -type constraint_list() :: #constraint_list{}. @@ -91,23 +93,26 @@ -type typesig_scc() :: [{mfa(), {cerl:c_var(), cerl:c_fun()}, dict()}]. -type typesig_funmap() :: [{type_var(), type_var()}]. %% Orddict --record(state, {callgraph :: dialyzer_callgraph:callgraph(), - cs = [] :: [constr()], - cmap = dict:new() :: dict(), - fun_map = [] :: typesig_funmap(), - fun_arities = dict:new() :: dict(), - in_match = false :: boolean(), - in_guard = false :: boolean(), - module :: module(), - name_map = dict:new() :: dict(), - next_label :: label(), - self_recs :: [label()], - plt :: dialyzer_plt:plt(), - prop_types = dict:new() :: dict(), - records = dict:new() :: dict(), - opaques = [] :: [erl_types:erl_type()], - scc = [] :: [type_var()], - mfas = [] :: [dialyzer_callgraph:mfa_or_funlbl()] +-type dict_or_ets() :: {'d', dict()} | {'e', ets:tid()}. + +-record(state, {callgraph :: dialyzer_callgraph:callgraph(), + cs = [] :: [constr()], + cmap = {'d', dict:new()} :: dict_or_ets(), + fun_map = [] :: typesig_funmap(), + fun_arities = dict:new() :: dict(), + in_match = false :: boolean(), + in_guard = false :: boolean(), + module :: module(), + name_map = dict:new() :: dict(), + next_label = 0 :: label(), + self_rec :: erl_types:erl_type(), + plt :: dialyzer_plt:plt(), + prop_types = {'d', dict:new()} :: dict_or_ets(), + records = dict:new() :: dict(), + opaques = [] :: [erl_types:erl_type()], + scc = [] :: [type_var()], + mfas :: [tuple()], + solvers = [] :: [solver()] }). %%----------------------------------------------------------------------------- @@ -119,8 +124,10 @@ %%-define(DEBUG_CONSTRAINTS, true). -ifdef(DEBUG). -define(DEBUG_NAME_MAP, true). +-define(DEBUG_LOOP_DETECTION, true). -endif. %%-define(DEBUG_NAME_MAP, true). +%%-define(DEBUG_LOOP_DETECTION, true). -ifdef(DEBUG). -define(debug(__String, __Args), io:format(__String, __Args)). @@ -139,7 +146,7 @@ %%----------------------------------------------------------------------------- %% Analysis of strongly connected components. %% -%% analyze_scc(SCC, NextLabel, CallGraph, PLT, PropTypes) -> FunTypes +%% analyze_scc(SCC, NextLabel, CallGraph, PLT, PropTypes, Solvers) -> FunTypes %% %% SCC - [{MFA, Def, Records}] %% where Def = {Var, Fun} as in the Core Erlang module definitions. @@ -152,15 +159,17 @@ %% about functions that can be called by this SCC. %% PropTypes - A dictionary. %% FunTypes - A dictionary. +%% Solvers - User specified solvers. %%----------------------------------------------------------------------------- -spec analyze_scc(typesig_scc(), label(), dialyzer_callgraph:callgraph(), - dialyzer_plt:plt(), dict()) -> dict(). + dialyzer_plt:plt(), dict(), [solver()]) -> dict(). -analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes) -> +analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers0) -> + Solvers = solvers(Solvers0), assert_format_of_scc(SCC), - State1 = new_state(SCC, NextLabel, CallGraph, Plt, PropTypes), + State1 = new_state(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers), DefSet = add_def_list([Var || {_MFA, {Var, _Fun}, _Rec} <- SCC], sets:new()), State2 = traverse_scc(SCC, DefSet, State1), State3 = state__finalize(State2), @@ -174,6 +183,9 @@ assert_format_of_scc([{_MFA, {_Var, _Fun}, _Records}|Left]) -> assert_format_of_scc([]) -> ok. +solvers([]) -> [v2]; +solvers(Solvers) -> Solvers. + %% ============================================================================ %% %% Gets the constraints by traversing the code. @@ -1661,18 +1673,23 @@ get_bif_test_constr(Dst, Arg, Type, State) -> solve([Fun], State) -> ?debug("============ Analyzing Fun: ~w ===========\n", [debug_lookup_name(Fun)]), - solve_fun(Fun, dict:new(), State); + solve_fun(Fun, map_new(), State); solve([_|_] = SCC, State) -> ?debug("============ Analyzing SCC: ~w ===========\n", [[debug_lookup_name(F) || F <- SCC]]), - solve_scc(SCC, dict:new(), State, false). + {Parallel, NewState} = + case parallel_split(SCC) of + false -> {false, State}; + SplitSCC -> {SplitSCC, minimize_state(State)} + end, + solve_scc(SCC, Parallel, map_new(), NewState, false). solve_fun(Fun, FunMap, State) -> Cs = state__get_cs(Fun, State), Deps = get_deps(Cs), Ref = mk_constraint_ref(Fun, Deps), %% Note that functions are always considered to succeed. - {ok, _MapDict, NewMap} = solve_ref_or_list(Ref, FunMap, dict:new(), State), + NewMap = solve(Fun, Ref, FunMap, State), NewType = lookup_type(Fun, NewMap), NewFunMap1 = case state__get_rec_var(Fun, State) of error -> FunMap; @@ -1680,21 +1697,23 @@ solve_fun(Fun, FunMap, State) -> end, enter_type(Fun, NewType, NewFunMap1). -solve_scc(SCC, Map, State, TryingUnit) -> - State1 = state__mark_as_non_self_rec(SCC, State), +solve_scc(SCC, Parallel, Map, State, TryingUnit) -> Vars0 = [{Fun, state__get_rec_var(Fun, State)} || Fun <- SCC], Vars = [Var || {_, {ok, Var}} <- Vars0], Funs = [Fun || {Fun, {ok, _}} <- Vars0], Types = unsafe_lookup_type_list(Funs, Map), RecTypes = [t_limit(Type, ?TYPE_LIMIT) || Type <- Types], CleanMap = lists:foldl(fun(Fun, AccFunMap) -> - dict:erase(t_var_name(Fun), AccFunMap) + erase_type(t_var_name(Fun), AccFunMap) end, Map, SCC), Map1 = enter_type_lists(Vars, RecTypes, CleanMap), ?debug("Checking SCC: ~w\n", [[debug_lookup_name(F) || F <- SCC]]), - SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State1) end, - Map2 = lists:foldl(SolveFun, Map1, SCC), FunSet = ordsets:from_list([t_var_name(F) || F <- SCC]), + Map2 = + case Parallel of + false -> solve_whole_scc(SCC, Map1, State); + SplitSCC -> solve_whole_scc_parallel(SplitSCC, Map1, State) + end, case maps_are_equal(Map2, Map, FunSet) of true -> ?debug("SCC ~w reached fixpoint\n", [SCC]), @@ -1708,20 +1727,134 @@ solve_scc(SCC, Map, State, TryingUnit) -> true -> t_fun(t_fun_args(T), t_unit()) end || T <- NewTypes], Map3 = enter_type_lists(Funs, UnitTypes, Map2), - solve_scc(SCC, Map3, State, true); + solve_scc(SCC, Parallel, Map3, State, true); false -> + case Parallel of + false -> true; + _ -> dispose_state(State) + end, Map2 end; false -> ?debug("SCC ~w did not reach fixpoint\n", [SCC]), - solve_scc(SCC, Map2, State, TryingUnit) + solve_scc(SCC, Parallel, Map2, State, TryingUnit) + end. + +solve_whole_scc(SCC, Map, State) -> + SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State) end, + lists:foldl(SolveFun, Map, SCC). + +%%------------------------------------------------------------------------------ + +-define(worth_it, 42). + +parallel_split(SCC) -> + Length = length(SCC), + case Length > 2*?worth_it of + false -> false; + true -> + case min(dialyzer_utils:parallelism(), 8) of + 1 -> false; + CPUs -> + FullShare = Length div CPUs + 1, + Unit = max(FullShare, ?worth_it), + split(SCC, Unit, []) + end + end. + +minimize_state(#state{ + cmap = {d, CMap}, + fun_map = FunMap, + fun_arities = FunArities, + self_rec = SelfRec, + prop_types = {d, PropTypes}, + opaques = Opaques, + solvers = Solvers + }) -> + ETSCMap = ets:new(cmap,[{read_concurrency, true}]), + ETSPropTypes = ets:new(prop_types,[{read_concurrency, true}]), + true = ets:insert(ETSCMap, dict:to_list(CMap)), + true = ets:insert(ETSPropTypes, dict:to_list(PropTypes)), + #state + {cmap = {e, ETSCMap}, + fun_map = FunMap, + fun_arities = FunArities, + self_rec = SelfRec, + prop_types = {e, ETSPropTypes}, + opaques = Opaques, + solvers = Solvers + }. + +dispose_state(#state{cmap = {e, ETSCMap}, + prop_types = {e, ETSPropTypes}}) -> + true = ets:delete(ETSCMap), + true = ets:delete(ETSPropTypes). + +solve_whole_scc_parallel(SplitSCC, Map, State) -> + Workers = spawn_workers(SplitSCC, Map, State), + wait_results(Workers, Map, fold_res_fun(State)). + +spawn_workers(SplitSCC, Map, State) -> + Spawner = solve_scc_spawner(self(), Map, State), + lists:foreach(Spawner, SplitSCC), + length(SplitSCC). + +wait_results(0, Map, _FoldResFun) -> + Map; +wait_results(Pending, Map, FoldResFun) -> + Res = receive_scc_result(), + NewMap = lists:foldl(FoldResFun, Map, Res), + wait_results(Pending-1, NewMap, FoldResFun). + +solve_scc_spawner(Parent, Map, State) -> + fun(SCCPart) -> + spawn_link(fun() -> solve_scc_worker(Parent, SCCPart, Map, State) end) + end. + +split([], _Unit, Acc) -> + Acc; +split(List, Unit, Acc) -> + {Taken, Rest} = + try + lists:split(Unit, List) + catch + _:_ -> {List, []} + end, + split(Rest, Unit, [Taken|Acc]). + +solve_scc_worker(Parent, SCCPart, Map, State) -> + SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State) end, + FinalMap = lists:foldl(SolveFun, Map, SCCPart), + Res = + [{F, t_limit(unsafe_lookup_type(F, FinalMap), ?TYPE_LIMIT)} || + F <- SCCPart], + send_scc_result(Parent, Res). + +fold_res_fun(State) -> + fun({F, Type}, Map) -> + case state__get_rec_var(F, State) of + {ok, R} -> + enter_type(R, Type, enter_type(F, Type, Map)); + error -> + enter_type(F, Type, Map) + end end. +receive_scc_result() -> + receive + {scc_fun, Res} -> Res + end. + +send_scc_result(Parent, Res) -> + Parent ! {scc_fun, Res}. + +%%------------------------------------------------------------------------------ + scc_fold_fun(F, FunMap, State) -> Deps = get_deps(state__get_cs(F, State)), Cs = mk_constraint_ref(F, Deps), %% Note that functions are always considered to succeed. - {ok, _NewMapDict, Map} = solve_ref_or_list(Cs, FunMap, dict:new(), State), + Map = solve(F, Cs, FunMap, State), NewType0 = unsafe_lookup_type(F, Map), NewType = t_limit(NewType0, ?TYPE_LIMIT), NewFunMap = case state__get_rec_var(F, State) of @@ -1734,15 +1867,440 @@ scc_fold_fun(F, FunMap, State) -> format_type(NewType)]), NewFunMap. +solve(Fun, Cs, FunMap, State) -> + Solvers = State#state.solvers, + R = [solver(S, solve_fun(S, Fun, Cs, FunMap, State)) || S <- Solvers], + check_solutions(R, Fun, no_solver, no_map). + +solver(Solver, SolveFun) -> + ?debug("Start solver ~w\n", [Solver]), + try timer:tc(SolveFun) of + {Time, {ok, Map}} -> + ?debug("End solver ~w (~w microsecs)\n", [Solver, Time]), + {Solver, Map, Time}; + {_, _R} -> + ?debug("Solver ~w returned unexpected result:\n ~P\n", + [Solver, _R, 60]), + throw(error) + catch E:R -> + io:format("Solver ~w failed: ~w:~p\n ~p\n", + [Solver, E, R, erlang:get_stacktrace()]), + throw(error) + end. + +solve_fun(v1, _Fun, Cs, FunMap, State) -> + fun() -> + {ok, _MapDict, NewMap} = solve_ref_or_list(Cs, FunMap, dict:new(), State), + {ok, NewMap} + end; +solve_fun(v2, Fun, _Cs, FunMap, State) -> + fun() -> v2_solve_ref(Fun, FunMap, State) end. + +check_solutions([], _Fun, _S, Map) -> + Map; +check_solutions([{S1,Map1,_Time1}|Maps], Fun, S, Map) -> + ?debug("Solver ~w needed ~w microsecs\n", [S1, _Time1]), + case Map =:= no_map orelse sane_maps(Map, Map1, [Fun], S, S1) of + true -> + check_solutions(Maps, Fun, S1, Map1); + false -> + ?debug("Constraint solvers do not agree on ~w\n", [Fun]), + pp_map(atom_to_list(S), Map), + pp_map(atom_to_list(S1), Map1), + io:format("A bug was found. Please report it, and use the option " + "`--solver v1' until the bug has been fixed.\n"), + throw(error) + end. + +sane_maps(Map1, Map2, Keys, _S1, _S2) -> + lists:all(fun(Key) -> + V1 = unsafe_lookup_type(Key, Map1), + V2 = unsafe_lookup_type(Key, Map2), + case t_is_equal(V1, V2) of + true -> true; + false -> + ?debug("Constraint solvers do not agree on ~w\n", [Key]), + ?debug("~w: ~s\n", + [_S1, format_type(unsafe_lookup_type(Key, Map1))]), + ?debug("~w: ~s\n", + [_S2, format_type(unsafe_lookup_type(Key, Map2))]), + false + end + end, Keys). + +%% Solver v2 + +-record(v2_state, {constr_data = dict:new() :: dict(), + state :: #state{}}). + +v2_solve_ref(Fun, Map, State) -> + V2State = #v2_state{state = State}, + {ok, NewMap, _, _} = v2_solve_reference(Fun, Map, V2State), + {ok, NewMap}. + +v2_solve(#constraint{}=C, Map, V2State) -> + State = V2State#v2_state.state, + case solve_one_c(C, Map, State#state.opaques) of + error -> + report_failed_constraint(C, Map), + {error, V2State}; + {ok, {NewMap, U}} -> + {ok, NewMap, V2State, U} + end; +v2_solve(#constraint_list{type = disj}=C, Map, V2State) -> + v2_solve_disjunct(C, Map, V2State); +v2_solve(#constraint_list{type = conj}=C, Map, V2State) -> + v2_solve_conjunct(C, Map, V2State); +v2_solve(#constraint_ref{id = Id}, Map, V2State) -> + v2_solve_reference(Id, Map, V2State). + +v2_solve_reference(Id, Map, V2State0) -> + ?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]), + pp_map("Map", Map), + pp_constr_data("solve_ref", V2State0), + Map1 = restore_local_map(V2State0, Id, Map), + State = V2State0#v2_state.state, + Cs = state__get_cs(Id, State), + Res = + case state__is_self_rec(Id, State) of + true -> v2_solve_self_recursive(Cs, Map1, Id, t_none(), V2State0); + false -> v2_solve(Cs, Map1, V2State0) + end, + {FunType, V2State} = + case Res of + {error, V2State1} -> + ?debug("Error solving for function ~p\n", [debug_lookup_name(Id)]), + Arity = state__fun_arity(Id, State), + FunType0 = + case state__prop_domain(t_var_name(Id), State) of + error -> t_fun(Arity, t_none()); + {ok, Dom} -> t_fun(Dom, t_none()) + end, + {FunType0, V2State1}; + {ok, NewMap, V2State1, U} -> + ?debug("Done solving fun: ~p\n", [debug_lookup_name(Id)]), + FunType0 = lookup_type(Id, NewMap), + V2State2 = save_local_map(V2State1, Id, U, NewMap), + {FunType0, V2State2} + end, + ?debug("ref Id=~w Assigned ~s\n", [Id, format_type(FunType)]), + {NewMap1, U1} = enter_var_type(Id, FunType, Map), + {NewMap2, U2} = + case state__get_rec_var(Id, State) of + {ok, Var} -> enter_var_type(Var, FunType, NewMap1); + error -> {NewMap1, []} + end, + {ok, NewMap2, V2State, lists:umerge(U1, U2)}. + +v2_solve_self_recursive(Cs, Map, Id, RecType0, V2State0) -> + ?debug("Solving self recursive ~w\n", [debug_lookup_name(Id)]), + State = V2State0#v2_state.state, + {ok, RecVar} = state__get_rec_var(Id, State), + ?debug("OldRecType ~s\n", [format_type(RecType0)]), + RecType = t_limit(RecType0, ?TYPE_LIMIT), + {Map1, U0} = enter_var_type(RecVar, RecType, Map), + V2State1 = save_updated_vars1(V2State0, Cs, U0), % Probably not necessary + case v2_solve(Cs, Map1, V2State1) of + {error, _V2State}=Error -> + case t_is_none(RecType0) of + true -> + %% Try again and assume that this is a non-terminating function. + Arity = state__fun_arity(Id, State), + NewRecType = t_fun(lists:duplicate(Arity, t_any()), t_unit()), + v2_solve_self_recursive(Cs, Map, Id, NewRecType, V2State0); + false -> + Error + end; + {ok, NewMap, V2State, U} -> + pp_map("recursive finished", NewMap), + NewRecType = unsafe_lookup_type(Id, NewMap), + case t_is_equal(NewRecType, RecType0) of + true -> + {NewMap2, U1} = enter_var_type(RecVar, NewRecType, NewMap), + {ok, NewMap2, V2State, lists:umerge(U, U1)}; + false -> + v2_solve_self_recursive(Cs, Map, Id, NewRecType, V2State0) + end + end. + +enter_var_type(Var, Type, Map0) -> + {Map, Vs} = enter_type2(Var, Type, Map0), + {Map, [t_var_name(V) || V <- Vs]}. + +v2_solve_disjunct(Disj, Map, V2State0) -> + #constraint_list{type = disj, id = _Id, list = Cs, masks = Masks} = Disj, + ?debug("disjunct Id=~w~n", [_Id]), + pp_map("Map", Map), + pp_constr_data("disjunct", V2State0), + case get_flags(V2State0, Disj) of + {V2State1, failed_list} -> {error, V2State1}; % cannot happen + {V2State1, Flags} when Flags =/= [] -> + {ok, V2State, Eval, UL, MapL0, Uneval, Failed} = + v2_solve_disj(Flags, Cs, 1, Map, V2State1, [], [], [], [], false), + ?debug("disj ending _Id=~w Eval=~w, |Uneval|=~w |UL|=~w~n", + [_Id, Eval, length(Uneval), length(UL)]), + if Eval =:= [], Uneval =:= [] -> + {error, failed_list(Disj, V2State0)}; + true -> + {Is0, UnIds} = lists:unzip(Uneval), + MapL = [restore_local_map(V2State, Id, Map) || + Id <- UnIds] ++ MapL0, + %% If some branch has just failed every variable of the + %% non-failed branches need to be checked, not just the + %% updated ones. + U0 = case Failed of + false -> lists:umerge(UL); + true -> constrained_keys(MapL) + end, + if U0 =:= [] -> {ok, Map, V2State, []}; + true -> + NotFailed = lists:umerge(Is0, Eval), + U1 = [V || V <- U0, + var_occurs_everywhere(V, Masks, NotFailed)], + NewMap = join_maps(U1, MapL, Map), + pp_map("NewMap", NewMap), + U = updated_vars_only(U1, Map, NewMap), + ?debug("disjunct finished _Id=~w\n", [_Id]), + {ok, NewMap, V2State, U} + end + end + end. + +var_occurs_everywhere(V, Masks, NotFailed) -> + ordsets:is_subset(NotFailed, get_mask(V, Masks)). + +v2_solve_disj([I|Is], [C|Cs], I, Map0, V2State0, UL, MapL, Eval, Uneval, + Failed0) -> + Id = C#constraint_list.id, + Map1 = restore_local_map(V2State0, Id, Map0), + case v2_solve(C, Map1, V2State0) of + {error, V2State} -> + ?debug("disj error I=~w~n", [I]), + Failed = Failed0 orelse not is_failed_list(C, V2State0), + v2_solve_disj(Is, Cs, I+1, Map0, V2State, UL, MapL, Eval, Uneval, Failed); + {ok, Map, V2State1, U} -> + ?debug("disj I=~w U=~w~n", [I, U]), + V2State = save_local_map(V2State1, Id, U, Map), + pp_map("DMap", Map), + v2_solve_disj(Is, Cs, I+1, Map0, V2State, [U|UL], [Map|MapL], + [I|Eval], Uneval, Failed0) + end; +v2_solve_disj([], [], _I, _Map, V2State, UL, MapL, Eval, Uneval, Failed) -> + {ok, V2State, lists:reverse(Eval), UL, MapL, lists:reverse(Uneval), Failed}; +v2_solve_disj(Is, [C|Cs], I, Map, V2State, UL, MapL, Eval, Uneval0, Failed) -> + Uneval = [{I,C#constraint_list.id} || + not is_failed_list(C, V2State)] ++ Uneval0, + v2_solve_disj(Is, Cs, I+1, Map, V2State, UL, MapL, Eval, Uneval, Failed). + +save_local_map(#v2_state{constr_data = ConData}=V2State, Id, U, Map) -> + Part0 = [{V,dict:fetch(V, Map)} || V <- U], + Part1 = + case dict:find(Id, ConData) of + error -> []; % cannot happen + {ok, {Part2,[]}} -> Part2 + end, + ?debug("save local map Id=~w:\n", [Id]), + Part = lists:ukeymerge(1, lists:keysort(1, Part0), Part1), + pp_map("New Part", dict:from_list(Part0)), + pp_map("Old Part", dict:from_list(Part1)), + pp_map(" => Part", dict:from_list(Part)), + V2State#v2_state{constr_data = dict:store(Id, {Part,[]}, ConData)}. + +restore_local_map(#v2_state{constr_data = ConData}, Id, Map0) -> + case dict:find(Id, ConData) of + error -> Map0; + {ok, failed} -> Map0; + {ok, {[],_}} -> Map0; + {ok, {Part0,U}} -> + Part = [{K,V} || {K,V} <- Part0, not lists:member(K, U)], + ?debug("restore local map Id=~w U=~w\n", [Id, U]), + pp_map("Part", dict:from_list(Part)), + pp_map("Map0", Map0), + Map = lists:foldl(fun({K,V}, D) -> dict:store(K, V, D)end, Map0, Part), + pp_map("Map", Map), + Map + end. + +v2_solve_conjunct(Conj, Map, V2State0) -> + #constraint_list{type = conj, list = Cs} = Conj, + ?debug("conjunct Id=~w~n", [Conj#constraint_list.id]), + IsFlat = case Cs of [#constraint{}|_] -> true; _ -> false end, + case get_flags(V2State0, Conj) of + {V2State, failed_list} -> {error, V2State}; + {V2State, Flags} -> + v2_solve_conj(Flags, Cs, 1, Map, Conj, IsFlat, V2State, [], [], [], + Map, Flags) + end. + +%% LastMap and LastFlags are used for loop detection. +v2_solve_conj([I|Is], [Cs|Tail], I, Map0, Conj, IsFlat, V2State0, + UL, NewFs0, VarsUp, LastMap, LastFlags) -> + ?debug("conj Id=~w I=~w~n", [Conj#constraint_list.id, I]), + true = IsFlat =:= is_record(Cs, constraint), + pp_constr_data("conj", V2State0), + case v2_solve(Cs, Map0, V2State0) of + {error, V2State1} -> {error, failed_list(Conj, V2State1)}; + {ok, Map, V2State1, []} -> + v2_solve_conj(Is, Tail, I+1, Map, Conj, IsFlat, V2State1, + UL, NewFs0, VarsUp, LastMap, LastFlags); + {ok, Map, V2State1, U} when IsFlat -> % optimization + %% It is ensured by enumerate_constraints() that every + %% #constraint{} has a conjunct as parent, and that such a + %% parent has nothing but #constraint{}:s as children, a fact + %% which is used here to simplify the flag calculation. + Mask = lists:umerge([get_mask(V, Conj#constraint_list.masks) || V <- U]), + {Is1, NewF} = add_mask_to_flags(Is, Mask, I, []), + NewFs = [NewF|NewFs0], + v2_solve_conj(Is1, Tail, I+1, Map, Conj, IsFlat, V2State1, + [U|UL], NewFs, VarsUp, LastMap, LastFlags); + {ok, Map, V2State1, U} -> + #constraint_list{masks = Masks, list = AllCs} = Conj, + M = lists:keydelete(I, 1, vars_per_child(U, Masks)), + {V2State2, NewF0} = save_updated_vars_list(AllCs, M, V2State1), + {NewF, F} = lists:splitwith(fun(J) -> J < I end, NewF0), + Is1 = lists:umerge(Is, F), + NewFs = [NewF|NewFs0], + v2_solve_conj(Is1, Tail, I+1, Map, Conj, IsFlat, V2State2, + [U|UL], NewFs, VarsUp, LastMap, LastFlags) + end; +v2_solve_conj([], _Cs, _I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp, + LastMap, LastFlags) -> + U = lists:umerge(UL), + case lists:umerge(NewFs) of + [] -> + ?debug("conjunct finished Id=~w\n", [Conj#constraint_list.id]), + {ok, Map, V2State, lists:umerge([U|VarsUp])}; + NewFlags when NewFlags =:= LastFlags, Map =:= LastMap -> + %% A loop was detected! The cause is some bug, possibly in erl_types. + %% The evaluation continues, but the results can be wrong. + report_detected_loop(Conj), + {ok, Map, V2State, lists:umerge([U|VarsUp])}; + NewFlags -> + #constraint_list{type = conj, list = Cs} = Conj, + v2_solve_conj(NewFlags, Cs, 1, Map, Conj, IsFlat, V2State, + [], [], [U|VarsUp], Map, NewFlags) + end; +v2_solve_conj(Is, [_|Tail], I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp, + LastMap, LastFlags) -> + v2_solve_conj(Is, Tail, I+1, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp, + LastMap, LastFlags). + +-ifdef(DEBUG_LOOP_DETECTION). +report_detected_loop(Conj) -> + io:format("A loop was detected in ~w\n", [Conj#constraint_list.id]). +-else. +report_detected_loop(_) -> + ok. +-endif. + +add_mask_to_flags(Flags, [Im|M], I, L) when I > Im -> + add_mask_to_flags(Flags, M, I, [Im|L]); +add_mask_to_flags(Flags, [_|M], _I, L) -> + {lists:umerge(Flags, M), lists:reverse(L)}. + +get_mask(V, {d, Masks}) -> + case dict:find(V, Masks) of + error -> []; + {ok, M} -> M + end; +get_mask(V, Masks) -> + case lists:keyfind(V, 1, Masks) of + false -> []; + {V, M} -> M + end. + +get_flags(#v2_state{constr_data = ConData}=V2State0, C) -> + #constraint_list{id = Id, list = Cs, masks = Masks} = C, + case dict:find(Id, ConData) of + error -> + ?debug("get_flags Id=~w Flags=all ~w\n", [Id, length(Cs)]), + V2State = V2State0#v2_state{constr_data = dict:store(Id, {[],[]}, ConData)}, + {V2State, lists:seq(1, length(Cs))}; + {ok, failed} -> + {V2State0, failed_list}; + {ok, {Part,U}} when U =/= [] -> + ?debug("get_flags Id=~w U=~w\n", [Id, U]), + V2State = V2State0#v2_state{constr_data = dict:store(Id, {Part,[]}, ConData)}, + save_updated_vars_list(Cs, vars_per_child(U, Masks), V2State) + end. + +vars_per_child(U, Masks) -> + family([{I, V} || V <- lists:usort(U), I <- get_mask(V, Masks)]). + +save_updated_vars_list(Cs, IU, V2State) -> + save_updated_vars_list1(Cs, IU, V2State, 1, []). + +save_updated_vars_list1([C|Cs], [{I,U}|IU], V2State0, I, Is) -> + V2State = save_updated_vars(C, U, V2State0), + save_updated_vars_list1(Cs, IU, V2State, I+1, [I|Is]); +save_updated_vars_list1([], [], V2State, _I, Is) -> + {V2State, lists:reverse(Is)}; +save_updated_vars_list1([_|Cs], IU, V2State, I, Is) -> + save_updated_vars_list1(Cs, IU, V2State, I+1, Is). + +save_updated_vars(#constraint{}, _, V2State) -> + V2State; +save_updated_vars(#constraint_list{}=C, U, V2State0) -> + save_updated_vars1(V2State0, C, U); +save_updated_vars(#constraint_ref{id = Id}, U, V2State) -> + Cs = state__get_cs(Id, V2State#v2_state.state), + save_updated_vars(Cs, U, V2State). + +save_updated_vars1(V2State, C, U) -> + #v2_state{constr_data = ConData} = V2State, + #constraint_list{id = Id} = C, + case dict:find(Id, ConData) of + error -> V2State; % error means everything is flagged + {ok, failed} -> V2State; + {ok, {Part,U0}} -> + %% Duplicates are not so common; let masks/2 remove them. + U1 = U ++ U0, + V2State#v2_state{constr_data = dict:store(Id, {Part,U1}, ConData)} + end. + +-ifdef(DEBUG). +pp_constr_data(_Tag, #v2_state{constr_data = D}) -> + io:format("Constr data at ~p\n", [_Tag]), + _ = [begin + case _PartU of + {_Part, _U} -> + io:format("Id: ~w Vars: ~w\n", [_Id, _U]), + [pp_map("Part", dict:from_list(_Part)) || _Part =/= []]; + failed -> + io:format("Id: ~w failed list\n", [_Id]) + end + end || + {_Id, _PartU} <- lists:keysort(1, dict:to_list(D))], + ok. + +-else. +pp_constr_data(_Tag, _V2State) -> + ok. +-endif. + +failed_list(#constraint_list{id = Id}, #v2_state{constr_data = D}=V2State) -> + ?debug("error list ~w~n", [Id]), + V2State#v2_state{constr_data = dict:store(Id, failed, D)}. + +is_failed_list(#constraint_list{id = Id}, #v2_state{constr_data = D}) -> + dict:find(Id, D) =:= {ok, failed}. + +%% Solver v1 + solve_ref_or_list(#constraint_ref{id = Id, deps = Deps}, Map, MapDict, State) -> {OldLocalMap, Check} = case dict:find(Id, MapDict) of - error -> {dict:new(), false}; + error -> {map_new(), false}; {ok, M} -> {M, true} end, ?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]), + %% Note: mk_constraint_ref() has already removed Id from Deps. The + %% reason for doing it there is that it makes it easy for + %% calculate_masks() to make the corresponding adjustment for + %% version v2. CheckDeps = ordsets:del_element(t_var_name(Id), Deps), + true = CheckDeps =:= Deps, case Check andalso maps_are_equal(OldLocalMap, Map, CheckDeps) of true -> ?debug("Equal\n", []), @@ -1771,6 +2329,7 @@ solve_ref_or_list(#constraint_ref{id = Id, deps = Deps}, FunType0 = lookup_type(Id, NewMap), {NewMapDict0, FunType0} end, + ?debug(" Id=~w Assigned ~s\n", [Id, format_type(FunType)]), NewMap1 = enter_type(Id, FunType, Map), NewMap2 = case state__get_rec_var(Id, State) of @@ -1783,17 +2342,21 @@ solve_ref_or_list(#constraint_list{type=Type, list = Cs, deps = Deps, id = Id}, Map, MapDict, State) -> {OldLocalMap, Check} = case dict:find(Id, MapDict) of - error -> {dict:new(), false}; + error -> {map_new(), false}; {ok, M} -> {M, true} end, ?debug("Checking ref to list: ~w\n", [Id]), - case Check andalso maps_are_equal(OldLocalMap, Map, Deps) of + if + OldLocalMap =:= error -> {error, MapDict}; true -> - ?debug("~w equal ~w\n", [Type, Id]), - {ok, MapDict, Map}; - false -> - ?debug("~w not equal: ~w. Solving\n", [Type, Id]), - solve_clist(Cs, Type, Id, Deps, MapDict, Map, State) + case Check andalso maps_are_equal(OldLocalMap, Map, Deps) of + true -> + ?debug("~w equal ~w\n", [Type, Id]), + {ok, MapDict, Map}; + false -> + ?debug("~w not equal: ~w. Solving\n", [Type, Id]), + solve_clist(Cs, Type, Id, Deps, MapDict, Map, State) + end end. solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) -> @@ -1801,8 +2364,8 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) -> {ok, RecVar} = state__get_rec_var(Id, State), ?debug("OldRecType ~s\n", [format_type(RecType0)]), RecType = t_limit(RecType0, ?TYPE_LIMIT), - Map1 = enter_type(RecVar, RecType, dict:erase(t_var_name(Id), Map)), - ?debug("\tMap in: ~p\n",[[{X, format_type(Y)}||{X, Y}<-dict:to_list(Map1)]]), + Map1 = enter_type(RecVar, RecType, erase_type(t_var_name(Id), Map)), + pp_map("Map1", Map1), case solve_ref_or_list(Cs, Map1, MapDict, State) of {error, _} = Error -> case t_is_none(RecType0) of @@ -1815,8 +2378,7 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) -> Error end; {ok, NewMapDict, NewMap} -> - ?debug("\tMap: ~p\n", - [[{X, format_type(Y)} || {X, Y} <- dict:to_list(NewMap)]]), + pp_map("NewMap", NewMap), NewRecType = unsafe_lookup_type(Id, NewMap), case t_is_equal(NewRecType, RecType0) of true -> @@ -1828,7 +2390,8 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) -> solve_clist(Cs, conj, Id, Deps, MapDict, Map, State) -> case solve_cs(Cs, Map, MapDict, State) of - {error, _} = Error -> Error; + {error, NewMapDict} -> + {error, dict:store(Id, error, NewMapDict)}; {ok, NewMapDict, NewMap} = Ret -> case Cs of [_] -> @@ -1850,7 +2413,7 @@ solve_clist(Cs, disj, Id, _Deps, MapDict, Map, State) -> end, {Maps, NewMapDict} = lists:mapfoldl(Fun, MapDict, Cs), case [X || {ok, X} <- Maps] of - [] -> {error, NewMapDict}; + [] -> {error, dict:store(Id, error, NewMapDict)}; MapList -> NewMap = join_maps(MapList), {ok, dict:store(Id, NewMap, NewMapDict), NewMap} @@ -1869,14 +2432,9 @@ solve_cs([#constraint_list{} = C|Tail], Map, MapDict, State) -> solve_cs([#constraint{} = C|Tail], Map, MapDict, State) -> case solve_one_c(C, Map, State#state.opaques) of error -> - ?debug("+++++++++++\nFailed: ~s :: ~s ~w ~s :: ~s\n+++++++++++\n", - [format_type(C#constraint.lhs), - format_type(lookup_type(C#constraint.lhs, Map)), - C#constraint.op, - format_type(C#constraint.rhs), - format_type(lookup_type(C#constraint.rhs, Map))]), + report_failed_constraint(C, Map), {error, MapDict}; - {ok, NewMap} -> + {ok, {NewMap, _U}} -> solve_cs(Tail, NewMap, MapDict, State) end; solve_cs([], Map, MapDict, _State) -> @@ -1897,7 +2455,11 @@ solve_one_c(#constraint{lhs = Lhs, rhs = Rhs, op = Op}, Map, Opaques) -> eq -> case solve_subtype(Lhs, Inf, Map, Opaques) of error -> error; - {ok, Map1} -> solve_subtype(Rhs, Inf, Map1, Opaques) + {ok, {Map1, U1}} -> + case solve_subtype(Rhs, Inf, Map1, Opaques) of + error -> error; + {ok, {Map2, U2}} -> {ok, {Map2, lists:umerge(U1, U2)}} + end end end end. @@ -1920,18 +2482,34 @@ solve_subtype(Type, Inf, Map, Opaques) -> end. %% end. +report_failed_constraint(_C, _Map) -> + ?debug("+++++++++++\nFailed: ~s :: ~s ~w ~s :: ~s\n+++++++++++\n", + [format_type(_C#constraint.lhs), + format_type(lookup_type(_C#constraint.lhs, _Map)), + _C#constraint.op, + format_type(_C#constraint.rhs), + format_type(lookup_type(_C#constraint.rhs, _Map))]). + %% ============================================================================ %% %% Maps and types. %% %% ============================================================================ +map_new() -> + dict:new(). + +join_maps([Map]) -> + Map; join_maps(Maps) -> - Keys = lists:foldl(fun(TmpMap, AccKeys) -> - [Key || Key <- AccKeys, dict:is_key(Key, TmpMap)] - end, - dict:fetch_keys(hd(Maps)), tl(Maps)), - join_maps(Keys, Maps, dict:new()). + Keys = constrained_keys(Maps), + join_maps(Keys, Maps, map_new()). + +constrained_keys(Maps) -> + lists:foldl(fun(TmpMap, AccKeys) -> + [Key || Key <- AccKeys, dict:is_key(Key, TmpMap)] + end, + dict:fetch_keys(hd(Maps)), tl(Maps)). join_maps([Key|Left], Maps = [Map|MapsLeft], AccMap) -> NewType = join_one_key(Key, MapsLeft, lookup_type(Key, Map)), @@ -1994,13 +2572,13 @@ enter_type(Key, Val, Map) when is_integer(Key) -> ?debug("Entering ~s :: ~s\n", [format_type(t_var(Key)), format_type(Val)]), case t_is_any(Val) of true -> - dict:erase(Key, Map); + erase_type(Key, Map); false -> LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT), case dict:find(Key, Map) of {ok, LimitedVal} -> Map; - {ok, _} -> dict:store(Key, LimitedVal, Map); - error -> dict:store(Key, LimitedVal, Map) + {ok, _} -> map_store(Key, LimitedVal, Map); + error -> map_store(Key, LimitedVal, Map) end end; enter_type(Key, Val, Map) -> @@ -2008,13 +2586,13 @@ enter_type(Key, Val, Map) -> KeyName = t_var_name(Key), case t_is_any(Val) of true -> - dict:erase(KeyName, Map); + erase_type(KeyName, Map); false -> LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT), case dict:find(KeyName, Map) of {ok, LimitedVal} -> Map; - {ok, _} -> dict:store(KeyName, LimitedVal, Map); - error -> dict:store(KeyName, LimitedVal, Map) + {ok, _} -> map_store(KeyName, LimitedVal, Map); + error -> map_store(KeyName, LimitedVal, Map) end end. @@ -2024,11 +2602,25 @@ enter_type_lists([Key|KeyTail], [Val|ValTail], Map) -> enter_type_lists([], [], Map) -> Map. -enter_type_list([{Key, Val}|Tail], Map) -> +enter_type_list(KeyVals, Map) -> + enter_type_list(KeyVals, Map, []). + +enter_type_list([{Key, Val}|Tail], Map, U0) -> + {Map1,U1} = enter_type2(Key, Val, Map), + enter_type_list(Tail, Map1, U1++U0); +enter_type_list([], Map, U) -> + {Map, ordsets:from_list(U)}. + +enter_type2(Key, Val, Map) -> Map1 = enter_type(Key, Val, Map), - enter_type_list(Tail, Map1); -enter_type_list([], Map) -> - Map. + {Map1, [Key || not is_same(Key, Map, Map1)]}. + +map_store(Key, Val, Map) -> + ?debug("Storing ~w :: ~s\n", [Key, format_type(Val)]), + dict:store(Key, Val, Map). + +erase_type(Key, Map) -> + dict:erase(Key, Map). lookup_type_list(List, Map) -> [lookup_type(X, Map) || X <- List]. @@ -2079,22 +2671,41 @@ mk_var_no_lit(Var) -> mk_var_no_lit_list(List) -> [mk_var_no_lit(X) || X <- List]. +updated_vars_only(U, OldMap, NewMap) -> + [V || V <- U, not is_same(V, OldMap, NewMap)]. + +is_same(Key, Map1, Map2) -> + t_is_equal(lookup_type(Key, Map1), lookup_type(Key, Map2)). + +pp_map(_S, _Map) -> + ?debug("\t~s: ~p\n", + [_S, [{X, lists:flatten(format_type(Y))} || + {X, Y} <- lists:keysort(1, dict:to_list(_Map))]]). + %% ============================================================================ %% %% The State. %% %% ============================================================================ -new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes) -> +new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes, Solvers) -> List = [{MFA, Var} || {MFA, {Var, _Fun}, _Rec} <- SCC0], NameMap = dict:from_list(List), MFAs = [MFA || {MFA, _Var} <- List], SCC = [mk_var(Fun) || {_MFA, {_Var, Fun}, _Rec} <- SCC0], - SelfRecs = [F || F <- SCC, - dialyzer_callgraph:is_self_rec(t_var_name(F), CallGraph)], + SelfRec = + case SCC of + [OneF] -> + Label = t_var_name(OneF), + case dialyzer_callgraph:is_self_rec(Label, CallGraph) of + true -> OneF; + false -> false + end; + _Many -> false + end, #state{callgraph = CallGraph, name_map = NameMap, next_label = NextLabel, - prop_types = PropTypes, plt = Plt, scc = ordsets:from_list(SCC), - mfas = MFAs, self_recs = ordsets:from_list(SelfRecs)}. + prop_types = {d, PropTypes}, plt = Plt, scc = ordsets:from_list(SCC), + mfas = MFAs, self_rec = SelfRec, solvers = Solvers}. state__set_rec_dict(State, RecDict) -> State#state{records = RecDict}. @@ -2193,14 +2804,21 @@ state__plt(#state{plt = PLT}) -> state__new_constraint_context(State) -> State#state{cs = []}. -state__prop_domain(FunLabel, #state{prop_types = PropTypes}) -> +state__prop_domain(FunLabel, #state{prop_types = {e, ETSPropTypes}}) -> + try ets:lookup_element(ETSPropTypes, FunLabel, 2) of + {_Range_Fun, Dom} -> {ok, Dom}; + FunType -> {ok, t_fun_args(FunType)} + catch + _:_ -> error + end; +state__prop_domain(FunLabel, #state{prop_types = {d, PropTypes}}) -> case dict:find(FunLabel, PropTypes) of error -> error; {ok, {_Range_Fun, Dom}} -> {ok, Dom}; {ok, FunType} -> {ok, t_fun_args(FunType)} end. -state__add_prop_constrs(Tree, #state{prop_types = PropTypes} = State) -> +state__add_prop_constrs(Tree, #state{prop_types = {d, PropTypes}} = State) -> Label = cerl_trees:get_label(Tree), case dict:find(Label, PropTypes) of error -> State; @@ -2263,21 +2881,17 @@ state__mk_vars(N, #state{next_label = NL} = State) -> Vars = [t_var(X) || X <- lists:seq(NL, NewLabel-1)], {State#state{next_label = NewLabel}, Vars}. -state__store_constrs(Id, Cs, #state{cmap = Dict} = State) -> +state__store_constrs(Id, Cs, #state{cmap = {d, Dict}} = State) -> NewDict = dict:store(Id, Cs, Dict), - State#state{cmap = NewDict}. + State#state{cmap = {d, NewDict}}. -state__get_cs(Var, #state{cmap = Dict}) -> +state__get_cs(Var, #state{cmap = {e, ETSDict}}) -> + ets:lookup_element(ETSDict, Var, 2); +state__get_cs(Var, #state{cmap = {d, Dict}}) -> dict:fetch(Var, Dict). -%% The functions here will not be treated as self recursive. -%% These functions will need to be handled as such manually. -state__mark_as_non_self_rec(SCC, #state{self_recs = SelfRecs} = State) -> - %% TODO: Check if the result is always empty and just set it to [] if so. - State#state{self_recs = ordsets:subtract(SelfRecs, ordsets:from_list(SCC))}. - -state__is_self_rec(Fun, #state{self_recs = SelfRecs}) -> - ordsets:is_element(Fun, SelfRecs). +state__is_self_rec(Fun, #state{self_rec = SelfRec}) -> + Fun =:= SelfRec. state__store_funs(Vars0, Funs0, #state{fun_map = Map} = State) -> debug_make_name_map(Vars0, Funs0), @@ -2314,7 +2928,7 @@ mk_constraint(Lhs, Op, Rhs) -> case Deps =:= [] of true -> %% This constraint is constant. Solve it immediately. - case solve_one_c(C, dict:new(), []) of + case solve_one_c(C, map_new(), []) of error -> throw(error); _ -> %% This is always true, keep it anyway for logistic reasons @@ -2337,8 +2951,9 @@ constraint_opnd_is_any(Type) -> t_is_any(Type). -ifdef(DEBUG). --spec mk_fun_var(fun((_) -> erl_types:erl_type()), [erl_types:erl_type()], - integer()) -> #fun_var{}. +-spec mk_fun_var(integer(), + fun((_) -> erl_types:erl_type()), + [erl_types:erl_type()]) -> #fun_var{}. mk_fun_var(Line, Fun, Types) -> Deps = [t_var_name(Var) || Var <- t_collect_vars(t_product(Types))], @@ -2386,7 +3001,9 @@ mk_constraints([], _Op, []) -> []. mk_constraint_ref(Id, Deps) -> - #constraint_ref{id = Id, deps = Deps}. + %% See also solve_ref_or_list(), #constraint_ref{}. + Ds = ordsets:del_element(t_var_name(Id), Deps), + #constraint_ref{id = Id, deps = Ds}. mk_constraint_list(Type, List) -> List1 = ordsets:from_list(lift_lists(Type, List)), @@ -2523,19 +3140,21 @@ enumerate_constraints([#constraint_ref{id = Id} = C|Tail], N, Acc, State) -> enumerate_constraints([#constraint_list{type = conj, list = List} = C|Tail], N, Acc, State) -> %% Separate the flat constraints from the deep ones to make a - %% separate fixpoint interation over the flat ones for speed. - {Flat, Deep} = lists:splitwith(fun(#constraint{}) -> true; + %% separate fixpoint iteration over the flat ones for speed. + {Flat, Deep} = lists:partition(fun(#constraint{}) -> true; (#constraint_list{}) -> false; (#constraint_ref{}) -> false end, List), {NewFlat, N1, State1} = enumerate_constraints(Flat, N, [], State), {NewDeep, N2, State2} = enumerate_constraints(Deep, N1, [], State1), {NewList, N3} = - case shorter_than_two(NewFlat) orelse (NewDeep =:= []) of - true -> {NewFlat ++ NewDeep, N2}; - false -> - {NewCLists, TmpN} = group_constraints_in_components(NewFlat, N2), - {NewCLists ++ NewDeep, TmpN} + if + NewFlat =:= [] -> {NewDeep, N2}; + NewDeep =:= [] -> {NewFlat, N2}; + true -> + TmpCList = mk_conj_constraint_list(NewFlat), + {[TmpCList#constraint_list{id = {list, N2}}| NewDeep], + N2 + 1} end, NewAcc = [C#constraint_list{list = NewList, id = {list, N3}}|Acc], enumerate_constraints(Tail, N3+1, NewAcc, State2); @@ -2549,42 +3168,6 @@ enumerate_constraints([#constraint{} = C|Tail], N, Acc, State) -> enumerate_constraints([], N, Acc, State) -> {lists:reverse(Acc), N, State}. -shorter_than_two([]) -> true; -shorter_than_two([_]) -> true; -shorter_than_two([_|_]) -> false. - -group_constraints_in_components(Cs, N) -> - DepList = [Deps || #constraint{deps = Deps} <- Cs], - case find_dep_components(DepList, []) of - [_] -> {Cs, N}; - [_|_] = Components -> - ConstrComp = [[C || #constraint{deps = D} = C <- Cs, - ordsets:is_subset(D, Comp)] - || Comp <- Components], - lists:mapfoldl(fun(CComp, TmpN) -> - TmpCList = mk_conj_constraint_list(CComp), - {TmpCList#constraint_list{id = {list, TmpN}}, - TmpN + 1} - end, N, ConstrComp) - end. - -find_dep_components([Set|Left], AccComponents) -> - {Component, Ungrouped} = find_dep_components(Left, Set, []), - case Component =:= Set of - true -> find_dep_components(Ungrouped, [Component|AccComponents]); - false -> find_dep_components([Component|Ungrouped], AccComponents) - end; -find_dep_components([], AccComponents) -> - AccComponents. - -find_dep_components([Set|Left], AccSet, Ungrouped) -> - case ordsets:intersection(Set, AccSet) of - [] -> find_dep_components(Left, AccSet, [Set|Ungrouped]); - [_|_] -> find_dep_components(Left, ordsets:union(Set, AccSet), Ungrouped) - end; -find_dep_components([], AccSet, Ungrouped) -> - {AccSet, Ungrouped}. - %% Put the fun ref constraints last in any conjunction since we need %% to separate the environment from the interior of the function. order_fun_constraints(State) -> @@ -2615,7 +3198,9 @@ order_fun_constraints([#constraint_list{list = List, type = Type} = C|Tail], end, lists:mapfoldl(FoldFun, State, List) end, - NewAcc = [update_constraint_list(C, NewList)|Acc], + C1 = update_constraint_list(C, NewList), + Masks = calculate_masks(NewList, 1, []), + NewAcc = [update_masks(C1, Masks)|Acc], order_fun_constraints(Tail, Funs, NewAcc, NewState); order_fun_constraints([#constraint{} = C|Tail], Funs, Acc, State) -> order_fun_constraints(Tail, Funs, [C|Acc], State); @@ -2623,6 +3208,22 @@ order_fun_constraints([], Funs, Acc, State) -> NewState = order_fun_constraints(Funs, State), {lists:reverse(Acc)++Funs, NewState}. +update_masks(C, Masks) -> + C#constraint_list{masks = Masks}. + +-define(VARS_LIMIT, 50). + +calculate_masks([C|Cs], I, L0) -> + calculate_masks(Cs, I+1, [{V, I} || V <- get_deps(C)] ++ L0); +calculate_masks([], _I, L) -> + M = family(L), + case length(M) > ?VARS_LIMIT of + true -> + {d, dict:from_list(M)}; + false -> + M + end. + %% ============================================================================ %% %% Utilities. @@ -2700,6 +3301,9 @@ lookup_record(Records, Tag, Arity) -> error end. +family(L) -> + sofs:to_external(sofs:rel2fam(sofs:relation(L))). + %% ============================================================================ %% %% Pretty printer and debug facilities. @@ -2714,13 +3318,24 @@ lookup_record(Records, Tag, Arity) -> -ifdef(DEBUG). format_type(#fun_var{deps = Deps, origin = Origin}) -> - io_lib:format("Fun@L~p(~s)", - [Origin, lists:flatten([format_type(t_var(X))||X<-Deps])]); + L = [format_type(t_var(X)) || X <- Deps], + io_lib:format("Fun@L~p(~s)", [Origin, join_chars(L, ",")]); format_type(Type) -> case cerl:is_literal(Type) of true -> io_lib:format("~w", [cerl:concrete(Type)]); false -> erl_types:t_to_string(Type) end. + +join_chars([], _Sep) -> + []; +join_chars([H|T], Sep) -> + [H|[[Sep,X] || X <- T]]. + +debug_lookup_name(Var) -> + case dict:find(t_var_name(Var), get(dialyzer_typesig_map)) of + error -> Var; + {ok, Name} -> Name + end. -endif. -ifdef(DEBUG_NAME_MAP). @@ -2739,12 +3354,6 @@ debug_make_name_map([Var|VarLeft], [Fun|FunLeft], Map) -> debug_make_name_map([], [], Map) -> Map. -debug_lookup_name(Var) -> - case dict:find(t_var_name(Var), get(dialyzer_typesig_map)) of - error -> Var; - {ok, Name} -> Name - end. - -else. debug_make_name_map(_Vars, _Funs) -> ok. @@ -2755,51 +3364,55 @@ pp_constrs_scc(SCC, State) -> [pp_constrs(Fun, state__get_cs(Fun, State), State) || Fun <- SCC]. pp_constrs(Fun, Cs, State) -> - io:format("Constraints for fun: ~w\n", [debug_lookup_name(Fun)]), + io:format("Constraints for fun: ~w", [debug_lookup_name(Fun)]), MaxDepth = pp_constraints(Cs, State), io:format("Depth: ~w\n", [MaxDepth]). pp_constraints(Cs, State) -> - Res = pp_constraints([Cs], none, 0, 0, State), + Res = pp_constraints([Cs], 0, 0, State), io:nl(), Res. -pp_constraints([List|Tail], Separator, Level, MaxDepth, - State) when is_list(List) -> - pp_constraints(List++Tail, Separator, Level, MaxDepth, State); -pp_constraints([#constraint_ref{id = Id}|Left], Separator, - Level, MaxDepth, State) -> +pp_constraints([List|Tail], Level, MaxDepth, State) when is_list(List) -> + pp_constraints(List++Tail, Level, MaxDepth, State); +pp_constraints([#constraint_ref{id = Id}|Left], Level, MaxDepth, State) -> Cs = state__get_cs(Id, State), + pp_indent(Level), io:format("%Ref ~w%", [t_var_name(Id)]), - pp_constraints([Cs|Left], Separator, Level, MaxDepth, State); -pp_constraints([#constraint{lhs = Lhs, op = Op, rhs = Rhs}], _Separator, - Level, MaxDepth, _State) -> - io:format("~s ~w ~s", [format_type(Lhs), Op, format_type(Rhs)]), + pp_constraints([Cs|Left], Level, MaxDepth, State); +pp_constraints([#constraint{}=C], Level, MaxDepth, _State) -> + pp_op(C, Level), erlang:max(Level, MaxDepth); -pp_constraints([#constraint{lhs = Lhs, op = Op, rhs = Rhs}|Tail], Separator, - Level, MaxDepth, State) -> - io:format("~s ~w ~s ~s ", [format_type(Lhs), Op, format_type(Rhs),Separator]), - pp_constraints(Tail, Separator, Level, MaxDepth, State); +pp_constraints([#constraint{}=C|Tail], Level, MaxDepth, State) -> + pp_op(C, Level), + pp_constraints(Tail, Level, MaxDepth, State); pp_constraints([#constraint_list{type = Type, list = List, id = Id}], - _Separator, Level, MaxDepth, State) -> - io:format("%List ~w(", [Id]), - NewSeparator = case Type of - conj -> "*"; - disj -> "+" - end, - NewMaxDepth = pp_constraints(List, NewSeparator, Level + 1, MaxDepth, State), + Level, MaxDepth, State) -> + pp_indent(Level), + case Type of + conj -> io:format("Conj ~w (", [Id]); + disj -> io:format("Disj ~w (", [Id]) + end, + NewMaxDepth = pp_constraints(List, Level + 1, MaxDepth, State), io:format(")", []), NewMaxDepth; pp_constraints([#constraint_list{type = Type, list = List, id = Id}|Tail], - Separator, Level, MaxDepth, State) -> - io:format("List ~w(", [Id]), - NewSeparator = case Type of - conj -> "*"; - disj -> "+" - end, - NewMaxDepth = pp_constraints(List, NewSeparator, Level+1, MaxDepth, State), - io:format(") ~s\n~s ", [Separator, Separator]), - pp_constraints(Tail, Separator, Level, NewMaxDepth, State). + Level, MaxDepth, State) -> + pp_indent(Level), + case Type of + conj -> io:format("Conj ~w (", [Id]); + disj -> io:format("Disj ~w (", [Id]) + end, + NewMaxDepth = pp_constraints(List, Level+1, MaxDepth, State), + io:format(")", []), + pp_constraints(Tail, Level, NewMaxDepth, State). + +pp_op(#constraint{lhs = Lhs, op = Op, rhs = Rhs}, Level) -> + pp_indent(Level), + io:format("~s ~w ~s", [format_type(Lhs), Op, format_type(Rhs)]). + +pp_indent(Level) -> + io:format("\n~*s", [Level*2, ""]). -else. pp_constrs_scc(_SCC, _State) -> ok. diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl index 2a248fb028..149e777e1f 100644 --- a/lib/dialyzer/src/dialyzer_utils.erl +++ b/lib/dialyzer/src/dialyzer_utils.erl @@ -43,7 +43,8 @@ pp_hook/0, process_record_remote_types/1, sets_filter/2, - src_compiler_opts/0 + src_compiler_opts/0, + parallelism/0 ]). -include("dialyzer.hrl"). @@ -536,3 +537,12 @@ pp_unit(Unit, Ctxt, Cont) -> pp_atom(Atom) -> String = atom_to_list(cerl:atom_val(Atom)), prettypr:text(String). + +%%------------------------------------------------------------------------------ + +-spec parallelism() -> integer(). + +parallelism() -> + CPUs = erlang:system_info(logical_processors_available), + Schedulers = erlang:system_info(schedulers), + min(CPUs, Schedulers). diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl new file mode 100644 index 0000000000..50b2e31ed8 --- /dev/null +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -0,0 +1,189 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2010. 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(dialyzer_worker). + +-export([launch/4, sequential/4]). + +-export_type([worker/0]). + +-type worker() :: pid(). %%opaque + +-type mode() :: dialyzer_coordinator:mode(). +-type coordinator() :: dialyzer_coordinator:coordinator(). +-type init_data() :: dialyzer_coordinator:init_data(). +-type result() :: dialyzer_coordinator:result(). + +-record(state, { + mode :: mode(), + job :: mfa_or_funlbl() | file:filename(), + coordinator :: coordinator(), + init_data :: init_data(), + depends_on = [] :: list() + }). + +-include("dialyzer.hrl"). + +%% -define(DEBUG, true). + +-ifdef(DEBUG). +-define(debug(X__, Y__), io:format(X__, Y__)). +-else. +-define(debug(X__, Y__), ok). +-endif. + +%%-------------------------------------------------------------------- + +-spec launch(mode(), [mfa_or_funlbl()], init_data(), coordinator()) -> worker(). + +launch(Mode, Job, InitData, Coordinator) -> + State = #state{mode = Mode, + job = Job, + init_data = InitData, + coordinator = Coordinator}, + InitState = + case Mode of + X when X =:= 'typesig'; X =:= 'dataflow' -> initializing; + X when X =:= 'compile'; X =:= 'warnings' -> running + end, + spawn_link(fun() -> loop(InitState, State) end). + +%%-------------------------------------------------------------------- + +loop(updating, State) -> + ?debug("Update: ~p\n",[State#state.job]), + NextStatus = + case waits_more_success_typings(State) of + true -> waiting; + false -> running + end, + loop(NextStatus, State); +loop(initializing, #state{job = SCC, init_data = InitData} = State) -> + DependsOn = dialyzer_succ_typings:find_depends_on(SCC, InitData), + ?debug("Deps ~p: ~p\n",[State#state.job, DependsOn]), + loop(updating, State#state{depends_on = DependsOn}); +loop(waiting, State) -> + ?debug("Wait: ~p\n",[State#state.job]), + NewState = wait_for_success_typings(State), + loop(updating, NewState); +loop(running, #state{mode = 'compile'} = State) -> + dialyzer_coordinator:wait_activation(), + ?debug("Compile: ~s\n",[State#state.job]), + Result = + case start_compilation(State) of + {ok, EstimatedSize, Data} -> + Label = ask_coordinator_for_label(EstimatedSize, State), + continue_compilation(Label, Data); + {error, _Reason} = Error -> + Error + end, + report_to_coordinator(Result, State); +loop(running, #state{mode = 'warnings'} = State) -> + dialyzer_coordinator:wait_activation(), + ?debug("Warning: ~s\n",[State#state.job]), + Result = collect_warnings(State), + report_to_coordinator(Result, State); +loop(running, #state{mode = Mode} = State) when + Mode =:= 'typesig'; Mode =:= 'dataflow' -> + request_activation(State), + ?debug("Run: ~p\n",[State#state.job]), + NotFixpoint = do_work(State), + ok = broadcast_done(State), + report_to_coordinator(NotFixpoint, State). + +waits_more_success_typings(#state{depends_on = Depends}) -> + Depends =/= []. + +broadcast_done(#state{job = SCC, init_data = InitData, + coordinator = Coordinator}) -> + RequiredBy = dialyzer_succ_typings:find_required_by(SCC, InitData), + {Callers, Unknown} = + dialyzer_coordinator:sccs_to_pids(RequiredBy, Coordinator), + send_done(Callers, SCC), + continue_broadcast_done(Unknown, SCC, Coordinator). + +send_done(Callers, SCC) -> + ?debug("Sending ~p: ~p\n",[SCC, Callers]), + SendSTFun = fun(PID) -> PID ! {done, SCC} end, + lists:foreach(SendSTFun, Callers). + +continue_broadcast_done([], _SCC, _Coordinator) -> ok; +continue_broadcast_done(Rest, SCC, Coordinator) -> + %% This time limit should be greater than the time required + %% by the coordinator to spawn all processes. + timer:sleep(500), + {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids(Rest, Coordinator), + send_done(Callers, SCC), + continue_broadcast_done(Unknown, SCC, Coordinator). + +wait_for_success_typings(#state{depends_on = DependsOn} = State) -> + receive + {done, SCC} -> + ?debug("GOT ~p: ~p\n",[State#state.job, SCC]), + State#state{depends_on = DependsOn -- [SCC]} + after + 5000 -> + ?debug("Still Waiting ~p: ~p\n",[State#state.job, DependsOn]), + State + end. + +request_activation(#state{coordinator = Coordinator}) -> + dialyzer_coordinator:request_activation(Coordinator). + +do_work(#state{mode = Mode, job = Job, init_data = InitData}) -> + case Mode of + typesig -> dialyzer_succ_typings:find_succ_types_for_scc(Job, InitData); + dataflow -> dialyzer_succ_typings:refine_one_module(Job, InitData) + end. + +report_to_coordinator(Result, #state{job = Job, coordinator = Coordinator}) -> + ?debug("Done: ~p\n",[Job]), + dialyzer_coordinator:job_done(Job, Result, Coordinator). + +start_compilation(#state{job = Job, init_data = InitData}) -> + dialyzer_analysis_callgraph:start_compilation(Job, InitData). + +ask_coordinator_for_label(EstimatedSize, #state{coordinator = Coordinator}) -> + dialyzer_coordinator:get_next_label(EstimatedSize, Coordinator). + +continue_compilation(Label, Data) -> + dialyzer_analysis_callgraph:continue_compilation(Label, Data). + +collect_warnings(#state{job = Job, init_data = InitData}) -> + dialyzer_succ_typings:collect_warnings(Job, InitData). + +%%------------------------------------------------------------------------------ + +-type extra() :: label() | 'unused'. + +-spec sequential(mode(), [mfa_or_funlbl()], init_data(), extra()) -> result(). + +sequential('compile', Job, InitData, Extra) -> + case dialyzer_analysis_callgraph:start_compilation(Job, InitData) of + {ok, EstimatedSize, Data} -> + {EstimatedSize, continue_compilation(Extra, Data)}; + {error, _Reason} = Error -> {0, Error} + end; +sequential('typesig', Job, InitData, _Extra) -> + dialyzer_succ_typings:find_succ_types_for_scc(Job, InitData); +sequential('dataflow', Job, InitData, _Extra) -> + dialyzer_succ_typings:refine_one_module(Job, InitData); +sequential('warnings', Job, InitData, _Extra) -> + dialyzer_succ_typings:collect_warnings(Job, InitData). diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile index 6a1abce943..9f8a3f1194 100644 --- a/lib/dialyzer/test/Makefile +++ b/lib/dialyzer/test/Makefile @@ -25,10 +25,10 @@ RELSYSDIR = $(RELEASE_PATH)/dialyzer_test include $(ERL_TOP)/make/otp_release_targets.mk release_tests_spec: - $(INSTALL_DIR) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - $(INSTALL_DATA) $(AUXILIARY_FILES) $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) - cd $(RELSYSDIR);\ + $(INSTALL_DIR) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + $(INSTALL_DATA) $(AUXILIARY_FILES) "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + cd "$(RELSYSDIR)";\ erlc dialyzer_common.erl file_utils.erl;\ erl -noshell -run dialyzer_common create_all_suites -s erlang halt diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/queue b/lib/dialyzer/test/opaque_SUITE_data/results/queue index 59ce33f098..c3f04ea64d 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/queue +++ b/lib/dialyzer/test/opaque_SUITE_data/results/queue @@ -5,6 +5,7 @@ queue_use.erl:27: The attempt to match a term of type queue() against the patter queue_use.erl:33: Attempt to test for equality between a term of type {[42,...],[]} and a term of opaque type queue() queue_use.erl:36: The attempt to match a term of type queue() against the pattern {F, _R} breaks the opaqueness of the term queue_use.erl:40: The call queue:out({[42,...],[]}) does not have an opaque term of type queue() as 1st argument +queue_use.erl:48: The call queue_use:add_unique(42,#db{p::[],q::queue()}) contains an opaque term as 2nd argument when terms of different types are expected in these positions queue_use.erl:51: The call queue_use:is_in_queue(E::42,DB::#db{p::[],q::queue()}) contains an opaque term as 2nd argument when terms of different types are expected in these positions queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue()} against the pattern {'db', _, {L1, L2}} breaks the opaqueness of queue() queue_use.erl:62: The call queue_use:tuple_queue({42,'gazonk'}) does not have a term of type {_,queue()} (with opaque subterms) as 1st argument diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes new file mode 100644 index 0000000000..8dc0361b0d --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes @@ -0,0 +1,31 @@ + +contracts_with_subtypes.erl:106: The call contracts_with_subtypes:rec_arg({'a','b'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:107: The call contracts_with_subtypes:rec_arg({'b','a'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:108: The call contracts_with_subtypes:rec_arg({'a',{'b','a'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:109: The call contracts_with_subtypes:rec_arg({'b',{'a','b'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:110: The call contracts_with_subtypes:rec_arg({'a',{'b',{'a','b'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:111: The call contracts_with_subtypes:rec_arg({'b',{'a',{'b','a'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:142: The pattern 1 can never match the type binary() | string() +contracts_with_subtypes.erl:145: The pattern 'alpha' can never match the type {'ok',X} | {'ok',X,binary() | string()} +contracts_with_subtypes.erl:147: The pattern 42 can never match the type {'ok',_} | {'ok',_,binary() | string()} +contracts_with_subtypes.erl:163: The pattern 'alpha' can never match the type {'ok',X} +contracts_with_subtypes.erl:165: The pattern 42 can never match the type {'ok',X} +contracts_with_subtypes.erl:183: The pattern 'alpha' can never match the type {'ok',X} +contracts_with_subtypes.erl:185: The pattern 42 can never match the type {'ok',X} +contracts_with_subtypes.erl:202: The pattern 1 can never match the type binary() | string() +contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',X,binary() | string()} +contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',X,binary() | string()} +contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',X,binary() | string()} +contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',X,binary() | string()} +contracts_with_subtypes.erl:234: Function flat_ets_new_t/0 has no local return +contracts_with_subtypes.erl:235: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed') +contracts_with_subtypes.erl:23: Invalid type specification for function contracts_with_subtypes:extract2/0. The success typing is () -> 'something' +contracts_with_subtypes.erl:261: Function factored_ets_new_t/0 has no local return +contracts_with_subtypes.erl:262: The call contracts_with_subtypes:factored_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,Type | Access | 'named_table' | {'keypos',Pos} | {'heir',Pid::pid(),HeirData} | {'heir','none'} | Tweaks), is_subtype(Type,type()), is_subtype(Access,access()), is_subtype(Tweaks,{'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed'), is_subtype(Pos,pos_integer()), is_subtype(HeirData,term()) +contracts_with_subtypes.erl:77: The call contracts_with_subtypes:foo1(5) breaks the contract (Arg1) -> Res when is_subtype(Arg1,atom()), is_subtype(Res,atom()) +contracts_with_subtypes.erl:78: The call contracts_with_subtypes:foo2(5) breaks the contract (Arg1) -> Res when is_subtype(Arg1,Arg2), is_subtype(Arg2,atom()), is_subtype(Res,atom()) +contracts_with_subtypes.erl:79: The call contracts_with_subtypes:foo3(5) breaks the contract (Arg1) -> Res when is_subtype(Arg2,atom()), is_subtype(Arg1,Arg2), is_subtype(Res,atom()) +contracts_with_subtypes.erl:7: Invalid type specification for function contracts_with_subtypes:extract/0. The success typing is () -> 'something' +contracts_with_subtypes.erl:80: The call contracts_with_subtypes:foo4(5) breaks the contract (Type) -> Type when is_subtype(Type,atom()) +contracts_with_subtypes.erl:81: The call contracts_with_subtypes:foo5(5) breaks the contract (Type::atom()) -> Type::atom() +contracts_with_subtypes.erl:82: The call contracts_with_subtypes:foo6(5) breaks the contract (Type) -> Type when is_subtype(Type,atom()) diff --git a/lib/dialyzer/test/small_SUITE_data/results/port_info_test b/lib/dialyzer/test/small_SUITE_data/results/port_info_test index 9ee863f9eb..863a3d61df 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/port_info_test +++ b/lib/dialyzer/test/small_SUITE_data/results/port_info_test @@ -3,4 +3,5 @@ port_info_test.erl:10: The pattern {'connected', 42} can never match the type 'u port_info_test.erl:14: The pattern {'registered_name', "42"} can never match the type 'undefined' | {'registered_name',atom()} port_info_test.erl:19: The pattern {'output', 42} can never match the type 'undefined' | {'connected',pid()} port_info_test.erl:24: Guard test 'links' =:= Atom::'connected' can never succeed -port_info_test.erl:28: The pattern {'gazonk', _} can never match the type 'undefined' | {'connected' | 'id' | 'input' | 'links' | 'name' | 'output' | 'registered_name',atom() | pid() | [pid() | char()] | integer()} +port_info_test.erl:28: The pattern {'gazonk', _} can never match the type 'undefined' | {'connected' | 'id' | 'input' | 'links' | 'name' | 'os_pid' | 'output' | 'registered_name',atom() | pid() | [pid() | char()] | integer()} +port_info_test.erl:32: The pattern {'os_pid', "42"} can never match the type 'undefined' | {'os_pid','undefined' | non_neg_integer()} diff --git a/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/a.erl b/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/a.erl new file mode 100644 index 0000000000..7efe870b0d --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/a.erl @@ -0,0 +1,9 @@ +-module(a). +-export([g/1]). + +-export_type([a/0, t/0]). +-type a() :: integer(). +-type t() :: a() | maybe_improper_list(t(), t()). + +-spec g(t()) -> t(). +g(X) -> X. diff --git a/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/b.erl b/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/b.erl new file mode 100644 index 0000000000..b08bc5e66c --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/b.erl @@ -0,0 +1,5 @@ +-module(b). +-export([f/1]). + +-spec f(a:t()) -> a:t(). +f(X) -> a:g(X). diff --git a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl new file mode 100644 index 0000000000..d72138d509 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl @@ -0,0 +1,267 @@ +-module(contracts_with_subtypes). + +-compile(export_all). + +%=============================================================================== + +-spec extract() -> 'ok'. + +extract() -> + case dz_extract() of + {ok, Val} -> Val; + error -> exit(boom) + end. + +-spec dz_extract() -> RetValue when + FileList :: something, + RetValue :: {ok, FileList} | error. + +dz_extract() -> get(foo). + +%------------------------------------------------------------------------------- + +-spec extract2() -> 'ok'. + +extract2() -> + case dz_extract2() of + {ok, Val} -> Val; + error -> exit(boom) + end. + +-spec dz_extract2() -> RetValue when + RetValue :: {ok, FileList} | error, + FileList :: something. + +dz_extract2() -> get(foo). + +%=============================================================================== + +-spec foo1(Arg1) -> Res when + Arg1 :: atom(), + Res :: atom(). + +foo1(X) -> X. + +-spec foo2(Arg1) -> Res when + Arg1 :: Arg2, + Arg2 :: atom(), + Res :: atom(). + +foo2(X) -> X. + +-spec foo3(Arg1) -> Res when + Arg2 :: atom(), + Arg1 :: Arg2, + Res :: atom(). + +foo3(X) -> X. + +-spec foo4(Type) -> Type when is_subtype(Type, atom()). + +foo4(X) -> X. + +-spec foo5(Type :: atom()) -> Type :: atom(). + +foo5(X) -> X. + +-spec foo6(Type) -> Type when Type :: atom(). + +foo6(X) -> X. + +-spec foo7(Type) -> Type. + +foo7(X) -> X. + +%------------------------------------------------------------------------------- + +bar(1) -> foo1(5); +bar(2) -> foo2(5); +bar(3) -> foo3(5); +bar(4) -> foo4(5); +bar(5) -> foo5(5); +bar(6) -> foo6(5); +bar(7) -> foo7(5). + +wrong_foo6() -> + b = foo6(a). + +%=============================================================================== + +-spec rec_arg(Arg) -> ok when + Arg :: {a, A} | {b, B}, + A :: a | {b, B}, + B :: b | {a, A}. + +rec_arg(X) -> get(X). + +c(aa) -> rec_arg({a, a}); +c(bb) -> rec_arg({b, b}); +c(abb) -> rec_arg({a, {b, b}}); +c(baa) -> rec_arg({b, {a, a}}); +c(abaa) -> rec_arg({a, {b, {a, a}}}); +c(babb) -> rec_arg({b, {a, {b, b}}}); +c(ababb) -> rec_arg({a, {b, {a, {b, b}}}}); +c(babaa) -> rec_arg({b, {a, {b, {a, a}}}}). + +w(ab) -> rec_arg({a, b}); +w(ba) -> rec_arg({b, a}); +w(aba) -> rec_arg({a, {b, a}}); +w(bab) -> rec_arg({b, {a, b}}); +w(abab) -> rec_arg({a, {b, {a, b}}}); +w(baba) -> rec_arg({b, {a, {b, a}}}); +w(ababa) -> rec_arg({a, {b, {a, {b, a}}}}); +w(babab) -> rec_arg({b, {a, {b, {a, b}}}}). + +%=============================================================================== + +-type dublo(X) :: {X, X}. + +-type weird(X,Y) :: {X, Y, X, X}. + +-spec forfun(dublo(Var)) -> ok when Var :: atom(). + +forfun(_) -> ok. + +-spec forfun2(weird(Var, Var)) -> ok when Var :: atom(). + +forfun2(_) -> ok. + +%=============================================================================== + +-spec shallow(X) -> {ok, X} | {ok, X, file:filename()} | err1 | err2. + +shallow(X) -> get(X). + +st(X) when is_atom(X) -> + case shallow(X) of + err1 -> ok; + err2 -> ok; + {ok, X} -> ok; + {ok, X, Res} -> + case Res of + 1 -> bad; + _Other -> ok + end; + alpha -> bad; + {ok, 42} -> bad; + 42 -> bad + end. + +%------------------------------------------------------------------------------- + +-spec deep(X) -> Ret when + Ret :: {ok, X} | Err, + Err :: err1 | err2. + +deep(X) -> get(X). + +dt(X) when is_atom(X) -> + case deep(X) of + err1 -> ok; + err2 -> ok; + {ok, X} -> ok; + alpha -> bad; + {ok, 42} -> bad; + 42 -> bad + end. + +%------------------------------------------------------------------------------- + +-type local_errors() :: err1 | err2. + +-spec deep2(X) -> Ret when + Ret :: {ok, X} | Err, + Err :: local_errors(). + +deep2(X) -> get(X). + +dt2(X) when is_atom(X) -> + case deep2(X) of + err1 -> ok; + err2 -> ok; + {ok, X} -> ok; + alpha -> bad; + {ok, 42} -> bad; + 42 -> bad + end. + +%------------------------------------------------------------------------------- + +-spec deep3(X) -> Ret when + Ret :: {ok, X, file:filename()} | Err, + Err :: local_errors(). + +deep3(X) -> get(X). + +dt3(X) when is_atom(X) -> + case deep3(X) of + err1 -> ok; + err2 -> ok; + {ok, X, Res} -> + case Res of + 1 -> bad; + _Other -> ok + end; + {ok, X} -> bad; + alpha -> bad; + {ok, 42} -> bad; + 42 -> bad + end. + +%=============================================================================== + +-spec flat_ets_new(Name, Options) -> atom() when + Name :: atom(), + Options :: [Option], + Option :: set + | ordered_set + | bag + | duplicate_bag + | public + | protected + | private + | named_table + | {keypos, integer()} + | {heir, pid(), term()} + | {heir, none} + | {write_concurrency, boolean()} + | {read_concurrency, boolean()} + | compressed. + +flat_ets_new(Name, Options) -> + get({Name, Options}). + +flat_ets_new_t() -> + flat_ets_new(12,[]), + flat_ets_new({a,b},[]), + flat_ets_new(name,[foo]), + flat_ets_new(name,{bag}), + flat_ets_new(name,bag), + ok. + +-type access() :: public | protected | private. +-type type() :: set | ordered_set | bag | duplicate_bag. + +-spec factored_ets_new(Name, Options) -> atom() when + Name :: atom(), + Options :: [Option], + Option :: Type | Access | named_table | {keypos,Pos} + | {heir, Pid :: pid(), HeirData} | {heir, none} | Tweaks, + Type :: type(), + Access :: access(), + Tweaks :: {write_concurrency, boolean()} + | {read_concurrency, boolean()} + | compressed, + Pos :: pos_integer(), + HeirData :: term(). + +factored_ets_new(Name, Options) -> + get({Name, Options}). + +factored_ets_new_t() -> + factored_ets_new(12,[]), + factored_ets_new({a,b},[]), + factored_ets_new(name,[foo]), + factored_ets_new(name,{bag}), + factored_ets_new(name,bag), + ok. diff --git a/lib/dialyzer/test/small_SUITE_data/src/deep_lc.erl b/lib/dialyzer/test/small_SUITE_data/src/deep_lc.erl new file mode 100644 index 0000000000..d9ca0817d9 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/deep_lc.erl @@ -0,0 +1,14 @@ +-module(deep_lc). + +-export([t/0]). + +%% This is compile/test/lc_SUITE:deeply_nested/1 +%% +%% Used to be _very_ slow. Unknown how slow, but more than 15 hours. + +t() -> + [[X1,X2,X3,X4,X5,X6,X7(),X8,X9,X10,X11,X12,X13,X14,X15,X16,X17,X18(),X19,X20] || + X1 <- [99],X2 <- [98],X3 <- [97],X4 <- [96],X5 <- [42],X6 <- [17], + X7 <- [fun() -> X5*X5 end],X8 <- [12],X9 <- [11],X10 <- [10], + X11 <- [9],X12 <- [8],X13 <- [7],X14 <- [6],X15 <- [5], + X16 <- [4],X17 <- [3],X18 <- [fun() -> X16+X17 end],X19 <- [2],X20 <- [1]]. diff --git a/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl b/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl index 2ee9a3a6e2..07f22256c9 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl @@ -3,7 +3,7 @@ %% and the quality of the warnings that Dialyzer spits out %% -module(port_info_test). --export([t1/1, t2/1, t3/1, t4/1, t5/2, buggy/1]). +-export([t1/1, t2/1, t3/1, t4/1, t5/2, t6/1, buggy/1]). %% The following errors are correctly caught, but the messages are a bit weird t1(X) when is_port(X) -> @@ -28,6 +28,10 @@ t5(X, Atom) when is_port(X) -> {gazonk, _} = erlang:port_info(X, Atom); t5(_, _) -> ok. +t6(X) when is_port(X) -> + {os_pid, "42"} = erlang:port_info(X, os_pid); +t6(_) -> ok. + %% The type system is not strong enough to catch the following errors buggy(X) when is_atom(X) -> {links, X} = erlang:port_info(foo, X). diff --git a/lib/diameter/autoconf/vxworks/sed.general b/lib/diameter/autoconf/vxworks/sed.general index 77b306aa0a..9199983e16 100644 --- a/lib/diameter/autoconf/vxworks/sed.general +++ b/lib/diameter/autoconf/vxworks/sed.general @@ -66,7 +66,6 @@ s|@HCLIBS@|| s|@ENABLE_ALLOC_TYPE_VARS@|| s|@TERMCAP_LIB@|| s|@ERTS_BUILD_SMP_EMU@|no| -s|@ERTS_BUILD_HYBRID_EMU@|no| s|@HAVE_VALGRIND@|no| s|@EXEEXT@|| s|@WITH_SCTP@|| diff --git a/lib/diameter/doc/src/Makefile b/lib/diameter/doc/src/Makefile index bc3e649e6b..cdf63e7fb6 100644 --- a/lib/diameter/doc/src/Makefile +++ b/lib/diameter/doc/src/Makefile @@ -162,18 +162,18 @@ include $(DIAMETER_TOP)/make/release_targets.mk endif release_docs_spec: $(LOCAL)docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html - $(INSTALL_DIR) $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man4 - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(HTMLDIR)/*.* $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DATA) $(MAN1_FILES) $(RELEASE_PATH)/man/man1 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN4_FILES) $(RELEASE_PATH)/man/man4 - [ -z "$(LOCAL)" ] || cp -r $(HTMLDIR)/js $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(HTMLDIR)/*.* "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4" + [ -z "$(LOCAL)" ] || cp -r $(HTMLDIR)/js "$(RELSYSDIR)/doc/html" echo $(LOCAL) release_spec: diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 93e2603c10..10139b90c7 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -692,6 +692,19 @@ The packet record contains the CEA in question.</p> </taglist> </item> +<tag><c>{watchdog, Ref, PeerRef, {From, To}, Config}</c></tag> +<item> +<code> +Ref = transport_ref() +PeerRef = diameter_app:peer_ref() +From, To = initial | okay | suspect | down | reopen +Config = {connect|listen, [transport_opt()]} +</code> + +<p> +An RFC 3539 watchdog state machine has changed state.</p> +</item> + </taglist> <p> @@ -764,15 +777,45 @@ Defaults to <c>diameter_tcp</c> if unspecified.</p> <p> The interface required of a transport module is documented in <seealso marker="diameter_transport">diameter_transport(3)</seealso>.</p> + +<p> +Multiple <c>transport_module</c> and <c>transport_config</c> +options are allowed. +The order of these is significant in this case (and only in this case), +a <c>transport_module</c> being paired with the first +<c>transport_config</c> following it in the options list, or the +default value for trailing modules. +Transport starts will be attempted with each of the +modules in order until one establishes a connection within the +corresponding timeout (see <c>transport_config</c> below) or all fail.</p> </item> <tag><c>{transport_config, term()}</c></tag> +<tag><c>{transport_config, term(), Unsigned32()}</c></tag> <item> <p> A term passed as the third argument to the <seealso marker="diameter_transport#start">start/3</seealso> function of the relevant <c>transport_module</c> in order to start a transport process. Defaults to the empty list if unspecified.</p> + +<p> +The 3-tuple form additionally specifies an interval, in milliseconds, +after which a started transport process should be terminated if it has +not yet established a connection. +For example, the following options on a connecting transport +request a connection with one peer over SCTP or another +(typically the same) over TCP.</p> + +<code> +{transport_module, diameter_sctp} +{transport_config, SctpOpts, 5000} +{transport_module, diameter_tcp} +{transport_config, TcpOpts} +</code> + +<p> +To listen on both SCTP and TCP, define one transport for each.</p> </item> <tag><c>{applications, [application_alias()]}</c></tag> diff --git a/lib/diameter/examples/code/peer.erl b/lib/diameter/examples/code/peer.erl index b07cd32b98..8fdeba57bf 100644 --- a/lib/diameter/examples/code/peer.erl +++ b/lib/diameter/examples/code/peer.erl @@ -117,6 +117,11 @@ server(T) -> %% %% Return config for a connecting transport. +client({all, LA, RA, RP}) -> + [[M,{K,C}], T] + = [client({P, LA, RA, RP}) || P <- [sctp,tcp]], + [M, {K,C,2000} | T]; + client({T, LA, RA, RP}) -> [{transport_module, tmod(T)}, {transport_config, [{ip, addr(LA)}, diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl index 4273262015..47f9d1240f 100644 --- a/lib/diameter/include/diameter.hrl +++ b/lib/diameter/include/diameter.hrl @@ -139,7 +139,6 @@ init_state, %% option 'state', initial callback state id, %% 32-bit unsigned application identifier = Dict:id() mutable = false, %% boolean(), do traffic callbacks modify state? - answer_errors = report}). %% | callback | discard - %% how to handle containing errors? + options = [{answer_errors, report}]}). %% | callback | discard -endif. %% -ifdef(diameter_hrl). diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index 26f5ae480e..99c343275b 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -220,27 +220,27 @@ endif release_spec: opt for d in bin ebin include src/dict; do \ - $(INSTALL_DIR) $(RELSYSDIR)/$$d; \ + $(INSTALL_DIR) "$(RELSYSDIR)/$$d"; \ done - $(INSTALL_SCRIPT) $(BINS:%=../bin/%) $(RELSYSDIR)/bin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_SCRIPT) $(BINS:%=../bin/%) "$(RELSYSDIR)/bin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(EXTERNAL_HRLS:%=../include/%) $(DICT_HRLS) \ - $(RELSYSDIR)/include - $(INSTALL_DATA) $(DICTS:%=dict/%.dia) $(RELSYSDIR)/src/dict + "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(DICTS:%=dict/%.dia) "$(RELSYSDIR)/src/dict" $(MAKE) $(TARGET_DIRS:%/=release_src_%) $(MAKE) $(EXAMPLE_DIRS:%/=release_examples_%) $(TARGET_DIRS:%/=release_src_%): release_src_%: - $(INSTALL_DIR) $(RELSYSDIR)/src/$* + $(INSTALL_DIR) "$(RELSYSDIR)/src/$*" $(INSTALL_DATA) $(filter $*/%, $(TARGET_MODULES:%=%.erl) \ $(INTERNAL_HRLS)) \ $(filter $*/%, compiler/$(DICT_YRL).yrl) \ - $(RELSYSDIR)/src/$* + "$(RELSYSDIR)/src/$*" $(EXAMPLE_DIRS:%/=release_examples_%): release_examples_%: - $(INSTALL_DIR) $(RELSYSDIR)/examples/$* + $(INSTALL_DIR) "$(RELSYSDIR)/examples/$*" $(INSTALL_DATA) $(patsubst %, ../examples/%, $(filter $*/%, $(EXAMPLES))) \ - $(RELSYSDIR)/examples/$* + "$(RELSYSDIR)/examples/$*" release_docs_spec: diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index b689a83127..4f90b741ae 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -312,6 +312,7 @@ call(SvcName, App, Message) -> -type transport_opt() :: {transport_module, atom()} | {transport_config, any()} + | {transport_config, any(), non_neg_integer() | infinity} | {applications, [app_alias()]} | {capabilities, [capability()]} | {capabilities_cb, evaluable()} diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index fe1212b7e0..421e280422 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -63,9 +63,9 @@ encode(Mod, #diameter_packet{} = Pkt) -> e(Mod, Pkt) catch error: Reason -> - %% Be verbose rather than letting the emulator truncate the - %% error report. - X = {Reason, ?STACK}, + %% Be verbose since a crash report may be truncated and + %% encode errors are self-inflicted. + X = {?MODULE, encode, {Reason, ?STACK}}, diameter_lib:error_report(X, {?MODULE, encode, [Mod, Pkt]}), exit(X) end; @@ -91,7 +91,8 @@ e(_, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) -> Flags = make_flags(0, Hdr), - Pkt#diameter_packet{bin = <<Vsn:8, Length:24, + Pkt#diameter_packet{header = Hdr, + bin = <<Vsn:8, Length:24, Flags:8, Code:24, Aid:32, Hid:32, diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 9253af0de2..e47f63f814 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -519,6 +519,7 @@ rm(SvcName, L) -> Refs = lists:map(fun(#transport{ref = R}) -> R end, L), case stop_transport(SvcName, Refs) of ok -> + diameter_stats:flush(Refs), lists:foreach(fun delete_object/1, L); {error, _} = No -> No @@ -600,7 +601,7 @@ app_acc({application, Opts}, Acc) -> module = init_mod(Mod), init_state = ModS, mutable = init_mutable(M), - answer_errors = init_answers(A)} + options = [{answer_errors, init_answers(A)}]} | Acc]; app_acc(_, Acc) -> Acc. diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl index 3e78c4caef..74ba709aac 100644 --- a/lib/diameter/src/base/diameter_peer.erl +++ b/lib/diameter/src/base/diameter_peer.erl @@ -27,12 +27,15 @@ up/2]). %% ... and the stack. --export([start/3, +-export([start/1, send/2, close/1, abort/1, notify/2]). +%% Old interface only called from old code. +-export([start/3]). %% < diameter-1.2 (R15B02) + %% Server start. -export([start_link/0]). @@ -57,6 +60,11 @@ %% Server state. -record(state, {id = now()}). +%% Default transport_module/config. +-define(DEFAULT_TMOD, diameter_tcp). +-define(DEFAULT_TCFG, []). +-define(DEFAULT_TTMO, infinity). + %%% --------------------------------------------------------------------------- %%% # notify/2 %%% --------------------------------------------------------------------------- @@ -68,9 +76,119 @@ notify(SvcName, T) -> %%% # start/3 %%% --------------------------------------------------------------------------- -start(T, Opts, #diameter_service{} = Svc) -> - {Mod, Cfg} = split_transport(Opts), - apply(Mod, start, [T, Svc, Cfg]). +%% From old code: make is restart. +start(_T, _Opts, #diameter_service{}) -> + {error, restart}. + +%%% --------------------------------------------------------------------------- +%%% # start/1 +%%% --------------------------------------------------------------------------- + +-spec start({T, [Opt], #diameter_service{}}) + -> {TPid, [Addr], Tmo, Data} + | {error, [term()]} + when T :: {connect|accept, diameter:transport_ref()}, + Opt :: diameter:transport_opt(), + TPid :: pid(), + Addr :: inet:ip_address(), + Tmo :: non_neg_integer() | infinity, + Data :: {{T, Mod, Cfg}, [Mod], [{T, [Mod], Cfg}], [Err]}, + Mod :: module(), + Cfg :: term(), + Err :: term() + ; ({#diameter_service{}, Tmo, Data}) + -> {TPid, [Addr], Tmo, Data} + | {error, [term()]} + when TPid :: pid(), + Addr :: inet:ip_address(), + Tmo :: non_neg_integer() | infinity, + Data :: {{T, Mod, Cfg}, [Mod], [{T, [Mod], Cfg}], [Err]}, + T :: {connect|accept, diameter:transport_ref()}, + Mod :: module(), + Cfg :: term(), + Err :: term(). + +%% Initial start. +start({T, Opts, #diameter_service{} = Svc}) -> + start(T, Svc, pair(Opts, [], []), []); + +%% Subsequent start. +start({#diameter_service{} = Svc, Tmo, {{T, _, Cfg}, Ms, Rest, Errs}}) -> + start(T, Ms, Cfg, Svc, Tmo, Rest, Errs). + +%% pair/3 +%% +%% Pair transport modules with config. + +%% Another transport_module: accumulate it. +pair([{transport_module, M} | Rest], Mods, Acc) -> + pair(Rest, [M|Mods], Acc); + +%% Another transport_config: accumulate another tuple. +pair([{transport_config = T, C} | Rest], Mods, Acc) -> + pair([{T, C, ?DEFAULT_TTMO} | Rest], Mods, Acc); +pair([{transport_config, C, Tmo} | Rest], Mods, Acc) -> + pair(Rest, [], acc({Mods, C, Tmo}, Acc)); + +pair([_ | Rest], Mods, Acc) -> + pair(Rest, Mods, Acc); + +%% No transport_module or transport_config: defaults. +pair([], [], []) -> + [{[?DEFAULT_TMOD], ?DEFAULT_TCFG, ?DEFAULT_TTMO}]; + +%% One transport_module, one transport_config. +pair([], [M], [{[], Cfg, Tmo}]) -> + [{[M], Cfg, Tmo}]; + +%% Trailing transport_module: default transport_config. +pair([], [_|_] = Mods, Acc) -> + lists:reverse(acc({Mods, ?DEFAULT_TCFG, ?DEFAULT_TTMO}, Acc)); + +pair([], [], Acc) -> + lists:reverse(def(Acc)). + +%% acc/2 + +acc(T, Acc) -> + [T | def(Acc)]. + +%% def/1 +%% +%% Default module of previous pair if none were specified. + +def([{[], Cfg, Tmo} | Acc]) -> + [{[?DEFAULT_TMOD], Cfg, Tmo} | Acc]; +def(Acc) -> + Acc. + +%% start/4 + +start(T, Svc, [{Ms, Cfg, Tmo} | Rest], Errs) -> + start(T, Ms, Cfg, Svc, Tmo, Rest, Errs); + +start(_, _, [], Errs) -> + {error, Errs}. + +%% start/7 + +start(T, [], _, Svc, _, Rest, Errs) -> + start(T, Svc, Rest, Errs); + +start(T, [M|Ms], Cfg, Svc, Tmo, Rest, Errs) -> + case start(M, [T, Svc, Cfg]) of + {ok, TPid} -> + {TPid, [], Tmo, {{T, M, Cfg}, Ms, Rest, Errs}}; + {ok, TPid, [_|_] = Addrs} -> + {TPid, Addrs, Tmo, {{T, M, Cfg}, Ms, Rest, Errs}}; + E -> + start(T, Ms, Cfg, Svc, Tmo, Rest, [E|Errs]) + end. + +%% start/2 + +start(Mod, Args) -> + apply(Mod, start, Args). %%% --------------------------------------------------------------------------- %%% # up/[12] @@ -204,21 +322,6 @@ bang(undefined = No, _) -> bang(Pid, T) -> Pid ! T. -%% split_transport/1 -%% -%% Split options into transport module, transport config and -%% remaining options. - -split_transport(Opts) -> - {[M,C], _} = proplists:split(Opts, [transport_module, - transport_config]), - {value(M, diameter_tcp), value(C, [])}. - -value([{_,V}], _) -> - V; -value([], V) -> - V. - %% call/1 call(Request) -> diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index 99644814d2..302540e76b 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -54,6 +54,12 @@ -define(NO_INBAND_SECURITY, 0). -define(TLS, 1). +%% Keys in process dictionary. +-define(CB_KEY, cb). %% capabilities callback +-define(DWA_KEY, dwa). %% outgoing DWA +-define(Q_KEY, q). %% transport start queue +-define(START_KEY, start). %% start of connected transport + %% A 2xxx series Result-Code. Not necessarily 2001. -define(IS_SUCCESS(N), 2 == (N) div 1000). @@ -115,16 +121,20 @@ %%% Output: Pid %%% --------------------------------------------------------------------------- +-spec start(T, [Opt], #diameter_service{}) + -> pid() + when T :: {connect|accept, diameter:transport_ref()}, + Opt :: diameter:transport_opt(). + %% diameter_config requires a non-empty list of applications on the %% service but diameter_service then constrains the list to any %% specified on the transport in question. Check here that the list is %% still non-empty. -start({_, Ref} = Type, Opts, #diameter_service{applications = Apps} = Svc) -> +start({_,_} = Type, Opts, #diameter_service{applications = Apps} = Svc) -> [] /= Apps orelse ?ERROR({no_apps, Type, Opts}), T = {self(), Type, Opts, Svc}, {ok, Pid} = diameter_peer_fsm_sup:start_child(T), - diameter_stats:reg(Pid, Ref), Pid. start_link(T) -> @@ -143,18 +153,18 @@ init(T) -> proc_lib:init_ack({ok, self()}), gen_server:enter_loop(?MODULE, [], i(T)). -i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) -> - putr(dwa, dwa(Caps)), +i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc}) -> + putr(?DWA_KEY, dwa(Caps)), {M, Ref} = T, + diameter_stats:reg(Ref), {[Ts], Rest} = proplists:split(Opts, [capabilities_cb]), - putr(capabilities_cb, {Ref, [F || {_,F} <- Ts]}), - {ok, TPid, Svc} = start_transport(T, Rest, Svc0), - erlang:monitor(process, TPid), + putr(?CB_KEY, {Ref, [F || {_,F} <- Ts]}), erlang:monitor(process, WPid), + {TPid, Addrs} = start_transport(T, Rest, Svc), #state{parent = WPid, transport = TPid, mode = M, - service = Svc}. + service = svc(Svc, Addrs)}. %% The transport returns its local ip addresses so that different %% transports on the same service can use different local addresses. %% The local addresses are put into Host-IP-Address avps here when @@ -164,18 +174,56 @@ i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) -> %% watchdog start (start/2) succeeds regardless so as not to crash the %% service. -start_transport(T, Opts, Svc) -> - case diameter_peer:start(T, Opts, Svc) of - {ok, TPid} -> - {ok, TPid, Svc}; - {ok, TPid, [_|_] = Addrs} -> - #diameter_service{capabilities = Caps0} = Svc, - Caps = Caps0#diameter_caps{host_ip_address = Addrs}, - {ok, TPid, Svc#diameter_service{capabilities = Caps}}; +start_transport(T, Opts, #diameter_service{capabilities = Caps} = Svc) -> + Addrs0 = Caps#diameter_caps.host_ip_address, + start_transport(Addrs0, {T, Opts, Svc}). + +start_transport(Addrs0, T) -> + case diameter_peer:start(T) of + {TPid, Addrs, Tmo, Data} -> + erlang:monitor(process, TPid), + q_next(TPid, Addrs0, Tmo, Data), + {TPid, addrs(Addrs, Addrs0)}; No -> exit({shutdown, No}) end. +addrs([], Addrs0) -> + Addrs0; +addrs(Addrs, _) -> + Addrs. + +svc(Svc, []) -> + Svc; +svc(Svc, Addrs) -> + readdr(Svc, Addrs). + +readdr(#diameter_service{capabilities = Caps0} = Svc, Addrs) -> + Caps = Caps0#diameter_caps{host_ip_address = Addrs}, + Svc#diameter_service{capabilities = Caps}. + +%% The 4-tuple Data returned from diameter_peer:start/1 identifies the +%% transport module/config use to start the transport process in +%% question as well as any alternates to try if a connection isn't +%% established within Tmo. +q_next(TPid, Addrs0, Tmo, {_,_,_,_} = Data) -> + send_after(Tmo, {connection_timeout, TPid}), + putr(?Q_KEY, {Addrs0, Tmo, Data}). + +%% Connection has been established: retain the started +%% pid/module/config in the process dictionary. This is a part of the +%% interface defined by this module, so that the transport pid can be +%% found when constructing service_info (in order to extract further +%% information from it). +keep_transport(TPid) -> + {_, _, {{_,_,_} = T, _, _, _}} = eraser(?Q_KEY), + putr(?START_KEY, {TPid, T}). + +send_after(infinity, _) -> + ok; +send_after(Tmo, T) -> + erlang:send_after(Tmo, self(), T). + %% handle_call/3 handle_call(_, _, State) -> @@ -202,14 +250,27 @@ handle_info(T, #state{} = State) -> ?LOG(stop, T), x(T, State) catch + exit: {diameter_codec, encode, _} = Reason -> + close_wd(Reason, State#state.parent), + ?LOG(stop, Reason), + %% diameter_codec:encode/2 emits an error report. Only + %% indicate the probable reason here. + diameter_lib:info_report(probable_configuration_error, + insufficient_capabilities), + {stop, {shutdown, Reason}, State}; {?MODULE, Tag, Reason} -> ?LOG(Tag, {Reason, T}), {stop, {shutdown, Reason}, State} end. -%% The form of the exception caught here is historical. It's +%% The form of the throw caught here is historical. It's %% significant that it's not a 2-tuple, as in ?FAILURE(Reason), %% since these are caught elsewhere. +%% Note that there's no guarantee that the service and transport +%% capabilities are good enough to build a CER/CEA that can be +%% succesfully encoded. It's not checked at diameter:add_transport/2 +%% since this can be called before creating the service. + x(Reason, #state{} = S) -> close_wd(Reason, S), {stop, {shutdown, Reason}, S}. @@ -240,25 +301,48 @@ eraser(Key) -> %% Connection to peer. transition({diameter, {TPid, connected, Remote}}, - #state{state = PS, + #state{transport = TPid, + state = PS, mode = M} = S) -> 'Wait-Conn-Ack' = PS, %% assert connect = M, %% - send_CER(S#state{mode = {M, Remote}, - transport = TPid}); + keep_transport(TPid), + send_CER(S#state{mode = {M, Remote}}); %% Connection from peer. transition({diameter, {TPid, connected}}, - #state{state = PS, + #state{transport = TPid, + state = PS, mode = M, parent = Pid} = S) -> 'Wait-Conn-Ack' = PS, %% assert accept = M, %% + keep_transport(TPid), Pid ! {accepted, self()}, - start_timer(S#state{state = recv_CER, - transport = TPid}); + start_timer(S#state{state = recv_CER}); + +%% Connection established after receiving a connection_timeout +%% message. This may be followed by an incoming message which arrived +%% before the transport was killed and this can't be distinguished +%% from one from the transport that's been started to replace it. +transition({diameter, {_, connected}}, _) -> + {stop, connection_timeout}; +transition({diameter, {_, connected, _}}, _) -> + {stop, connection_timeout}; + +%% Connection has timed out: start an alternate. +transition({connection_timeout = T, TPid}, + #state{transport = TPid, + state = 'Wait-Conn-Ack'} + = S) -> + exit(TPid, {shutdown, T}), + start_next(S); + +%% Connect timeout after connection or alternate start: ignore. +transition({connection_timeout, _}, _) -> + ok; %% Incoming message from the transport. transition({diameter, {recv, Pkt}}, S) -> @@ -305,14 +389,21 @@ transition({resolve_port, _Pid} = T, #state{transport = TPid}) -> TPid ! T, ok; -%% Parent or transport has died. -transition({'DOWN', _, process, P, _}, - #state{parent = Pid, - transport = TPid}) - when P == Pid; - P == TPid -> +%% Parent has died. +transition({'DOWN', _, process, WPid, _}, + #state{parent = WPid}) -> stop; +%% Transport has died before connection timeout. +transition({'DOWN', _, process, TPid, _}, + #state{transport = TPid} + = S) -> + start_next(S); + +%% Transport has died after connection timeout. +transition({'DOWN', _, process, _, _}, _) -> + ok; + %% State query. transition({state, Pid}, #state{state = S, transport = TPid}) -> Pid ! {self(), [S, TPid]}, @@ -320,6 +411,19 @@ transition({state, Pid}, #state{state = S, transport = TPid}) -> %% Crash on anything unexpected. +%% start_next/1 + +start_next(#state{service = Svc0} = S) -> + case getr(?Q_KEY) of + {Addrs0, Tmo, Data} -> + Svc = readdr(Svc0, Addrs0), + {TPid, Addrs} = start_transport(Addrs0, {Svc, Tmo, Data}), + S#state{transport = TPid, + service = svc(Svc, Addrs)}; + undefined -> + stop + end. + %% send_CER/1 send_CER(#state{mode = {connect, Remote}, @@ -649,7 +753,7 @@ rc([RC|_]) -> %% answer/2 answer('DWR', _) -> - getr(dwa); + getr(?DWA_KEY); answer(Name, #state{service = #diameter_service{capabilities = Caps}}) -> a(Name, Caps). @@ -749,15 +853,15 @@ caps(#state{service = Svc}) -> %% caps_cb/1 caps_cb(Caps) -> - {Ref, Ts} = eraser(capabilities_cb), - ccb(Ts, [Ref, Caps]). + {Ref, Ts} = eraser(?CB_KEY), + caps_cb(Ts, [Ref, Caps]). -ccb([], _) -> +caps_cb([], _) -> ok; -ccb([F | Rest], T) -> +caps_cb([F | Rest], T) -> case diameter_lib:eval([F|T]) of ok -> - ccb(Rest, T); + caps_cb(Rest, T); N when ?IS_SUCCESS(N) -> %% 2xxx result code: accept immediately N; Res -> diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 3dfdcee2b2..54594db292 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -43,8 +43,7 @@ subscriptions/0, services/0, services/1, - whois/1, - flush_stats/1]). + whois/1]). %% test/debug -export([call_module/3, @@ -65,13 +64,33 @@ -include_lib("diameter/include/diameter.hrl"). -include("diameter_internal.hrl"). +%% The "old" states maintained in this module historically. -define(STATE_UP, up). -define(STATE_DOWN, down). +-type op_state() :: ?STATE_UP + | ?STATE_DOWN. + +%% The RFC 3539 watchdog states that are now maintained, albeit +%% along with the old up/down. okay = up, else down. +-define(WD_INITIAL, initial). +-define(WD_OKAY, okay). +-define(WD_SUSPECT, suspect). +-define(WD_DOWN, down). +-define(WD_REOPEN, reopen). + +-type wd_state() :: ?WD_INITIAL + | ?WD_OKAY + | ?WD_SUSPECT + | ?WD_DOWN + | ?WD_REOPEN. + -define(DEFAULT_TC, 30000). %% RFC 3588 ch 2.1 -define(DEFAULT_TIMEOUT, 5000). %% for outgoing requests -define(RESTART_TC, 1000). %% if restart was this recent +-define(RELAY, ?DIAMETER_DICT_RELAY). + %% Used to be able to swap this with anything else dict-like but now %% rely on the fact that a service's #state{} record does not change %% in storing in it ?STATE table and not always going through the @@ -117,7 +136,8 @@ type :: match(connect | accept), ref :: match(reference()), %% key into diameter_config options :: match([diameter:transport_opt()]),%% from start_transport - op_state = ?STATE_DOWN :: match(?STATE_DOWN | ?STATE_UP), + op_state = {?STATE_DOWN, ?WD_INITIAL} + :: match(op_state() | {op_state(), wd_state()}), started = now(), %% at process start conn = false :: match(boolean() | pid())}). %% true at accept, pid() at connection_up (connT key) @@ -388,15 +408,6 @@ whois(SvcName) -> undefined end. -%%% --------------------------------------------------------------------------- -%%% # flush_stats/1 -%%% -%%% Output: list of {{SvcName, Alias, Counter}, Value} -%%% --------------------------------------------------------------------------- - -flush_stats(TPid) -> - diameter_stats:flush(TPid). - %% =========================================================================== %% =========================================================================== @@ -516,6 +527,34 @@ transition({reconnect, Pid}, S) -> reconnect(Pid, S), ok; +%% Watchdog is sending notification of a state transition. Note that +%% the connection_up/down messages are pre-date this message and are +%% still used. A 'watchdog' message will follow these and communicate +%% the same state as was set in handling connection_up/down. +transition({watchdog, Pid, {TPid, From, To}}, #state{service_name = SvcName, + peerT = PeerT}) -> + #peer{ref = Ref, type = T, options = Opts, op_state = {OS,_}} + = P + = fetch(PeerT, Pid), + insert(PeerT, P#peer{op_state = {OS, To}}), + send_event(SvcName, {watchdog, Ref, TPid, {From, To}, {T, Opts}}), + ok; +%% Death of a peer process results in the removal of it's peer and any +%% associated conn record when 'DOWN' is received (after this) but the +%% states will be {?STATE_UP, ?WD_DOWN} for a short time. (No real +%% problem since ?WD_* is only used in service_info.) We set ?WD_OKAY +%% as a consequence of connection_up since we know a watchdog is +%% coming. We can't set anything at connection_down since we don't +%% know if the subsequent watchdog message will be ?WD_DOWN or +%% ?WD_SUSPECT. We don't (yet) set ?STATE_* as a consequence of a +%% watchdog message since this requires changing some of the matching +%% on ?STATE_*. +%% +%% Death of a conn process results in connection_down followed by +%% watchdog ?WD_DOWN. The latter doesn't result in the conn record +%% being deleted since 'DOWN' from death of its peer doesn't (yet) +%% deal with the record having been removed. + %% Monitor process has died. Just die with a reason that tells %% diameter_config about the happening. If a cleaner shutdown is %% required then someone should stop us. @@ -879,7 +918,14 @@ accepted(Pid, _TPid, #state{peerT = PeerT} = S) -> fetch(Tid, Key) -> [T] = ets:lookup(Tid, Key), - T. + case T of + #peer{op_state = ?STATE_UP} = P -> + P#peer{op_state = {?STATE_UP, ?WD_OKAY}}; + #peer{op_state = ?STATE_DOWN} = P -> + P#peer{op_state = {?STATE_DOWN, ?WD_DOWN}}; + _ -> + T + end. %%% --------------------------------------------------------------------------- %%% # connection_up/3 @@ -925,12 +971,12 @@ connection_up(T, P, C, #state{peerT = PeerT, service = #diameter_service{applications = Apps}} = S) -> - #peer{conn = TPid, op_state = ?STATE_DOWN} + #peer{conn = TPid, op_state = {?STATE_DOWN, _}} = P, #conn{apps = SApps, caps = Caps} = C, - insert(PeerT, P#peer{op_state = ?STATE_UP}), + insert(PeerT, P#peer{op_state = {?STATE_UP, ?WD_OKAY}}), request_peer_up(TPid), report_status(up, P, C, S, T), @@ -945,27 +991,35 @@ ilp({Id, Alias}, {TC, SA}, LDict) -> init_conn(Id, Alias, TC, SA), ?Dict:append(Alias, TC, LDict). -init_conn(Id, Alias, TC, {SvcName, Apps}) -> +init_conn(Id, Alias, {TPid, _} = TC, {SvcName, Apps}) -> #diameter_app{module = ModX, id = Id} %% assert = find_app(Alias, Apps), - peer_cb({ModX, peer_up, [SvcName, TC]}, Alias). + peer_cb({ModX, peer_up, [SvcName, TC]}, Alias) + orelse exit(TPid, kill). %% fake transport failure + +%% find_app/2 find_app(Alias, Apps) -> - lists:keyfind(Alias, #diameter_app.alias, Apps). + case lists:keyfind(Alias, #diameter_app.alias, Apps) of + #diameter_app{options = E} = A when is_atom(E) -> %% upgrade + A#diameter_app{options = [{answer_errors, E}]}; + A -> + A + end. -%% A failing peer callback brings down the service. In the case of -%% peer_up we could just kill the transport and emit an error but for -%% peer_down we have no way to cleanup any state change that peer_up -%% may have introduced. +%% Don't bring down the service (and all associated connections) +%% regardless of what happens. peer_cb(MFA, Alias) -> try state_cb(MFA, Alias) of ModS -> - mod_state(Alias, ModS) + mod_state(Alias, ModS), + true catch - E: Reason -> - ?ERROR({E, Reason, MFA, ?STACK}) + E:R -> + diameter_lib:error_report({failure, {E, R, Alias, ?STACK}}, MFA), + false end. %%% --------------------------------------------------------------------------- @@ -979,22 +1033,22 @@ peer_cb(MFA, Alias) -> connection_down(Pid, #state{peerT = PeerT, connT = ConnT} = S) -> - #peer{op_state = ?STATE_UP, %% assert + #peer{op_state = {?STATE_UP, WS}, %% assert conn = TPid} = P = fetch(PeerT, Pid), C = fetch(ConnT, TPid), - insert(PeerT, P#peer{op_state = ?STATE_DOWN}), + insert(PeerT, P#peer{op_state = {?STATE_DOWN, WS}}), connection_down(P,C,S). %% connection_down/3 -connection_down(#peer{op_state = ?STATE_DOWN}, _, S) -> +connection_down(#peer{op_state = {?STATE_DOWN, _}}, _, S) -> S; connection_down(#peer{conn = TPid, - op_state = ?STATE_UP} + op_state = {?STATE_UP, _}} = P, #conn{caps = Caps, apps = SApps} @@ -1043,7 +1097,7 @@ peer_down(Pid, Reason, #state{peerT = PeerT} = S) -> %% Send an event at connection establishment failure. closed({shutdown, {close, _TPid, Reason}}, - #peer{op_state = ?STATE_DOWN, + #peer{op_state = {?STATE_DOWN, _}, ref = Ref, type = Type, options = Opts}, @@ -1352,7 +1406,7 @@ send_request(Pkt, TPid, Caps, App, Opts, Caller, SvcName) -> #diameter_app{alias = Alias, dictionary = Dict, module = ModX, - answer_errors = AE} + options = [{answer_errors, AE} | _]} = App, EPkt = encode(Dict, Pkt), @@ -1935,6 +1989,12 @@ is_loop(Code, Vid, OH, Avps) -> %% %% Send a locally originating reply. +%% Skip the setting of Result-Code and Failed-AVP's below. +reply([Msg], Dict, TPid, Pkt) + when is_list(Msg); + is_tuple(Msg) -> + reply(Msg, Dict, TPid, Pkt#diameter_packet{errors = []}); + %% No errors or a diameter_header/avp list. reply(Msg, Dict, TPid, #diameter_packet{errors = Es, transport_data = TD} @@ -1942,7 +2002,7 @@ reply(Msg, Dict, TPid, #diameter_packet{errors = Es, when [] == Es; is_record(hd(Msg), diameter_header) -> Pkt = diameter_codec:encode(Dict, make_answer_packet(Msg, ReqPkt)), - incr(send, Pkt, TPid), %% count result codes in sent answers + incr(send, Pkt, Dict, TPid), %% count result codes in sent answers send(TPid, Pkt#diameter_packet{transport_data = TD}); %% Or not: set Result-Code and Failed-AVP AVP's. @@ -1983,7 +2043,10 @@ rc(RC) -> rc(Rec, RC, Failed, Dict) when is_integer(RC) -> - set(Rec, [{'Result-Code', RC} | failed_avp(Rec, Failed, Dict)], Dict). + set(Rec, + lists:append([rc(Rec, {'Result-Code', RC}, Dict), + failed_avp(Rec, Failed, Dict)]), + Dict). %% Reply as name and tuple list ... set([_|_] = Ans, Avps, _) -> @@ -1993,6 +2056,22 @@ set([_|_] = Ans, Avps, _) -> set(Rec, Avps, Dict) -> Dict:'#set-'(Avps, Rec). +%% rc/3 +%% +%% Turn the result code into a list if its optional and only set it if +%% the arity is 1 or {0,1}. In other cases (which probably shouldn't +%% exist in practise) we can't know what's appropriate. + +rc([MsgName | _], {'Result-Code' = K, RC} = T, Dict) -> + case Dict:avp_arity(MsgName, 'Result-Code') of + 1 -> [T]; + {0,1} -> [{K, [RC]}]; + _ -> [] + end; + +rc(Rec, T, Dict) -> + rc([Dict:rec2msg(element(1, Rec))], T, Dict). + %% failed_avp/3 failed_avp(_, [] = No, _) -> @@ -2200,44 +2279,39 @@ handle_answer(SvcName, _, {error, Req, Reason}) -> handle_answer(SvcName, AnswerErrors, {answer, #request{dictionary = Dict} = Req, Pkt}) -> - a(examine(diameter_codec:decode(Dict, Pkt)), - SvcName, - AnswerErrors, - Req). + answer(examine(diameter_codec:decode(Dict, Pkt)), + SvcName, + AnswerErrors, + Req). %% We don't really need to do a full decode if we're a relay and will %% just resend with a new hop by hop identifier, but might a proxy %% want to examine the answer? -a(#diameter_packet{errors = []} - = Pkt, - SvcName, - AE, - #request{transport = TPid, - caps = Caps, - packet = P} - = Req) -> +answer(Pkt, SvcName, AE, #request{transport = TPid, + dictionary = Dict} + = Req) -> try - incr(in, Pkt, TPid) + incr(recv, Pkt, Dict, TPid) of - _ -> - cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]) + _ -> a(Pkt, SvcName, AE, Req) catch exit: {invalid_error_bit, _} = E -> - e(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req) - end; - -a(#diameter_packet{} = Pkt, SvcName, AE, Req) -> - e(Pkt, SvcName, AE, Req). + a(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req) + end. -e(Pkt, SvcName, callback, #request{transport = TPid, - caps = Caps, - packet = Pkt} - = Req) -> - cb(Req, handle_answer, [Pkt, msg(Pkt), SvcName, {TPid, Caps}]); -e(Pkt, SvcName, report, Req) -> +a(#diameter_packet{errors = Es} = Pkt, SvcName, AE, #request{transport = TPid, + caps = Caps, + packet = P} + = Req) + when [] == Es; + callback == AE -> + cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]); + +a(Pkt, SvcName, report, Req) -> x(errors, handle_answer, [SvcName, Req, Pkt]); -e(Pkt, SvcName, discard, Req) -> + +a(Pkt, SvcName, discard, Req) -> x({errors, handle_answer, [SvcName, Req, Pkt]}). %% Note that we don't check that the application id in the answer's @@ -2249,17 +2323,19 @@ e(Pkt, SvcName, discard, Req) -> %% Increment a stats counter for an incoming or outgoing message. %% TODO: fix -incr(_, #diameter_packet{msg = undefined}, _) -> +incr(_, #diameter_packet{msg = undefined}, _, _) -> ok; -incr(Dir, Pkt, TPid) - when is_pid(TPid) -> +incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, _, TPid) -> + incr(TPid, {diameter_codec:msg_id(H), D, error}); + +incr(Dir, Pkt, Dict, TPid) -> #diameter_packet{header = #diameter_header{is_error = E} = Hdr, msg = Rec} = Pkt, - RC = int(get_avp_value(?BASE, 'Result-Code', Rec)), + RC = int(get_avp_value(Dict, 'Result-Code', Rec)), PE = is_protocol_error(RC), %% Check that the E bit is set only for 3xxx result codes. @@ -2267,15 +2343,21 @@ incr(Dir, Pkt, TPid) orelse (E andalso PE) orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]), - Ctr = rc_counter(Rec, RC), - is_tuple(Ctr) - andalso incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}). + irc(TPid, Hdr, Dir, rc_counter(Dict, Rec, RC)). + +irc(_, _, _, undefined) -> + false; + +irc(TPid, Hdr, Dir, Ctr) -> + incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}). %% incr/2 incr(TPid, Counter) -> diameter_stats:incr(Counter, TPid, 1). +%% error_counter/2 + %% RFC 3588, 7.6: %% %% All Diameter answer messages defined in vendor-specific @@ -2285,26 +2367,27 @@ incr(TPid, Counter) -> %% Maintain statistics assuming one or the other, not both, which is %% surely the intent of the RFC. -rc_counter(_, RC) - when is_integer(RC) -> - {'Result-Code', RC}; -rc_counter(Rec, _) -> - rcc(get_avp_value(?BASE, 'Experimental-Result', Rec)). +rc_counter(Dict, Rec, undefined) -> + er(get_avp_value(Dict, 'Experimental-Result', Rec)); +rc_counter(_, _, RC) -> + {'Result-Code', RC}. %% Outgoing answers may be in any of the forms messages can be sent %% in. Incoming messages will be records. We're assuming here that the %% arity of the result code AVP's is 0 or 1. -rcc([{_,_,RC} = T]) - when is_integer(RC) -> +er([{_,_,N} = T | _]) + when is_integer(N) -> T; -rcc({_,_,RC} = T) - when is_integer(RC) -> +er({_,_,N} = T) + when is_integer(N) -> T; -rcc(_) -> +er(_) -> undefined. -int([N]) +%% Extract the first good looking integer. There's no guarantee +%% that what we're looking for has arity 1. +int([N|_]) when is_integer(N) -> N; int(N) @@ -2349,8 +2432,11 @@ rt(#request{packet = #diameter_packet{msg = undefined}}, _) -> false; %% TODO: Not what we should do. %% ... or not. -rt(#request{packet = #diameter_packet{msg = Msg}} = Req, S) -> - find_transport(get_destination(Msg), Req, S). +rt(#request{packet = #diameter_packet{msg = Msg}, + dictionary = Dict} + = Req, + S) -> + find_transport(get_destination(Dict, Msg), Req, S). %%% --------------------------------------------------------------------------- %%% # report_status/5 @@ -2462,12 +2548,12 @@ find_transport({alias, Alias}, Msg, Opts, #state{service = Svc} = S) -> find_transport(#diameter_app{} = App, Msg, Opts, S) -> ft(App, Msg, Opts, S). -ft(#diameter_app{module = Mod} = App, Msg, Opts, S) -> +ft(#diameter_app{module = Mod, dictionary = Dict} = App, Msg, Opts, S) -> #options{filter = Filter, extra = Xtra} = Opts, pick_peer(App#diameter_app{module = Mod ++ Xtra}, - get_destination(Msg), + get_destination(Dict, Msg), Filter, S); ft(false = No, _, _, _) -> @@ -2503,11 +2589,11 @@ find_transport([_,_] = RH, Filter, S). -%% get_destination/1 +%% get_destination/2 -get_destination(Msg) -> - [str(get_avp_value(?BASE, 'Destination-Realm', Msg)), - str(get_avp_value(?BASE, 'Destination-Host', Msg))]. +get_destination(Dict, Msg) -> + [str(get_avp_value(Dict, 'Destination-Realm', Msg)), + str(get_avp_value(Dict, 'Destination-Host', Msg))]. %% This is not entirely correct. The avp could have an arity 1, in %% which case an empty list is a DiameterIdentity of length 0 rather @@ -2531,6 +2617,9 @@ str(T) -> %% question. The third form allows messages to be sent as is, without %% a dictionary, which is needed in the case of relay agents, for one. +get_avp_value(?RELAY, Name, Msg) -> + get_avp_value(?BASE, Name, Msg); + get_avp_value(Dict, Name, [#diameter_header{} | Avps]) -> try {Code, _, VId} = Dict:avp_header(Name), @@ -2746,20 +2835,45 @@ transports(#state{peerT = PeerT}) -> 'Vendor-Specific-Application-Id', 'Firmware-Revision']). +%% The config returned by diameter:service_info(SvcName, all). -define(ALL_INFO, [capabilities, applications, transport, - pending, - statistics]). + pending]). + +%% The rest. +-define(OTHER_INFO, [connections, + name, + peers, + statistics]). -service_info(Items, S) - when is_list(Items) -> - [{complete(I), service_info(I,S)} || I <- Items]; service_info(Item, S) when is_atom(Item) -> - service_info(Item, S, true). + case tagged_info(Item, S) of + {_, T} -> T; + undefined = No -> No + end; -service_info(Item, #state{service = Svc} = S, Complete) -> +service_info(Items, S) -> + tagged_info(Items, S). + +tagged_info(Item, S) + when is_atom(Item) -> + case complete(Item) of + {value, I} -> + {I, complete_info(I,S)}; + false -> + undefined + end; + +tagged_info(Items, S) + when is_list(Items) -> + [T || I <- Items, T <- [tagged_info(I,S)], T /= undefined, T /= []]; + +tagged_info(_, _) -> + undefined. + +complete_info(Item, #state{service = Svc} = S) -> case Item of name -> S#state.service_name; @@ -2803,84 +2917,160 @@ service_info(Item, #state{service = Svc} = S, Complete) -> applications -> info_apps(S); transport -> info_transport(S); pending -> info_pending(S); - statistics -> info_stats(S); - keys -> ?ALL_INFO ++ ?CAP_INFO; %% mostly for test + keys -> ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO; all -> service_info(?ALL_INFO, S); - _ when Complete -> service_info(complete(Item), S, false); - _ -> undefined + statistics -> info_stats(S); + connections -> info_connections(S); + peers -> info_peers(S) end. +complete(I) + when I == keys; + I == all -> + {value, I}; complete(Pre) -> P = atom_to_list(Pre), - case [I || I <- [name | ?ALL_INFO] ++ ?CAP_INFO, + case [I || I <- ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO, lists:prefix(P, atom_to_list(I))] of - [I] -> I; - _ -> Pre + [I] -> {value, I}; + _ -> false end. +%% info_stats/1 + info_stats(#state{peerT = PeerT}) -> - Peers = ets:select(PeerT, [{#peer{ref = '$1', conn = '$2', _ = '_'}, - [{'is_pid', '$2'}], - [['$1', '$2']]}]), - diameter_stats:read(lists:append(Peers)). -%% TODO: include peer identities in return value - -info_transport(#state{peerT = PeerT, connT = ConnT}) -> - dict:fold(fun it/3, + MatchSpec = [{#peer{ref = '$1', conn = '$2', _ = '_'}, + [{'is_pid', '$2'}], + [['$1', '$2']]}], + diameter_stats:read(lists:append(ets:select(PeerT, MatchSpec))). + +%% info_transport/1 +%% +%% One entry per configured transport. Statistics for each entry are +%% the accumulated values for the ref and associated peer pids. + +info_transport(S) -> + PeerD = peer_dict(S, config_dict(S)), + RefsD = dict:map(fun(_, Ls) -> [P || L <- Ls, {peer, {P,_}} <- L] end, + PeerD), + Refs = lists:append(dict:fold(fun(R, Ps, A) -> [[R|Ps] | A] end, + [], + RefsD)), + Stats = diameter_stats:read(Refs), + dict:fold(fun(R, Ls, A) -> + Ps = dict:fetch(R, RefsD), + [[{ref, R} | transport(Ls)] ++ [stats([R|Ps], Stats)] + | A] + end, [], - ets:foldl(fun(T,A) -> it_acc(ConnT, A, T) end, - dict:new(), - PeerT)). - -it(Ref, [[{type, connect} | _] = L], Acc) -> - [[{ref, Ref} | L] | Acc]; -it(Ref, [[{type, accept}, {options, Opts} | _] | _] = L, Acc) -> - [[{ref, Ref}, - {type, listen}, - {options, Opts}, - {accept, [lists:nthtail(2,A) || A <- L]}] - | Acc]. -%% Each entry has the same Opts. (TODO) - -it_acc(ConnT, Acc, #peer{pid = Pid, - type = Type, - ref = Ref, - options = Opts, - op_state = OS, - started = T, - conn = TPid}) -> + PeerD). + +%% Only a config entry for a listening transport: use it. +transport([[{type, listen}, _] = L]) -> + L ++ [{accept, []}]; + +%% Only one config or peer entry for a connecting transport: use it. +transport([[{type, connect} | _] = L]) -> + L; + +%% Peer entries: discard config. Note that the peer entries have +%% length at least 3. +transport([[_,_] | L]) -> + transport(L); + +%% Possibly many peer entries for a listening transport. Note that all +%% have the same options by construction, which is not terribly space +%% efficient. (TODO: all entries for the same Ref should share options.) +transport([[{type, accept}, {options, Opts} | _] | _] = Ls) -> + [{type, listen}, + {options, Opts}, + {accept, [lists:nthtail(2,L) || L <- Ls]}]. + +peer_dict(#state{peerT = PeerT, connT = ConnT}, Dict0) -> + ets:foldl(fun(T,A) -> peer_acc(ConnT, A, T) end, Dict0, PeerT). + +peer_acc(ConnT, Acc, #peer{pid = Pid, + type = Type, + ref = Ref, + options = Opts, + op_state = OS, + started = T, + conn = TPid}) -> + WS = wd_state(OS), dict:append(Ref, [{type, Type}, {options, Opts}, - {watchdog, {Pid, T, OS}} - | info_conn(ConnT, TPid)], + {watchdog, {Pid, T, WS}} + | info_conn(ConnT, TPid, WS /= ?WD_DOWN)], Acc). -info_conn(ConnT, TPid) -> - info_conn(ets:lookup(ConnT, TPid)). +info_conn(ConnT, TPid, true) + when is_pid(TPid) -> + info_conn(ets:lookup(ConnT, TPid)); +info_conn(_, _, _) -> + []. + +%% The point of extracting the config here is so that 'transport' info +%% has one entry for each transport ref, the peer table only +%% containing entries that have a living watchdog. + +config_dict(#state{service_name = SvcName}) -> + lists:foldl(fun config_acc/2, + dict:new(), + diameter_config:lookup(SvcName)). + +config_acc({Ref, T, Opts}, Dict) + when T == listen; + T == connect -> + dict:store(Ref, [[{type, T}, {options, Opts}]], Dict); +config_acc(_, Dict) -> + Dict. + +wd_state({_,S}) -> + S; +wd_state(?STATE_UP) -> + ?WD_OKAY; +wd_state(?STATE_DOWN) -> + ?WD_DOWN. info_conn([#conn{pid = Pid, apps = SApps, caps = Caps, started = T}]) -> [{peer, {Pid, T}}, {apps, SApps}, - {caps, info_caps(Caps)}]; + {caps, info_caps(Caps)} + | try [{port, info_port(Pid)}] catch _:_ -> [] end]; info_conn([] = No) -> No. +%% Extract information that the processes involved are expected to +%% "publish" in their process dictionaries. Simple but backhanded. +info_port(Pid) -> + {_, PD} = process_info(Pid, dictionary), + {_, T} = lists:keyfind({diameter_peer_fsm, start}, 1, PD), + {TPid, {_Type, TMod, _Cfg}} = T, + {_, TD} = process_info(TPid, dictionary), + {_, Data} = lists:keyfind({TMod, info}, 1, TD), + [{owner, TPid}, + {module, TMod} + | try TMod:info(Data) catch _:_ -> [] end]. + +%% Use the fields names from diameter_caps instead of +%% diameter_base_CER to distinguish between the 2-tuple values +%% compared to the single capabilities values. Note also that the +%% returned list is tagged 'caps' rather than 'capabilities' to +%% emphasize the difference. info_caps(#diameter_caps{} = C) -> lists:zip(record_info(fields, diameter_caps), tl(tuple_to_list(C))). info_apps(#state{service = #diameter_service{applications = Apps}}) -> lists:map(fun mk_app/1, Apps). -mk_app(#diameter_app{alias = Alias, - dictionary = Dict, - module = ModX, - id = Id}) -> - [{alias, Alias}, - {dictionary, Dict}, - {module, ModX}, - {id, Id}]. +mk_app(#diameter_app{} = A) -> + lists:zip(record_info(fields, diameter_app), tl(tuple_to_list(A))). + +%% info_pending/1 +%% +%% One entry for each outgoing request whose answer is outstanding. info_pending(#state{} = S) -> MatchSpec = [{{'$1', @@ -2895,3 +3085,57 @@ info_pending(#state{} = S) -> {{from, '$3'}}]}}]}], ets:select(?REQUEST_TABLE, MatchSpec). + +%% info_connections/1 +%% +%% One entry per transport connection. Statistics for each entry are +%% for the peer pid only. + +info_connections(S) -> + ConnL = conn_list(S), + Stats = diameter_stats:read([P || L <- ConnL, {peer, {P,_}} <- L]), + [L ++ [stats([P], Stats)] || L <- ConnL, {peer, {P,_}} <- L]. + +conn_list(S) -> + lists:append(dict:fold(fun conn_acc/3, [], peer_dict(S, dict:new()))). + +conn_acc(Ref, Peers, Acc) -> + [[[{ref, Ref} | L] || L <- Peers, lists:keymember(peer, 1, L)] + | Acc]. + +stats(Refs, Stats) -> + {statistics, dict:to_list(lists:foldl(fun(R,D) -> + stats_acc(R, D, Stats) + end, + dict:new(), + Refs))}. + +stats_acc(Ref, Dict, Stats) -> + lists:foldl(fun({C,N}, D) -> dict:update_counter(C, N, D) end, + Dict, + proplists:get_value(Ref, Stats, [])). + +%% info_peers/1 +%% +%% One entry per peer Origin-Host. Statistics for each entry are +%% accumulated values for all peer pids. + +info_peers(S) -> + {PeerD, RefD} = lists:foldl(fun peer_acc/2, + {dict:new(), dict:new()}, + conn_list(S)), + Refs = lists:append(dict:fold(fun(_, Rs, A) -> [Rs|A] end, + [], + RefD)), + Stats = diameter_stats:read(Refs), + dict:fold(fun(OH, Cs, A) -> + Rs = dict:fetch(OH, RefD), + [{OH, [{connections, Cs}, stats(Rs, Stats)]} | A] + end, + [], + PeerD). + +peer_acc(Peer, {PeerD, RefD}) -> + [{TPid, _}, [{origin_host, {_, OH}} | _]] + = [proplists:get_value(K, Peer) || K <- [peer, caps]], + {dict:append(OH, Peer, PeerD), dict:append(OH, TPid, RefD)}. diff --git a/lib/diameter/src/base/diameter_stats.erl b/lib/diameter/src/base/diameter_stats.erl index 71479afa95..70727d068e 100644 --- a/lib/diameter/src/base/diameter_stats.erl +++ b/lib/diameter/src/base/diameter_stats.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -22,14 +22,13 @@ %% -module(diameter_stats). --compile({no_auto_import, [monitor/2]}). -behaviour(gen_server). --export([reg/1, reg/2, - incr/1, incr/2, incr/3, +-export([reg/2, reg/1, + incr/3, incr/1, read/1, - flush/0, flush/1]). + flush/1]). %% supervisor callback -export([start_link/0]). @@ -48,123 +47,105 @@ -include("diameter_internal.hrl"). -%% ets table containing stats. reg(Pid, Ref) inserts a {Pid, Ref}, -%% incr(Counter, X, N) updates the counter keyed at {Counter, X}, and -%% Pid death causes counters keyed on {Counter, Pid} to be deleted and -%% added to those keyed on {Counter, Ref}. +%% ets table containing 2-tuple stats. reg(Pid, Ref) inserts a {Pid, +%% Ref}, incr(Counter, X, N) updates the counter keyed at {Counter, +%% X}, and Pid death causes counters keyed on {Counter, Pid} to be +%% deleted and added to those keyed on {Counter, Ref}. -define(TABLE, ?MODULE). %% Name of registered server. -define(SERVER, ?MODULE). -%% Entries in the table. --define(REC(Key, Value), {Key, Value}). - %% Server state. -record(state, {id = now()}). -type counter() :: any(). --type contrib() :: any(). - -%%% --------------------------------------------------------------------------- -%%% # reg(Pid, Contrib) -%%% -%%% Description: Register a process as a contributor of statistics -%%% associated with a specified term. Statistics can be -%%% contributed by specifying either Pid or Contrib as -%%% the second argument to incr/3. Statistics contributed -%%% by Pid are folded into the corresponding entry for -%%% Contrib when the process dies. -%%% -%%% Contrib can be any term but should not be a pid -%%% passed as the first argument to reg/2. Subsequent -%%% registrations for the same Pid overwrite the association -%%% --------------------------------------------------------------------------- - --spec reg(pid(), contrib()) - -> true. +-type ref() :: any(). + +%% --------------------------------------------------------------------------- +%% # reg(Pid, Ref) +%% +%% Register a process as a contributor of statistics associated with a +%% specified term. Statistics can be contributed by specifying either +%% Pid or Ref as the second argument to incr/3. Statistics contributed +%% by Pid are folded into the corresponding entry for Ref when the +%% process dies. +%% --------------------------------------------------------------------------- + +-spec reg(pid(), ref()) + -> boolean(). -reg(Pid, Contrib) +reg(Pid, Ref) when is_pid(Pid) -> - call({reg, Pid, Contrib}). + call({reg, Pid, Ref}). --spec reg(contrib()) +-spec reg(ref()) -> true. reg(Ref) -> reg(self(), Ref). -%%% --------------------------------------------------------------------------- -%%% # incr(Counter, Contrib, N) -%%% -%%% Description: Increment a counter for the specified contributor. -%%% -%%% Contrib will typically be an argument passed to reg/2 -%%% but there's nothing that requires this. In particular, -%%% if Contrib is a pid that hasn't been registered then -%%% counters are unaffected by the death of the process. -%%% --------------------------------------------------------------------------- - --spec incr(counter(), contrib(), integer()) - -> integer(). +%% --------------------------------------------------------------------------- +%% # incr(Counter, Ref, N) +%% +%% Increment a counter for the specified contributor. +%% +%% Ref will typically be an argument passed to reg/2 but there's +%% nothing that requires this. Only registered pids can contribute +%% counters however, otherwise incr/3 is a no-op. +%% --------------------------------------------------------------------------- -incr(Ctr, Contrib, N) -> - update_counter({Ctr, Contrib}, N). +-spec incr(counter(), ref(), integer()) + -> integer() | false. -incr(Ctr, N) +incr(Ctr, Ref, N) when is_integer(N) -> - incr(Ctr, self(), N); - -incr(Ctr, Contrib) -> - incr(Ctr, Contrib, 1). + update_counter({Ctr, Ref}, N). incr(Ctr) -> incr(Ctr, self(), 1). -%%% --------------------------------------------------------------------------- -%%% # read(Contribs) -%%% -%%% Description: Retrieve counters for the specified contributors. -%%% --------------------------------------------------------------------------- +%% --------------------------------------------------------------------------- +%% # read(Refs) +%% +%% Retrieve counters for the specified contributors. +%% --------------------------------------------------------------------------- --spec read([contrib()]) - -> [{contrib(), [{counter(), integer()}]}]. +-spec read([ref()]) + -> [{ref(), [{counter(), integer()}]}]. -read(Contribs) -> - lists:foldl(fun(?REC({T,C}, N), D) -> orddict:append(C, {T,N}, D) end, +read(Refs) -> + read(Refs, false). + +read(Refs, B) -> + MatchSpec = [{{{'_', '$1'}, '_'}, + [?ORCOND([{'=:=', '$1', {const, R}} + || R <- Refs])], + ['$_']}], + L = ets:select(?TABLE, MatchSpec), + B andalso delete(L), + lists:foldl(fun({{C,R}, N}, D) -> orddict:append(R, {C,N}, D) end, orddict:new(), - ets:select(?TABLE, [{?REC({'_', '$1'}, '_'), - [?ORCOND([{'=:=', '$1', {const, C}} - || C <- Contribs])], - ['$_']}])). - -%%% --------------------------------------------------------------------------- -%%% # flush(Contrib) -%%% -%%% Description: Retrieve and delete statistics for the specified -%%% contributor. -%%% -%%% If Contrib is a pid registered with reg/2 then statistics -%%% for both and its associated contributor are retrieved. -%%% --------------------------------------------------------------------------- - --spec flush(contrib()) - -> [{counter(), integer()}]. + L). + +%% --------------------------------------------------------------------------- +%% # flush(Refs) +%% +%% Retrieve and delete statistics for the specified contributors. +%% --------------------------------------------------------------------------- + +-spec flush([ref()]) + -> [{ref(), {counter(), integer()}}]. -flush(Contrib) -> +flush(Refs) -> try - call({flush, Contrib}) + call({flush, Refs}) catch exit: _ -> [] end. -flush() -> - flush(self()). - -%%% --------------------------------------------------------- -%%% EXPORTED INTERNAL FUNCTIONS -%%% --------------------------------------------------------- +%% =========================================================================== start_link() -> ServerName = {local, ?SERVER}, @@ -179,18 +160,16 @@ state() -> uptime() -> call(uptime). -%%% ---------------------------------------------------------- -%%% # init(_) -%%% -%%% Output: {ok, State} -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # init/1 +%% ---------------------------------------------------------- init([]) -> ets:new(?TABLE, [named_table, ordered_set, public]), {ok, #state{}}. %% ---------------------------------------------------------- -%% handle_call(Request, From, State) +%% # handle_call/3 %% ---------------------------------------------------------- handle_call(state, _, State) -> @@ -199,31 +178,31 @@ handle_call(state, _, State) -> handle_call(uptime, _, #state{id = Time} = State) -> {reply, diameter_lib:now_diff(Time), State}; -handle_call({reg, Pid, Contrib}, _From, State) -> - monitor(not ets:member(?TABLE, Pid), Pid), - {reply, insert(?REC(Pid, Contrib)), State}; +handle_call({incr, T}, _, State) -> + {reply, update_counter(T), State}; -handle_call({flush, Contrib}, _From, State) -> - {reply, fetch(Contrib), State}; +handle_call({reg, Pid, Ref}, _From, State) -> + B = ets:insert_new(?TABLE, {Pid, Ref}), + B andalso erlang:monitor(process, Pid), + {reply, B, State}; + +handle_call({flush, Refs}, _From, State) -> + {reply, read(Refs, true), State}; handle_call(Req, From, State) -> ?UNEXPECTED([Req, From]), {reply, nok, State}. %% ---------------------------------------------------------- -%% handle_cast(Request, State) +%% # handle_cast/2 %% ---------------------------------------------------------- -handle_cast({incr, Rec}, State) -> - update_counter(Rec), - {noreply, State}; - handle_cast(Msg, State) -> ?UNEXPECTED([Msg]), {noreply, State}. %% ---------------------------------------------------------- -%% handle_info(Request, State) +%% # handle_info/2 %% ---------------------------------------------------------- handle_info({'DOWN', _MRef, process, Pid, _}, State) -> @@ -235,91 +214,62 @@ handle_info(Info, State) -> {noreply, State}. %% ---------------------------------------------------------- -%% terminate(Reason, State) +%% # terminate/2 %% ---------------------------------------------------------- terminate(_Reason, _State) -> ok. %% ---------------------------------------------------------- -%% code_change(OldVsn, State, Extra) +%% # code_change/3 %% ---------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. -%%% --------------------------------------------------------- -%%% INTERNAL FUNCTIONS -%%% --------------------------------------------------------- - -%% monitor/2 - -monitor(true, Pid) -> - erlang:monitor(process, Pid); -monitor(false = No, _) -> - No. +%% =========================================================================== %% down/1 down(Pid) -> - L = ets:match_object(?TABLE, ?REC({'_', Pid}, '_')), - [?REC(_, Ref) = T] = lookup(Pid), + down(lookup(Pid), ets:match_object(?TABLE, {{'_', Pid}, '_'})). + +down([{_, Ref} = T], L) -> fold(Ref, L), - delete_object(T), + delete([T|L]); +down([], L) -> %% flushed delete(L). -%% Fold Pid-based entries into Ref-based ones. +%% Fold pid-based entries into ref-based ones. fold(Ref, L) -> - lists:foreach(fun(?REC({K, _}, V)) -> update_counter({{K, Ref}, V}) end, - L). - -delete(Objs) -> - lists:foreach(fun delete_object/1, Objs). - -%% fetch/1 - -fetch(X) -> - MatchSpec = [{?REC({'_', '$1'}, '_'), - [?ORCOND([{'==', '$1', {const, T}} || T <- [X | ref(X)]])], - ['$_']}], - L = ets:select(?TABLE, MatchSpec), - delete(L), - D = lists:foldl(fun sum/2, dict:new(), L), - dict:to_list(D). - -sum({{Ctr, _}, N}, Dict) -> - dict:update(Ctr, fun(V) -> V+N end, N, Dict). - -ref(Pid) - when is_pid(Pid) -> - ets:select(?TABLE, [{?REC(Pid, '$1'), [], ['$1']}]); -ref(_) -> - []. + lists:foreach(fun({{K, _}, V}) -> update_counter({{K, Ref}, V}) end, L). %% update_counter/2 %% -%% From an arbitrary request process. Cast to the server process to -%% insert a new element if the counter doesn't exists so that two -%% processes don't do so simultaneously. +%% From an arbitrary process. Call to the server process to insert a +%% new element if the counter doesn't exists so that two processes +%% don't insert simultaneously. update_counter(Key, N) -> try ets:update_counter(?TABLE, Key, N) catch error: badarg -> - cast({incr, ?REC(Key, N)}) + call({incr, {Key, N}}) end. %% update_counter/1 %% -%% From the server process. +%% From the server process, when update_counter/2 failed due to a +%% non-existent entry. -update_counter(?REC(Key, N) = T) -> +update_counter({{_Ctr, Ref} = Key, N} = T) -> try ets:update_counter(?TABLE, Key, N) catch error: badarg -> - insert(T) + (not is_pid(Ref) orelse ets:member(?TABLE, Ref)) + andalso begin insert(T), N end end. insert(T) -> @@ -328,13 +278,8 @@ insert(T) -> lookup(Key) -> ets:lookup(?TABLE, Key). -delete_object(T) -> - ets:delete_object(?TABLE, T). - -%% cast/1 - -cast(Msg) -> - gen_server:cast(?SERVER, Msg). +delete(Objs) -> + lists:foreach(fun({K,_}) -> ets:delete(?TABLE, K) end, Objs). %% call/1 diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index fb22fd8275..d7474e5c56 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -54,7 +54,7 @@ %% number of DWAs received during reopen %% end PCB parent = self() :: pid(), - transport :: pid(), + transport :: pid() | undefined, tref :: reference(), %% reference for current watchdog timer message_data}). %% term passed into diameter_service with message @@ -64,6 +64,13 @@ %% that a failed capabilities exchange produces the desired exit %% reason. +-spec start(Type, {RecvData, [Opt], SvcName, #diameter_service{}}) + -> {reference(), pid()} + when Type :: {connect|accept, diameter:transport_ref()}, + RecvData :: term(), + Opt :: diameter:transport_opt(), + SvcName :: diameter:service_name(). + start({_,_} = Type, T) -> Ref = make_ref(), {ok, Pid} = diameter_watchdog_sup:start_child({Ref, {Type, self(), T}}), @@ -102,7 +109,7 @@ i({_, Pid, _} = T) -> %% from old code erlang:monitor(process, Pid), make_state(T). -make_state({T, Pid, {ConnT, +make_state({T, Pid, {RecvData, Opts, SvcName, #diameter_service{applications = Apps, @@ -116,7 +123,7 @@ make_state({T, Pid, {ConnT, tw = proplists:get_value(watchdog_timer, Opts, ?DEFAULT_TW_INIT), - message_data = {ConnT, SvcName, Apps}}. + message_data = {RecvData, SvcName, Apps}}. %% handle_call/3 @@ -134,14 +141,36 @@ handle_info(T, State) -> case transition(T, State) of ok -> {noreply, State}; - #watchdog{status = X} = S -> - ?LOGC(X =/= State#watchdog.status, transition, X), + #watchdog{} = S -> + event(State, S), {noreply, S}; stop -> ?LOG(stop, T), + event(State, State#watchdog{status = down}), {stop, {shutdown, T}, State} end. +event(#watchdog{status = T}, #watchdog{status = T}) -> + ok; + +event(#watchdog{transport = undefined}, #watchdog{transport = undefined}) -> + ok; + +event(#watchdog{status = From, transport = F, parent = Pid}, + #watchdog{status = To, transport = T}) -> + E = {tpid(F,T), From, To}, + notify(Pid, E), + ?LOG(transition, {self(), E}). + +tpid(_, Pid) + when is_pid(Pid) -> + Pid; +tpid(Pid, _) -> + Pid. + +notify(Pid, E) -> + Pid ! {watchdog, self(), E}. + %% terminate/2 terminate(_, _) -> @@ -251,8 +280,8 @@ transition({'DOWN', _, process, TPid, _}, status = initial}) -> stop; -transition({'DOWN', _, process, Pid, _}, - #watchdog{transport = Pid} +transition({'DOWN', _, process, TPid, _}, + #watchdog{transport = TPid} = S) -> failover(S), close(S), @@ -385,7 +414,7 @@ recv(Name, Pkt, S) -> rcv(Name, Pkt, S), NS catch - throw: {?MODULE, throwaway, #watchdog{} = NS} -> + {?MODULE, throwaway, #watchdog{} = NS} -> NS end. diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk index 7a700a6d53..5d3c4157ae 100644 --- a/lib/diameter/src/modules.mk +++ b/lib/diameter/src/modules.mk @@ -58,6 +58,7 @@ RT_MODULES = \ transport/diameter_tcp_sup \ transport/diameter_sctp \ transport/diameter_sctp_sup \ + transport/diameter_transport \ transport/diameter_transport_sup # Handwritten (compile time) modules not included in the app file. diff --git a/lib/diameter/src/transport/diameter_etcp.erl b/lib/diameter/src/transport/diameter_etcp.erl index d925d62545..cd62cf34fa 100644 --- a/lib/diameter/src/transport/diameter_etcp.erl +++ b/lib/diameter/src/transport/diameter_etcp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -36,7 +36,9 @@ send/2, close/1, setopts/2, - port/1]). + sockname/1, + peername/1, + getstat/1]). %% child start -export([start_link/1]). @@ -113,10 +115,20 @@ close(Pid) -> setopts(_, _) -> ok. -%% port/1 +%% sockname/1 -port(_) -> - 3868. %% We have no local port: fake it. +sockname(_) -> + {error, ?MODULE}. + +%% peername/1 + +peername(_) -> + {error, ?MODULE}. + +%% getstat/1 + +getstat(_) -> + {error, ?MODULE}. %% start_link/1 diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl index 68b0342cd5..79b8b851fb 100644 --- a/lib/diameter/src/transport/diameter_sctp.erl +++ b/lib/diameter/src/transport/diameter_sctp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -37,12 +37,18 @@ code_change/3, terminate/2]). +-export([info/1]). %% service_info callback + -export([ports/0, ports/1]). -include_lib("kernel/include/inet_sctp.hrl"). -include_lib("diameter/include/diameter.hrl"). +%% Keys into process dictionary. +-define(INFO_KEY, info). +-define(REF_KEY, ref). + -define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})). %% The default port for a listener. @@ -62,6 +68,7 @@ -record(transport, {parent :: pid(), mode :: {accept, pid()} + | accept | {connect, {list(inet:ip_address()), uint(), list()}} %% {RAs, RP, Errors} | connect, @@ -134,6 +141,24 @@ start_link(T) -> diameter_lib:spawn_opts(server, [])). %% --------------------------------------------------------------------------- +%% # info/1 +%% --------------------------------------------------------------------------- + +info({gen_sctp, Sock}) -> + lists:flatmap(fun(K) -> info(K, Sock) end, + [{socket, sockname}, + {peer, peername}, + {statistics, getstat}]). + +info({K,F}, Sock) -> + case inet:F(Sock) of + {ok, V} -> + [{K,V}]; + _ -> + [] + end. + +%% --------------------------------------------------------------------------- %% # init/1 %% --------------------------------------------------------------------------- @@ -157,7 +182,7 @@ i({connect, Pid, Opts, Addrs, Ref}) -> RAs = [diameter_lib:ipaddr(A) || {raddr, A} <- As], [RP] = [P || {rport, P} <- Ps] ++ [P || P <- [?DEFAULT_PORT], [] == Ps], {LAs, Sock} = open(Addrs, Rest, 0), - putr(ref, Ref), + putr(?REF_KEY, Ref), proc_lib:init_ack({ok, self(), LAs}), erlang:monitor(process, Pid), #transport{parent = Pid, @@ -169,7 +194,7 @@ i({connect, _, _, _} = T) -> %% from old code %% An accepting transport spawned by diameter. i({accept, Pid, LPid, Sock, Ref}) when is_pid(Pid) -> - putr(ref, Ref), + putr(?REF_KEY, Ref), proc_lib:init_ack({ok, self()}), erlang:monitor(process, Pid), erlang:monitor(process, LPid), @@ -181,7 +206,7 @@ i({accept, _, _, _} = T) -> %% from old code %% An accepting transport spawned at association establishment. i({accept, Ref, LPid, Sock, Id}) -> - putr(ref, Ref), + putr(?REF_KEY, Ref), proc_lib:init_ack({ok, self()}), MRef = erlang:monitor(process, LPid), %% Wait for a signal that the transport has been started before @@ -325,6 +350,11 @@ terminate(_, #transport{assoc_id = undefined}) -> ok; terminate(_, #transport{socket = Sock, + mode = accept, + assoc_id = Id}) -> + close(Sock, Id); + +terminate(_, #transport{socket = Sock, mode = {accept, _}, assoc_id = Id}) -> close(Sock, Id); @@ -356,13 +386,16 @@ start_timer(S) -> %% Incoming message from SCTP. l({sctp, Sock, _RA, _RP, Data} = Msg, #listener{socket = Sock} = S) -> - setopts(Sock), - case find(Data, S) of + Id = assoc_id(Data), + + try find(Id, Data, S) of {TPid, NewS} -> - TPid ! Msg, + TPid ! {peeloff, peeloff(Sock, Id, TPid), Msg}, NewS; false -> S + after + setopts(Sock) end; %% Transport is asking message to be sent. See send/3 for why the send @@ -430,15 +463,19 @@ t(T,S) -> %% transition/2 -%% Incoming message. -transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock, - mode = {accept, _}} - = S) -> - recv(Data, S); +%% Listening process is transfering ownership of an association. +transition({peeloff, Sock, {sctp, LSock, _RA, _RP, _Data} = Msg}, + #transport{mode = {accept, _}, + socket = LSock} + = S) -> + transition(Msg, S#transport{socket = Sock}); -transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) -> +%% Incoming message. +transition({sctp, _Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) -> setopts(Sock), recv(Data, S); +%% Don't match on Sock since in R15B01 it can be the listening socket +%% in the (peeled-off) accept case, which is likely a bug. %% Outgoing message. transition({diameter, {send, Msg}}, S) -> @@ -456,13 +493,18 @@ transition({diameter, {close, Pid}}, #transport{parent = Pid}) -> transition({diameter, {tls, _Ref, _Type, _Bool}}, _) -> stop; +%% Parent process has died. +transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) -> + stop; + %% Listener process has died. transition({'DOWN', _, process, Pid, _}, #transport{mode = {accept, Pid}}) -> stop; -%% Parent process has died. -transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) -> - stop; +%% Ditto but we have ownership of the association. It might be that +%% we'll go down anyway though. +transition({'DOWN', _, process, _Pid, _}, #transport{mode = accept}) -> + ok; %% Request for the local port number. transition({resolve_port, Pid}, #transport{socket = Sock}) @@ -521,14 +563,6 @@ send(Bin, #transport{streams = {_, OS}, %% send/3 -%% Messages have to be sent from the controlling process, which is -%% probably a bug. Sending from here causes an inet_reply, Sock, -%% Status} message to be sent to the controlling process while -%% gen_sctp:send/4 here hangs. -send(StreamId, Bin, #transport{assoc_id = AId, - mode = {accept, LPid}}) -> - LPid ! {send, AId, StreamId, Bin}; - send(StreamId, Bin, #transport{socket = Sock, assoc_id = AId}) -> send(Sock, AId, StreamId, Bin). @@ -554,10 +588,9 @@ recv({_, #sctp_assoc_change{state = comm_up, mode = {T, _}, socket = Sock} = S) -> - Ref = getr(ref), + Ref = getr(?REF_KEY), is_reference(Ref) %% started in new code - andalso - (true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}})), + andalso publish(T, Ref, Id, Sock), up(S#transport{assoc_id = Id, streams = {IS, OS}}); @@ -599,6 +632,10 @@ recv({_, #sctp_paddr_change{}}, _) -> recv({_, #sctp_pdapi_event{}}, _) -> ok. +publish(T, Ref, Id, Sock) -> + true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}}), + putr(?INFO_KEY, {gen_sctp, Sock}). %% for info/1 + %% up/1 up(#transport{parent = Pid, @@ -608,21 +645,15 @@ up(#transport{parent = Pid, S#transport{mode = C}; up(#transport{parent = Pid, - mode = {accept, _}} + mode = {accept = A, _}} = S) -> diameter_peer:up(Pid), - S. + S#transport{mode = A}. -%% find/2 - -find({[#sctp_sndrcvinfo{assoc_id = Id}], _} - = Data, - #listener{tmap = T} - = S) -> - f(ets:lookup(T, Id), Data, S); +%% find/3 -find({_, Rec} = Data, #listener{tmap = T} = S) -> - f(ets:lookup(T, assoc_id(Rec)), Data, S). +find(Id, Data, #listener{tmap = T} = S) -> + f(ets:lookup(T, Id), Data, S). %% New association and a transport waiting for one: use it. f([], @@ -663,17 +694,29 @@ f([], _, _) -> %% assoc_id/1 -assoc_id(#sctp_shutdown_event{assoc_id = Id}) -> +assoc_id({[#sctp_sndrcvinfo{assoc_id = Id}], _}) -> Id; -assoc_id(#sctp_assoc_change{assoc_id = Id}) -> +assoc_id({_, Rec}) -> + id(Rec). + +id(#sctp_shutdown_event{assoc_id = Id}) -> + Id; +id(#sctp_assoc_change{assoc_id = Id}) -> Id; -assoc_id(#sctp_sndrcvinfo{assoc_id = Id}) -> +id(#sctp_sndrcvinfo{assoc_id = Id}) -> Id; -assoc_id(#sctp_paddr_change{assoc_id = Id}) -> +id(#sctp_paddr_change{assoc_id = Id}) -> Id; -assoc_id(#sctp_adaptation_event{assoc_id = Id}) -> +id(#sctp_adaptation_event{assoc_id = Id}) -> Id. +%% peeloff/3 + +peeloff(LSock, Id, TPid) -> + {ok, Sock} = gen_sctp:peeloff(LSock, Id), + ok = gen_sctp:controlling_process(Sock, TPid), + Sock. + %% connect/4 connect(_, [], _, Reasons) -> diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index f98ae84cf4..f3fbbee609 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -37,11 +37,17 @@ code_change/3, terminate/2]). +-export([info/1]). %% service_info callback + -export([ports/0, ports/1]). -include_lib("diameter/include/diameter.hrl"). +%% Keys into process dictionary. +-define(INFO_KEY, info). +-define(REF_KEY, ref). + -define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})). -define(DEFAULT_PORT, 3868). %% RFC 3588, ch 2.1 @@ -111,6 +117,33 @@ start_link(T) -> diameter_lib:spawn_opts(server, [])). %% --------------------------------------------------------------------------- +%% # info/1 +%% --------------------------------------------------------------------------- + +info({Mod, Sock}) -> + lists:flatmap(fun(K) -> info(Mod, K, Sock) end, + [{socket, fun sockname/2}, + {peer, fun peername/2}, + {statistics, fun getstat/2} + | ssl_info(Mod, Sock)]). + +info(Mod, {K,F}, Sock) -> + case F(Mod, Sock) of + {ok, V} -> + [{K,V}]; + _ -> + [] + end. + +ssl_info(ssl = M, Sock) -> + [{M, ssl_info(Sock)}]; +ssl_info(_, _) -> + []. + +ssl_info(Sock) -> + [{peercert, C} || {ok, C} <- [ssl:peercert(Sock)]]. + +%% --------------------------------------------------------------------------- %% # init/1 %% --------------------------------------------------------------------------- @@ -133,7 +166,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs}) MPid ! {stop, self()}, %% tell the monitor to die M = if SslOpts -> ssl; true -> Mod end, setopts(M, Sock), - putr(ref, Ref), + putr(?REF_KEY, Ref), #transport{parent = Pid, module = M, socket = Sock, @@ -191,7 +224,7 @@ i(accept = T, Ref, Mod, Pid, Opts, Addrs) -> {LAddr, LSock} = listener(Ref, {Mod, Opts, Addrs}), proc_lib:init_ack({ok, self(), [LAddr]}), Sock = ok(accept(Mod, LSock)), - true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}), + publish(Mod, T, Ref, Sock), diameter_peer:up(Pid), Sock; @@ -202,10 +235,14 @@ i(connect = T, Ref, Mod, Pid, Opts, Addrs) -> RPort = get_port(RP), proc_lib:init_ack({ok, self(), [LAddr]}), Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddr, Rest))), - true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}), + publish(Mod, T, Ref, Sock), diameter_peer:up(Pid, {RAddr, RPort}), Sock. +publish(Mod, T, Ref, Sock) -> + true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}), + putr(?INFO_KEY, {Mod, Sock}). %% for info/1 + ok({ok, T}) -> T; ok(No) -> @@ -521,7 +558,7 @@ tls_handshake(Type, true, #transport{socket = Sock, ssl = Opts} = S) -> {ok, SSock} = tls(Type, Sock, [{cb_info, ?TCP_CB(M)} | Opts]), - Ref = getr(ref), + Ref = getr(?REF_KEY), is_reference(Ref) %% started in new code andalso (true = diameter_reg:add_new({?MODULE, Type, {Ref, SSock}})), @@ -696,12 +733,32 @@ setopts(M, Sock) -> portnr(gen_tcp, Sock) -> inet:port(Sock); -portnr(ssl, Sock) -> - case ssl:sockname(Sock) of +portnr(M, Sock) -> + case M:sockname(Sock) of {ok, {_Addr, PortNr}} -> {ok, PortNr}; {error, _} = No -> No - end; -portnr(M, Sock) -> - M:port(Sock). + end. + +%% sockname/2 + +sockname(gen_tcp, Sock) -> + inet:sockname(Sock); +sockname(M, Sock) -> + M:sockname(Sock). + +%% peername/2 + +peername(gen_tcp, Sock) -> + inet:peername(Sock); +peername(M, Sock) -> + M:peername(Sock). + +%% getstat/2 + +getstat(gen_tcp, Sock) -> + inet:getstat(Sock); +getstat(M, Sock) -> + M:getstat(Sock). +%% Note that ssl:getstat/1 doesn't yet exist in R15B01. diff --git a/lib/diameter/src/transport/diameter_transport.erl b/lib/diameter/src/transport/diameter_transport.erl new file mode 100644 index 0000000000..ff4b6bbc6d --- /dev/null +++ b/lib/diameter/src/transport/diameter_transport.erl @@ -0,0 +1,55 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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(diameter_transport). + +%% +%% This module implements a transport start function that +%% evaluates its config argument. +%% + +%% Transport start functions +-export([start/3, + select/3, + eval/3]). + +%% start/3 + +%% Call a start function in this module ... +start(T, Svc, {F,A}) -> + start(T, Svc, {?MODULE, F, [A]}); + +%% ... or some other. +start(T, Svc, F) -> + diameter_lib:eval([F, T, Svc]). + +%% select/3 +%% +%% A start function that whose config argument is expected to return a +%% new start function. + +select(T, Svc, F) -> + start(T, Svc, diameter_lib:eval([F, T, Svc])). + +%% eval/3 +%% +%% A start function that simply evaluates its config argument. + +eval(_, _, F) -> + diameter_lib:eval(F). diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile index 5203ac4854..616fcca0c0 100644 --- a/lib/diameter/test/Makefile +++ b/lib/diameter/test/Makefile @@ -146,7 +146,7 @@ log: # ---------------------------------------------------- /%: % force - sed -f release.sed $< > $(RELSYSDIR)$@ + sed -f release.sed $< > "$(RELSYSDIR)$@" ifeq ($(ERL_TOP),) include $(DIAMETER_TOP)/make/release_targets.mk @@ -157,17 +157,17 @@ endif release_spec release_docs_spec: release_tests_spec: - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(TEST_SPEC_FILE) \ $(COVER_SPEC_FILE) \ $(HRL_FILES) \ - $(RELSYSDIR) + "$(RELSYSDIR)" $(MAKE) $(DATA_DIRS:%/=release_data_%) $(MAKE) $(ERL_FILES:%=/%) $(DATA_DIRS:%/=release_data_%): release_data_%: - $(INSTALL_DIR) $(RELSYSDIR)/$* - $(INSTALL_DATA) $(filter $*/%, $(DATA)) $(RELSYSDIR)/$* + $(INSTALL_DIR) "$(RELSYSDIR)/$*" + $(INSTALL_DATA) $(filter $*/%, $(DATA)) "$(RELSYSDIR)/$*" force: diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl index 54a161d606..ae128b8203 100644 --- a/lib/diameter/test/diameter_capx_SUITE.erl +++ b/lib/diameter/test/diameter_capx_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -93,12 +93,12 @@ -define(fail(T), erlang:error({T, process_info(self(), messages)})). --define(TIMEOUT, 2000). +-define(TIMEOUT, 10000). %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start, start_services, diff --git a/lib/diameter/test/diameter_compiler_SUITE.erl b/lib/diameter/test/diameter_compiler_SUITE.erl index 3b4c9706e0..4b792b5426 100644 --- a/lib/diameter/test/diameter_compiler_SUITE.erl +++ b/lib/diameter/test/diameter_compiler_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -339,7 +339,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 5}}]. + [{timetrap, {minutes, 2}}]. all() -> [format, diff --git a/lib/diameter/test/diameter_dict_SUITE.erl b/lib/diameter/test/diameter_dict_SUITE.erl index 5cf8506d3f..3cc65c0257 100644 --- a/lib/diameter/test/diameter_dict_SUITE.erl +++ b/lib/diameter/test/diameter_dict_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -48,7 +48,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [{group, all}, diff --git a/lib/diameter/test/diameter_failover_SUITE.erl b/lib/diameter/test/diameter_failover_SUITE.erl index 53398dd93e..ed31670031 100644 --- a/lib/diameter/test/diameter_failover_SUITE.erl +++ b/lib/diameter/test/diameter_failover_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -101,7 +101,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start, diff --git a/lib/diameter/test/diameter_gen_sctp_SUITE.erl b/lib/diameter/test/diameter_gen_sctp_SUITE.erl index acc3b9e254..2fde7b9fdb 100644 --- a/lib/diameter/test/diameter_gen_sctp_SUITE.erl +++ b/lib/diameter/test/diameter_gen_sctp_SUITE.erl @@ -347,8 +347,15 @@ receive_what_was_sent(_Config) -> %% open/0 open() -> - gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary]). + open([]). +%% open/1 + +open(Opts) -> + gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary, + {recbuf, 1 bsl 16}, {sndbuf, 1 bsl 16} + | Opts]). + %% assoc/1 assoc(Sock) -> diff --git a/lib/diameter/test/diameter_reg_SUITE.erl b/lib/diameter/test/diameter_reg_SUITE.erl index ec6a0ca731..4939019f7a 100644 --- a/lib/diameter/test/diameter_reg_SUITE.erl +++ b/lib/diameter/test/diameter_reg_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -43,7 +43,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [{group, all}, diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl index 70e1866791..f10d82bdf8 100644 --- a/lib/diameter/test/diameter_relay_SUITE.erl +++ b/lib/diameter/test/diameter_relay_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -48,6 +48,7 @@ send_loop/1, send_timeout_1/1, send_timeout_2/1, + info/1, disconnect/1, stop_services/1, stop/1]). @@ -112,7 +113,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start, @@ -136,7 +137,8 @@ tc() -> send4, send_loop, send_timeout_1, - send_timeout_2]. + send_timeout_2, + info]. %% =========================================================================== %% start/stop testcases @@ -224,6 +226,9 @@ send_timeout(Tmo) -> {'Re-Auth-Request-Type', ?AUTHORIZE_ONLY}], call(Req, [{filter, realm}, {timeout, Tmo}]). +info(_Config) -> + [] = ?util:info(). + %% =========================================================================== realm(Host) -> diff --git a/lib/diameter/test/diameter_stats_SUITE.erl b/lib/diameter/test/diameter_stats_SUITE.erl index e7807fd360..8b7d8cb1b6 100644 --- a/lib/diameter/test/diameter_stats_SUITE.erl +++ b/lib/diameter/test/diameter_stats_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -30,16 +30,17 @@ end_per_suite/1]). %% testcases --export([an/1, - twa/1]). +-export([reg/1, + incr/1, + read/1, + flush/1]). -define(stat, diameter_stats). --define(util, diameter_util). %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [{group, all}, @@ -49,8 +50,10 @@ groups() -> [{all, [], tc()}]. tc() -> - [an, - twa]. + [reg, + incr, + read, + flush]. init_per_suite(Config) -> ok = diameter:start(), @@ -61,25 +64,62 @@ end_per_suite(_Config) -> %% =========================================================================== -an(_) -> - Ref = {'_', make_ref()}, +reg(_) -> + Ref = '$1', true = ?stat:reg(Ref), - true = ?stat:reg(Ref), %% duplicate - ok = ?stat:incr(x), - ok = ?stat:incr(x, Ref), - ok = ?stat:incr(y, 2), - ok = ?stat:incr(y, Ref), - %% Flushing a pid flushes even stats on the registered reference. - [{x,2},{y,3}] = lists:sort(?stat:flush()), - [] = ?stat:flush(Ref), - [] = ?stat:flush(). - -twa(_) -> + false = ?stat:reg(Ref). %% duplicate + +incr(_) -> + Ref = '_', + Ctr = x, + false = ?stat:incr(Ctr), %% not registered, + 1 = ?stat:incr(Ctr, Ref, 1), %% only pids need register + true = ?stat:reg(Ref), + spawn(fun() -> + true = ?stat:reg(Ref), + 2 = ?stat:incr(Ctr, self(), 2) + end), + ok = fold(Ctr, Ref, 3), %% folded + ?stat:flush([self(), Ref]). + +read(_) -> + Ref = make_ref(), + C1 = {a,b}, + C2 = {b,a}, + true = ?stat:reg(Ref), + 1 = ?stat:incr(C1), + 1 = ?stat:incr(C2), + 2 = ?stat:incr(C1), + 7 = ?stat:incr(C1, Ref, 7), + Self = self(), + [{Ref, [{C1,7}]}, {Self, [{C1,2}, {C2,1}]}] + = lists:sort(?stat:read([self(), Ref, make_ref()])), + [] = ?stat:read([]), + [] = ?stat:read([make_ref()]), + ?stat:flush([self(), Ref, make_ref()]). + +flush(_) -> Ref = make_ref(), - ok = ?stat:incr(x, 8), - ok = ?stat:incr(x, Ref, 7), - %% Flushing a reference doesn't affect registered pids. - [{x,7}] = ?stat:flush(Ref), - [] = ?stat:flush(Ref), - [{x,8}] = ?stat:flush(), - [] = ?stat:flush(). + Ctr = '_', + true = ?stat:reg(Ref), + 1 = ?stat:incr(Ctr), + 3 = ?stat:incr(Ctr, self(), 2), + 2 = ?stat:incr(Ctr, Ref, 2), + Self = self(), + [{Self, [{Ctr, 3}]}] = ?stat:flush([self()]), + 1 = ?stat:incr(Ctr), + [{Ref, [{Ctr, 2}]}] = ?stat:flush([Ref]), + [{Self, [{Ctr, 1}]}] = ?stat:flush([self()]), + [] = ?stat:flush([self(), Ref]). + +%% =========================================================================== + +%% Keep incremented until a fold results in the specified value. +fold(Ctr, Ref, N) -> + case ?stat:incr(Ctr, Ref, 0) of + N -> + ok; + M when M < N -> + erlang:yield(), + fold(Ctr, Ref, N) + end. diff --git a/lib/diameter/test/diameter_sync_SUITE.erl b/lib/diameter/test/diameter_sync_SUITE.erl index ab629fb1c1..457efab8ae 100644 --- a/lib/diameter/test/diameter_sync_SUITE.erl +++ b/lib/diameter/test/diameter_sync_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -43,7 +43,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [{group, all}, diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl index 85b953dc1a..6cc34b20c5 100644 --- a/lib/diameter/test/diameter_tls_SUITE.erl +++ b/lib/diameter/test/diameter_tls_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -127,7 +127,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start_ssl, diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 6eed8d3b5d..99b4fc7f63 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -159,7 +159,7 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start, start_services, add_transports, result_codes] diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl index 0c42f955ad..890d24f6f8 100644 --- a/lib/diameter/test/diameter_util.erl +++ b/lib/diameter/test/diameter_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -35,7 +35,8 @@ lport/3, listen/2, listen/3, connect/3, connect/4, - disconnect/4]). + disconnect/4, + info/0]). %% common_test-specific -export([write_priv/3, @@ -262,7 +263,10 @@ listen(SvcName, Prot) -> listen(SvcName, Prot, []). listen(SvcName, Prot, Opts) -> - add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}). + SvcName = diameter:service_info(SvcName, name), %% assert + Ref = add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}), + true = transport(SvcName, Ref), %% assert + Ref. %% --------------------------------------------------------------------------- %% connect/2-3 @@ -275,15 +279,22 @@ connect(Client, Prot, LRef) -> connect(Client, Prot, LRef, Opts) -> [PortNr] = lport(Prot, LRef, 20), - Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}), + Client = diameter:service_info(Client, name), %% assert true = diameter:subscribe(Client), + Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}), + true = transport(Client, Ref), %% assert + ok = receive {diameter_event, Client, {up, Ref, _, _, _}} -> ok - after 2000 -> + after 10000 -> {Client, Prot, PortNr, process_info(self(), messages)} end, Ref. +transport(SvcName, Ref) -> + [Ref] == [R || [{ref, R} | _] <- diameter:service_info(SvcName, transport), + R == Ref]. + %% --------------------------------------------------------------------------- %% disconnect/4 %% @@ -295,7 +306,7 @@ disconnect(Client, Ref, Server, LRef) -> ok = diameter:remove_transport(Client, Ref), ok = receive {diameter_event, Server, {down, LRef, _, _}} -> ok - after 2000 -> + after 10000 -> {Client, Ref, Server, LRef, process_info(self(), messages)} end. @@ -320,3 +331,17 @@ opts(listen) -> []; opts(PortNr) -> [{raddr, ?ADDR}, {rport, PortNr}]. + +%% --------------------------------------------------------------------------- +%% info/0 + +info() -> + [_|_] = Svcs = diameter:services(), %% assert + run([[fun info/1, S] || S <- Svcs]). + +info(S) -> + [_|_] = Keys = diameter:service_info(S, keys), + [] = run([[fun info/2, K, S] || K <- Keys]). + +info(Key, SvcName) -> + [{Key, _}] = diameter:service_info(SvcName, [Key]). diff --git a/lib/edoc/doc/src/Makefile b/lib/edoc/doc/src/Makefile index b933094464..1f92d36f12 100644 --- a/lib/edoc/doc/src/Makefile +++ b/lib/edoc/doc/src/Makefile @@ -124,13 +124,13 @@ 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/edoc/include/Makefile b/lib/edoc/include/Makefile index 5b2ad38c9d..cf2e051632 100644 --- a/lib/edoc/include/Makefile +++ b/lib/edoc/include/Makefile @@ -49,8 +49,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(INCLUDE_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(INCLUDE_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/edoc/priv/Makefile b/lib/edoc/priv/Makefile index 13225e6d1a..73c42c05eb 100644 --- a/lib/edoc/priv/Makefile +++ b/lib/edoc/priv/Makefile @@ -45,9 +45,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(PRIV_FILES) $(RELSYSDIR)/priv - $(INSTALL_SCRIPT) $(GEN_SCRIPT) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(PRIV_FILES) "$(RELSYSDIR)/priv" + $(INSTALL_SCRIPT) $(GEN_SCRIPT) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/edoc/src/Makefile b/lib/edoc/src/Makefile index fcb0b61292..72354ac711 100644 --- a/lib/edoc/src/Makefile +++ b/lib/edoc/src/Makefile @@ -85,10 +85,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(OBJECTS) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(SOURCES) $(HRL_FILES) $(YRL_FILE) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(OBJECTS) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(SOURCES) $(HRL_FILES) $(YRL_FILE) "$(RELSYSDIR)/src" release_docs_spec: diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl index aad0b14371..624f9177a2 100644 --- a/lib/edoc/src/edoc_data.erl +++ b/lib/edoc/src/edoc_data.erl @@ -167,7 +167,10 @@ callbacks(Es, Module, Env, Opts) -> case lists:any(fun (#entry{name = {behaviour_info, 1}}) -> true; (_) -> false end, - Es) of + Es) + orelse + lists:keymember(callback, 1, Module#module.attributes) + of true -> try (Module#module.name):behaviour_info(callbacks) of Fs -> diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl index 7fd8358add..90fb8a679c 100644 --- a/lib/edoc/src/edoc_lib.erl +++ b/lib/edoc/src/edoc_lib.erl @@ -469,6 +469,10 @@ uri_get("ftp:" ++ Path) -> uri_get("//" ++ Path) -> Msg = io_lib:format("cannot access network-path: '//~s'.", [Path]), {error, Msg}; +uri_get([C, $:, $/ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z -> + uri_get_file(Path); % special case for Windows +uri_get([C, $:, $\ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z -> + uri_get_file(Path); % special case for Windows uri_get(URI) -> case is_relative_uri(URI) of true -> diff --git a/lib/edoc/test/Makefile b/lib/edoc/test/Makefile index 2dbdb77eff..2033e003b3 100644 --- a/lib/edoc/test/Makefile +++ b/lib/edoc/test/Makefile @@ -57,10 +57,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) edoc.spec edoc.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) edoc.spec edoc.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk index b8f33894f1..2f403212c8 100644 --- a/lib/edoc/vsn.mk +++ b/lib/edoc/vsn.mk @@ -1 +1 @@ -EDOC_VSN = 0.7.9.1 +EDOC_VSN = 0.7.10 diff --git a/lib/eldap/.gitignore b/lib/eldap/.gitignore index 5585418186..d3dd8228d5 100644 --- a/lib/eldap/.gitignore +++ b/lib/eldap/.gitignore @@ -1,4 +1,4 @@ *.beam *.asn1db -src/ELDAPv3.hrl -src/ELDAPv3.erl +ebin/ELDAPv3.hrl +ebin/ELDAPv3.erl diff --git a/lib/eldap/doc/src/Makefile b/lib/eldap/doc/src/Makefile index 4c827319b4..a4827793f4 100644 --- a/lib/eldap/doc/src/Makefile +++ b/lib/eldap/doc/src/Makefile @@ -99,16 +99,16 @@ clean clean_docs clean_tex: 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 -# $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 -# $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" +# $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" +# $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/eldap/src/Makefile b/lib/eldap/src/Makefile index 4ddb8082d7..39a41d08e2 100644 --- a/lib/eldap/src/Makefile +++ b/lib/eldap/src/Makefile @@ -96,14 +96,14 @@ $(ASN1_HRL): ../asn1/$(ASN1_FILES) include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/asn1 - $(INSTALL_DATA) ../asn1/$(ASN1_FILES) $(RELSYSDIR)/asn1 - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/asn1" + $(INSTALL_DATA) ../asn1/$(ASN1_FILES) "$(RELSYSDIR)/asn1" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/eldap/test/Makefile b/lib/eldap/test/Makefile index a17d4f56b2..3c5810eece 100644 --- a/lib/eldap/test/Makefile +++ b/lib/eldap/test/Makefile @@ -50,7 +50,7 @@ RELSYSDIR = $(RELEASE_PATH)/eldap_test # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += $(INCLUDES) +ERL_COMPILE_FLAGS += $(INCLUDES) -pa $(ERL_TOP)/lib/eldap/ebin EBIN = . @@ -75,9 +75,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) -# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" +# @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/erl_docgen/doc/src/Makefile b/lib/erl_docgen/doc/src/Makefile index ff50c12895..a254c4fb6d 100644 --- a/lib/erl_docgen/doc/src/Makefile +++ b/lib/erl_docgen/doc/src/Makefile @@ -124,13 +124,13 @@ debug opt: 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/man6 - $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/erl_docgen/priv/bin/Makefile b/lib/erl_docgen/priv/bin/Makefile index 95ad36216a..449cc6691c 100644 --- a/lib/erl_docgen/priv/bin/Makefile +++ b/lib/erl_docgen/priv/bin/Makefile @@ -64,8 +64,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(ESCRIPT_FILES) $(RELSYSDIR)/priv/bin + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_SCRIPT) $(ESCRIPT_FILES) "$(RELSYSDIR)/priv/bin" release_docs_spec: diff --git a/lib/erl_docgen/priv/css/Makefile b/lib/erl_docgen/priv/css/Makefile index 81124fb111..0faca019f3 100644 --- a/lib/erl_docgen/priv/css/Makefile +++ b/lib/erl_docgen/priv/css/Makefile @@ -64,13 +64,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/css - $(INSTALL_DATA) $(CSS_FILES) $(RELSYSDIR)/priv/css + $(INSTALL_DIR) "$(RELSYSDIR)/priv/css" + $(INSTALL_DATA) $(CSS_FILES) "$(RELSYSDIR)/priv/css" release_docs_spec: - $(INSTALL_DIR) $(RELEASE_PATH)/doc - $(INSTALL_DATA) $(CSS_FILES) ../nyi.html $(RELEASE_PATH)/doc + $(INSTALL_DIR) "$(RELEASE_PATH)/doc" + $(INSTALL_DATA) $(CSS_FILES) ../nyi.html "$(RELEASE_PATH)/doc" release_tests_spec: diff --git a/lib/erl_docgen/priv/dtd/Makefile b/lib/erl_docgen/priv/dtd/Makefile index 65c68fcca7..314374d34b 100644 --- a/lib/erl_docgen/priv/dtd/Makefile +++ b/lib/erl_docgen/priv/dtd/Makefile @@ -89,8 +89,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/dtd - $(INSTALL_DATA) $(DTD_FILES) $(ENT_FILES) $(RELSYSDIR)/priv/dtd + $(INSTALL_DIR) "$(RELSYSDIR)/priv/dtd" + $(INSTALL_DATA) $(DTD_FILES) $(ENT_FILES) "$(RELSYSDIR)/priv/dtd" release_docs_spec: diff --git a/lib/erl_docgen/priv/dtd_html_entities/Makefile b/lib/erl_docgen/priv/dtd_html_entities/Makefile index d57302bd1b..9cd3f38c4b 100644 --- a/lib/erl_docgen/priv/dtd_html_entities/Makefile +++ b/lib/erl_docgen/priv/dtd_html_entities/Makefile @@ -63,8 +63,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/dtd_html_entities - $(INSTALL_DATA) $(ENTITY_FILES) $(RELSYSDIR)/priv/dtd_html_entities + $(INSTALL_DIR) "$(RELSYSDIR)/priv/dtd_html_entities" + $(INSTALL_DATA) $(ENTITY_FILES) "$(RELSYSDIR)/priv/dtd_html_entities" release_docs_spec: diff --git a/lib/erl_docgen/priv/dtd_man_entities/Makefile b/lib/erl_docgen/priv/dtd_man_entities/Makefile index 5651d8bc29..b6f29af6b4 100644 --- a/lib/erl_docgen/priv/dtd_man_entities/Makefile +++ b/lib/erl_docgen/priv/dtd_man_entities/Makefile @@ -63,8 +63,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/dtd_man_entities - $(INSTALL_DATA) $(ENTITY_FILES) $(RELSYSDIR)/priv/dtd_man_entities + $(INSTALL_DIR) "$(RELSYSDIR)/priv/dtd_man_entities" + $(INSTALL_DATA) $(ENTITY_FILES) "$(RELSYSDIR)/priv/dtd_man_entities" release_docs_spec: diff --git a/lib/erl_docgen/priv/images/Makefile b/lib/erl_docgen/priv/images/Makefile index 8b858fefb3..389f9f98ae 100644 --- a/lib/erl_docgen/priv/images/Makefile +++ b/lib/erl_docgen/priv/images/Makefile @@ -66,13 +66,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/images - $(INSTALL_DATA) $(GIF_FILES) $(PNG_FILES) $(RELSYSDIR)/priv/images + $(INSTALL_DIR) "$(RELSYSDIR)/priv/images" + $(INSTALL_DATA) $(GIF_FILES) $(PNG_FILES) "$(RELSYSDIR)/priv/images" release_docs_spec: - $(INSTALL_DIR) $(RELEASE_PATH)/doc - $(INSTALL_DATA) $(PNG_FILES) $(RELEASE_PATH)/doc + $(INSTALL_DIR) "$(RELEASE_PATH)/doc" + $(INSTALL_DATA) $(PNG_FILES) "$(RELEASE_PATH)/doc" release_tests_spec: diff --git a/lib/erl_docgen/priv/js/flipmenu/Makefile b/lib/erl_docgen/priv/js/flipmenu/Makefile index 65c5c91f35..682d186608 100644 --- a/lib/erl_docgen/priv/js/flipmenu/Makefile +++ b/lib/erl_docgen/priv/js/flipmenu/Makefile @@ -68,13 +68,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/js/flipmenu - $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) $(RELSYSDIR)/priv/js/flipmenu + $(INSTALL_DIR) "$(RELSYSDIR)/priv/js/flipmenu" + $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) "$(RELSYSDIR)/priv/js/flipmenu" release_docs_spec: - $(INSTALL_DIR) $(RELEASE_PATH)/doc/js/flipmenu - $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) $(RELEASE_PATH)/doc/js/flipmenu + $(INSTALL_DIR) "$(RELEASE_PATH)/doc/js/flipmenu" + $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) "$(RELEASE_PATH)/doc/js/flipmenu" release_tests_spec: diff --git a/lib/erl_docgen/priv/xsl/Makefile b/lib/erl_docgen/priv/xsl/Makefile index 92a53b8c0b..ca7a4e8157 100644 --- a/lib/erl_docgen/priv/xsl/Makefile +++ b/lib/erl_docgen/priv/xsl/Makefile @@ -68,8 +68,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/xsl - $(INSTALL_DATA) $(XSL_FILES) $(RELSYSDIR)/priv/xsl + $(INSTALL_DIR) "$(RELSYSDIR)/priv/xsl" + $(INSTALL_DATA) $(XSL_FILES) "$(RELSYSDIR)/priv/xsl" release_docs_spec: diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index 7cf5465f90..4bc5abb364 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -1817,7 +1817,14 @@ <xsl:choose> <xsl:when test="ancestor::cref"> - <a name="{substring-before(nametext, '(')}"><span class="bold_code"><xsl:value-of select="ret"/><xsl:text> </xsl:text><xsl:value-of select="nametext"/></span></a><br/> + <a name="{substring-before(nametext, '(')}"> + <span class="bold_code"> + <xsl:value-of select="ret"/> + <xsl:call-template name="maybe-space-after-ret"> + <xsl:with-param name="s" select="ret"/> + </xsl:call-template> + <xsl:value-of select="nametext"/> + </span></a><br/> </xsl:when> <xsl:when test="ancestor::erlref"> <xsl:variable name="fname"> @@ -1845,6 +1852,18 @@ </xsl:template> + <xsl:template name="maybe-space-after-ret"> + <xsl:param name="s"/> + <xsl:variable name="last_char" + select="substring($s, string-length($s), 1)"/> + <xsl:choose> + <xsl:when test="$last_char != '*'"> + <xsl:text> </xsl:text> + </xsl:when> + </xsl:choose> + </xsl:template> + + <!-- Type --> <xsl:template match="type"> <xsl:param name="partnum"/> diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl index 5234ba6bd0..33808859c7 100644 --- a/lib/erl_docgen/priv/xsl/db_man.xsl +++ b/lib/erl_docgen/priv/xsl/db_man.xsl @@ -3,7 +3,7 @@ # # %CopyrightBegin% # - # Copyright Ericsson AB 2009-2011. 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 @@ -758,10 +758,32 @@ <xsl:template name="name"> <xsl:text> .B </xsl:text> - <xsl:apply-templates/> + <xsl:choose> + <xsl:when test="ancestor::cref"> + <xsl:value-of select="ret"/> + <xsl:call-template name="maybe-space-after-ret"> + <xsl:with-param name="s" select="ret"/> + </xsl:call-template> + <xsl:value-of select="nametext"/> + </xsl:when> + <xsl:otherwise> + <xsl:apply-templates/> + </xsl:otherwise> + </xsl:choose> <xsl:text> .br</xsl:text> </xsl:template> + <xsl:template name="maybe-space-after-ret"> + <xsl:param name="s"/> + <xsl:variable name="last_char" + select="substring($s, string-length($s), 1)"/> + <xsl:choose> + <xsl:when test="$last_char != '*'"> + <xsl:text> </xsl:text> + </xsl:when> + </xsl:choose> + </xsl:template> + <!-- Type --> <xsl:template match="type"> diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl index 4ed4fa14c4..da96052462 100644 --- a/lib/erl_docgen/priv/xsl/db_pdf.xsl +++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl @@ -3,7 +3,7 @@ # # %CopyrightBegin% # - # Copyright Ericsson AB 2009-2011. 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 @@ -1424,7 +1424,13 @@ <xsl:param name="partnum"/> <xsl:choose> <xsl:when test="ancestor::cref"> - <fo:block id="{generate-id(nametext)}"><xsl:value-of select="ret"/><xsl:text></xsl:text><xsl:value-of select="nametext"/></fo:block> + <fo:block id="{generate-id(nametext)}"> + <xsl:value-of select="ret"/> + <xsl:call-template name="maybe-space-after-ret"> + <xsl:with-param name="s" select="ret"/> + </xsl:call-template> + <xsl:value-of select="nametext"/> + </fo:block> </xsl:when> <xsl:otherwise> <fo:block id="{generate-id(.)}"><xsl:value-of select="."/></fo:block> @@ -1432,6 +1438,16 @@ </xsl:choose> </xsl:template> + <xsl:template name="maybe-space-after-ret"> + <xsl:param name="s"/> + <xsl:variable name="last_char" + select="substring($s, string-length($s), 1)"/> + <xsl:choose> + <xsl:when test="$last_char != '*'"> + <xsl:text> </xsl:text> + </xsl:when> + </xsl:choose> + </xsl:template> <!-- Type --> <xsl:template match="type"> @@ -1564,7 +1580,7 @@ </xsl:variable> <fo:block xsl:use-attribute-sets="image"> - <fo:external-graphic content-width="60%" src="{@file}"/> + <fo:external-graphic content-width="scale-down-to-fit" inline-progression-dimension.maximum="100%" src="{@file}"/> <xsl:apply-templates> <xsl:with-param name="chapnum" select="$chapnum"/> diff --git a/lib/erl_docgen/src/Makefile b/lib/erl_docgen/src/Makefile index cbaf6e4627..4d8f43bfed 100644 --- a/lib/erl_docgen/src/Makefile +++ b/lib/erl_docgen/src/Makefile @@ -89,10 +89,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) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/erl_interface/aclocal.m4 b/lib/erl_interface/aclocal.m4 index 339a15a2bb..a76594d86f 100644 --- a/lib/erl_interface/aclocal.m4 +++ b/lib/erl_interface/aclocal.m4 @@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us dnl Cross compilation variables AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)]) +AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)]) @@ -606,6 +607,103 @@ ifelse([$5], , , [$5 fi ]) +dnl ---------------------------------------------------------------------- +dnl +dnl AC_DOUBLE_MIDDLE_ENDIAN +dnl +dnl Checks whether doubles are represented in "middle-endian" format. +dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly, +dnl as well as DOUBLE_MIDDLE_ENDIAN. +dnl +dnl + +AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN], +[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian, +[# It does not; compile a test program. +AC_RUN_IFELSE( +[AC_LANG_SOURCE([[#include <stdlib.h> + +int +main(void) +{ + int i = 0; + int zero = 0; + int bigendian; + int zero_index = 0; + + union + { + long int l; + char c[sizeof (long int)]; + } u; + + /* we'll use the one with 32-bit words */ + union + { + double d; + unsigned int c[2]; + } vint; + + union + { + double d; + unsigned long c[2]; + } vlong; + + union + { + double d; + unsigned short c[2]; + } vshort; + + + /* Are we little or big endian? From Harbison&Steele. */ + u.l = 1; + bigendian = (u.c[sizeof (long int) - 1] == 1); + + zero_index = bigendian ? 1 : 0; + + vint.d = 1.0; + vlong.d = 1.0; + vshort.d = 1.0; + + if (sizeof(unsigned int) == 4) + { + if (vint.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned long) == 4) + { + if (vlong.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned short) == 4) + { + if (vshort.c[zero_index] != 0) + zero = 1; + } + + exit (zero); +} +]])], + [ac_cv_c_double_middle_endian=no], + [ac_cv_c_double_middle_endian=yes], + [ac_cv_c_double_middle=unknown])]) +case $ac_cv_c_double_middle_endian in + yes) + m4_default([$1], + [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1, + [Define to 1 if your processor stores the words in a double in + middle-endian format (like some ARMs).])]) ;; + no) + $2 ;; + *) + m4_default([$3], + [AC_MSG_WARN([unknown double endianness +presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;; +esac +])# AC_C_DOUBLE_MIDDLE_ENDIAN + dnl ---------------------------------------------------------------------- dnl @@ -1337,6 +1435,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian]) fi +case X$erl_xcomp_double_middle_endian in + X) ;; + Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;; + *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);; +esac + +AC_C_DOUBLE_MIDDLE_ENDIAN + AC_ARG_ENABLE(native-ethr-impls, AS_HELP_STRING([--disable-native-ethr-impls], [disable native ethread implementations]), diff --git a/lib/erl_interface/doc/src/Makefile b/lib/erl_interface/doc/src/Makefile index e05b647cb2..2bad8976b5 100644 --- a/lib/erl_interface/doc/src/Makefile +++ b/lib/erl_interface/doc/src/Makefile @@ -113,16 +113,16 @@ clean clean_docs clean_tex: 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/man1 - $(INSTALL_DATA) $(MAN1_FILES) $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in index d6b0ca1f16..661dbb68ac 100644 --- a/lib/erl_interface/src/Makefile.in +++ b/lib/erl_interface/src/Makefile.in @@ -843,32 +843,32 @@ EXTRA = \ $(TARGET)/eidefs.mk release: opt - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/lib - $(INSTALL_DIR) $(RELSYSDIR)/bin - $(INSTALL_DIR) $(RELSYSDIR)/src/auxdir - $(INSTALL_DIR) $(RELSYSDIR)/src/connect - $(INSTALL_DIR) $(RELSYSDIR)/src/decode - $(INSTALL_DIR) $(RELSYSDIR)/src/encode - $(INSTALL_DIR) $(RELSYSDIR)/src/epmd - $(INSTALL_DIR) $(RELSYSDIR)/src/legacy - $(INSTALL_DIR) $(RELSYSDIR)/src/misc - $(INSTALL_DIR) $(RELSYSDIR)/src/prog - $(INSTALL_DIR) $(RELSYSDIR)/src/registry - $(INSTALL_DATA) $(HEADERS) $(RELSYSDIR)/include - $(INSTALL_DATA) $(OBJ_TARGETS) $(RELSYSDIR)/lib + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/lib" + $(INSTALL_DIR) "$(RELSYSDIR)/bin" + $(INSTALL_DIR) "$(RELSYSDIR)/src/auxdir" + $(INSTALL_DIR) "$(RELSYSDIR)/src/connect" + $(INSTALL_DIR) "$(RELSYSDIR)/src/decode" + $(INSTALL_DIR) "$(RELSYSDIR)/src/encode" + $(INSTALL_DIR) "$(RELSYSDIR)/src/epmd" + $(INSTALL_DIR) "$(RELSYSDIR)/src/legacy" + $(INSTALL_DIR) "$(RELSYSDIR)/src/misc" + $(INSTALL_DIR) "$(RELSYSDIR)/src/prog" + $(INSTALL_DIR) "$(RELSYSDIR)/src/registry" + $(INSTALL_DATA) $(HEADERS) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(OBJ_TARGETS) "$(RELSYSDIR)/lib" ifneq ($(EXE_TARGETS),) - $(INSTALL_PROGRAM) $(EXE_TARGETS) $(RELSYSDIR)/bin -endif - $(INSTALL_DATA) $(EXTRA) $(RELSYSDIR)/src - $(INSTALL_DATA) connect/*.[ch] $(RELSYSDIR)/src/connect - $(INSTALL_DATA) decode/*.[ch] $(RELSYSDIR)/src/decode - $(INSTALL_DATA) encode/*.[ch] $(RELSYSDIR)/src/encode - $(INSTALL_DATA) epmd/*.[ch] $(RELSYSDIR)/src/epmd - $(INSTALL_DATA) misc/*.[ch] $(RELSYSDIR)/src/misc - $(INSTALL_DATA) registry/*.[ch] $(RELSYSDIR)/src/registry - $(INSTALL_DATA) legacy/*.[ch] $(RELSYSDIR)/src/legacy - $(INSTALL_DATA) prog/*.[ch] $(RELSYSDIR)/src/prog + $(INSTALL_PROGRAM) $(EXE_TARGETS) "$(RELSYSDIR)/bin" +endif + $(INSTALL_DATA) $(EXTRA) "$(RELSYSDIR)/src" + $(INSTALL_DATA) connect/*.[ch] "$(RELSYSDIR)/src/connect" + $(INSTALL_DATA) decode/*.[ch] "$(RELSYSDIR)/src/decode" + $(INSTALL_DATA) encode/*.[ch] "$(RELSYSDIR)/src/encode" + $(INSTALL_DATA) epmd/*.[ch] "$(RELSYSDIR)/src/epmd" + $(INSTALL_DATA) misc/*.[ch] "$(RELSYSDIR)/src/misc" + $(INSTALL_DATA) registry/*.[ch] "$(RELSYSDIR)/src/registry" + $(INSTALL_DATA) legacy/*.[ch] "$(RELSYSDIR)/src/legacy" + $(INSTALL_DATA) prog/*.[ch] "$(RELSYSDIR)/src/prog" release_docs: diff --git a/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile index 4faf89c0d6..d11a138844 100644 --- a/lib/erl_interface/test/Makefile +++ b/lib/erl_interface/test/Makefile @@ -71,9 +71,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/erl_interface/test/all_SUITE_data/Makefile.src b/lib/erl_interface/test/all_SUITE_data/Makefile.src index 70652e47c5..39085def2d 100644 --- a/lib/erl_interface/test/all_SUITE_data/Makefile.src +++ b/lib/erl_interface/test/all_SUITE_data/Makefile.src @@ -32,7 +32,7 @@ all: $(ALL_OBJS) $(EI_COMMON_OBJS): gccifier@exe@ -@IFEQ@ (@erl_interface_cross_compile@, true) +@IFEQ@ (@cross@, yes) gccifier@exe@: $(CP) gccifier.sh gccifier@exe@ $(CHMOD) a+x gccifier@exe@ diff --git a/lib/erl_interface/test/all_SUITE_data/gccifier.c b/lib/erl_interface/test/all_SUITE_data/gccifier.c index 9f556fc4ed..a1019f9a72 100644 --- a/lib/erl_interface/test/all_SUITE_data/gccifier.c +++ b/lib/erl_interface/test/all_SUITE_data/gccifier.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2004-2009. 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 @@ -16,7 +16,6 @@ * * %CopyrightEnd% * - */ /* @@ -74,17 +73,23 @@ save_arg(args_t *args, char *arg1, ...) args->vec = (char **) (args->no ? realloc((void *) args->vec, (sizeof(char *) - *(args->no + ARGS_INCR + 1))) + *(args->no + ARGS_INCR + 2))) : malloc((sizeof(char *) - *(args->no + ARGS_INCR + 1)))); + *(args->no + ARGS_INCR + 2)))); if (!args->vec) enomem(); args->no += ARGS_INCR; } + if (carg == arg1) { + args->vec[args->ix++] = "\""; + args->chars++; + } args->vec[args->ix++] = carg; args->chars += strlen(carg); carg = va_arg(argp, char *); } + args->vec[args->ix++] = "\""; + args->chars++; args->vec[args->ix++] = " "; args->chars++; va_end(argp); @@ -232,6 +237,9 @@ main(int argc, char *argv[]) CHECK_FIRST_LINK_ARG; save_arg(&link_args, "-libpath:", arg, NULL); } + else if (strcmp("-link",arg) == 0) { + CHECK_FIRST_LINK_ARG; + } #endif /* #ifdef __WIN32__ */ else if (is_prefix("-l", &arg)) { CHECK_FIRST_LINK_ARG; diff --git a/lib/erl_interface/test/all_SUITE_data/init_tc.erl b/lib/erl_interface/test/all_SUITE_data/init_tc.erl index 7a599994fc..60c965eda3 100644 --- a/lib/erl_interface/test/all_SUITE_data/init_tc.erl +++ b/lib/erl_interface/test/all_SUITE_data/init_tc.erl @@ -48,8 +48,7 @@ run1(Name) -> generate(TcName, Cases) -> Hrl = TcName ++ "_cases.hrl", {ok, HrlFile} = file:open(Hrl, write), - {ok, Dir} = file:get_cwd(), - generate_hrl(Cases, HrlFile, {filename:join(Dir, TcName), 0}), + generate_hrl(Cases, HrlFile, {TcName, 0}), file:close(HrlFile), C = TcName ++ "_decl.c", {ok, CFile} = file:open(C, write), @@ -57,7 +56,7 @@ generate(TcName, Cases) -> file:close(CFile). generate_hrl([Case|Rest], File, {Name, Number}) -> - io:format(File, "-define(~s, {\"~s\", ~w}).~n", [Case, Name, Number]), + io:format(File, "-define(~s, {filename:join(proplists:get_value(data_dir,Config),\"~s\"), ~w}).~n", [Case, Name, Number]), generate_hrl(Rest, File, {Name, Number+1}); generate_hrl([], _, _) -> ok. diff --git a/lib/erl_interface/test/ei_tmo_SUITE.erl b/lib/erl_interface/test/ei_tmo_SUITE.erl index 7ff8c08280..cc22cb7440 100644 --- a/lib/erl_interface/test/ei_tmo_SUITE.erl +++ b/lib/erl_interface/test/ei_tmo_SUITE.erl @@ -88,12 +88,12 @@ ei_recv_tmo(doc) -> ei_recv_tmo(suite) -> []; ei_recv_tmo(Config) when is_list(Config) -> - ?line do_one_recv(c_node_recv_tmo_1), - ?line do_one_recv_failure(c_node_recv_tmo_2), + ?line do_one_recv(Config,c_node_recv_tmo_1), + ?line do_one_recv_failure(Config,c_node_recv_tmo_2), ok. -do_one_recv(CNode) -> +do_one_recv(Config,CNode) -> ?line {_,Host} = split(node()), ?line P1 = runner:start(?recv_tmo), ?line runner:send_term(P1,{CNode, @@ -107,7 +107,7 @@ do_one_recv(CNode) -> ?line {term, Term1} = runner:get_term(P1, 10000), ?line runner:recv_eot(P1). -do_one_recv_failure(CNode) -> +do_one_recv_failure(Config,CNode) -> ?line P1 = runner:start(?recv_tmo), ?line runner:send_term(P1,{CNode, erlang:get_cookie(), @@ -128,14 +128,14 @@ ei_send_tmo(Config) when is_list(Config) -> %dbg:p(self()), VxSim = ?config(vxsim, Config), ?line register(ei_send_tmo_1,self()), - ?line do_one_send(self(),c_node_send_tmo_1), - ?line do_one_send(ei_send_tmo_1,c_node_send_tmo_2), - ?line do_one_send_failure(self(),cccc1,c_nod_send_tmo_3,VxSim), - ?line do_one_send_failure(ei_send_tmo_1,cccc2,c_nod_send_tmo_4,VxSim), + ?line do_one_send(Config,self(),c_node_send_tmo_1), + ?line do_one_send(Config,ei_send_tmo_1,c_node_send_tmo_2), + ?line do_one_send_failure(Config,self(),cccc1,c_nod_send_tmo_3,VxSim), + ?line do_one_send_failure(Config,ei_send_tmo_1,cccc2,c_nod_send_tmo_4,VxSim), ok. -do_one_send(From,CNode) -> +do_one_send(Config,From,CNode) -> ?line {_,Host} = split(node()), ?line P1 = runner:start(?send_tmo), ?line runner:send_term(P1,{CNode, @@ -155,7 +155,7 @@ do_one_send(From,CNode) -> ?line {term, 0} = runner:get_term(P1, 10000), ?line runner:recv_eot(P1). -do_one_send_failure(From,FakeName,CName,VxSim) -> +do_one_send_failure(Config,From,FakeName,CName,VxSim) -> ?line {_,Host} = split(node()), ?line OurName = join(FakeName,Host), ?line Node = join(CName,Host), diff --git a/lib/erl_interface/test/erl_match_SUITE.erl b/lib/erl_interface/test/erl_match_SUITE.erl index e019fecca8..edbb17a8c1 100644 --- a/lib/erl_interface/test/erl_match_SUITE.erl +++ b/lib/erl_interface/test/erl_match_SUITE.erl @@ -29,7 +29,7 @@ bind/1, integers/1, floats/1, binaries/1, strings/1]). %% For interactive running of matcher. --export([start_matcher/0, erl_match/3]). +-export([start_matcher/1, erl_match/3]). %% This test suite tests the erl_match() function. @@ -57,7 +57,7 @@ end_per_group(_GroupName, Config) -> atoms(suite) -> []; atoms(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line eq(P, '', ''), ?line eq(P, a, a), @@ -74,7 +74,7 @@ atoms(Config) when is_list(Config) -> lists(suite) -> []; lists(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line eq(P, [], []), ?line ne(P, [], [a]), @@ -101,7 +101,7 @@ lists(Config) when is_list(Config) -> tuples(suite) -> []; tuples(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line ne(P, {}, {a, b}), ?line ne(P, {a, b}, {}), @@ -129,7 +129,7 @@ tuples(Config) when is_list(Config) -> references(suite) -> []; references(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line Ref1 = make_ref(), ?line Ref2 = make_ref(), @@ -144,7 +144,7 @@ references(Config) when is_list(Config) -> pids(suite) -> []; pids(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line Pid1 = c:pid(0,1,2), ?line Pid2 = c:pid(0,1,3), @@ -163,8 +163,8 @@ ports(Config) when is_list(Config) -> vxworks -> {skipped,"not on vxworks, pucko"}; _ -> - ?line P = start_matcher(), - ?line P2 = start_matcher(), + ?line P = start_matcher(Config), + ?line P2 = start_matcher(Config), ?line eq(P, P, P), ?line ne(P, P, P2), @@ -176,7 +176,7 @@ ports(Config) when is_list(Config) -> integers(suite) -> []; integers(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line I1 = 123, ?line I2 = 12345, ?line I3 = -123, @@ -195,7 +195,7 @@ integers(Config) when is_list(Config) -> floats(suite) -> []; floats(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line F1 = 3.1414, ?line F2 = 3.1415, ?line F3 = 3.1416, @@ -218,7 +218,7 @@ floats(Config) when is_list(Config) -> binaries(suite) -> []; binaries(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line Bin1 = term_to_binary({kalle, 146015, {kungsgatan, 23}}), ?line Bin2 = term_to_binary(sune), ?line Bin3 = list_to_binary("sune"), @@ -237,7 +237,7 @@ binaries(Config) when is_list(Config) -> strings(suite) -> []; strings(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line S1 = "string", ?line S2 = "streng", @@ -254,7 +254,7 @@ strings(Config) when is_list(Config) -> bind(suite) -> []; bind(Config) when is_list(Config) -> - ?line P = start_bind(), + ?line P = start_bind(Config), ?line S = "[X,Y,Z]", ?line L1 = [301,302,302], ?line L2 = [65,66,67], @@ -265,7 +265,7 @@ bind(Config) when is_list(Config) -> ?line runner:finish(P), ok. -start_bind() -> +start_bind(Config) -> runner:start(?erl_match_bind). bind_ok(Port, Bind, Term) -> @@ -287,7 +287,7 @@ erl_bind(Port, Pattern, Term) -> -start_matcher() -> +start_matcher(Config) -> runner:start(?erl_match_server). eq(Port, Pattern, Term) -> diff --git a/lib/et/doc/src/Makefile b/lib/et/doc/src/Makefile index 6bb8164e91..facd335449 100644 --- a/lib/et/doc/src/Makefile +++ b/lib/et/doc/src/Makefile @@ -97,14 +97,14 @@ debug opt: 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/et/doc/src/et_intro.xml b/lib/et/doc/src/et_intro.xml index 0c5fb14d55..60da289721 100644 --- a/lib/et/doc/src/et_intro.xml +++ b/lib/et/doc/src/et_intro.xml @@ -40,8 +40,8 @@ ports or files.</p> <section> - <title>Scope and Purpose</title>' - + <title>Scope and Purpose</title> + <p>This manual describes the <c>Event Tracer (ET)</c> application, as a component of the Erlang/Open Telecom Platform development environment. It is assumed that the reader is familiar with the diff --git a/lib/et/examples/Makefile b/lib/et/examples/Makefile index 67a6536fdf..190d5e2d6a 100644 --- a/lib/et/examples/Makefile +++ b/lib/et/examples/Makefile @@ -73,8 +73,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/et/src/Makefile b/lib/et/src/Makefile index bb6632ee91..7693571c5c 100644 --- a/lib/et/src/Makefile +++ b/lib/et/src/Makefile @@ -104,14 +104,14 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl index 86f46f25d0..b559da8807 100644 --- a/lib/et/src/et_wx_contents_viewer.erl +++ b/lib/et/src/et_wx_contents_viewer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. 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 @@ -245,12 +245,7 @@ handle_event(#wx{id = Id, Data when is_record(Data, filter) -> F = Data, ChildState= S#state{active_filter = F#filter.name}, - case wx_object:start_link(?MODULE, [ChildState], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid); - _ -> - ignore - end; + wx_object:start_link(?MODULE, [ChildState], []); {hide, Actors} -> send_viewer_event(S, {delete_actors, Actors}); {show, Actors} -> @@ -356,12 +351,7 @@ handle_event(#wx{event = #wxKey{rawCode = KeyCode}}, S) -> case lists:keysearch(?DEFAULT_FILTER_NAME, #filter.name, S#state.filters) of {value, F} when is_record(F, filter) -> ChildState= S#state{active_filter = F#filter.name}, - case wx_object:start_link(?MODULE, [ChildState], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid); - _ -> - ignore - end; + wx_object:start_link(?MODULE, [ChildState], []); false -> ignore end, @@ -370,12 +360,7 @@ handle_event(#wx{event = #wxKey{rawCode = KeyCode}}, S) -> case catch lists:nth(Int-$0, S#state.filters) of F when is_record(F, filter) -> ChildState= S#state{active_filter = F#filter.name}, - case wx_object:start_link(?MODULE, [ChildState], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid); - _ -> - ignore - end; + wx_object:start_link(?MODULE, [ChildState], []); {'EXIT', _} -> ignore end, diff --git a/lib/et/test/Makefile b/lib/et/test/Makefile index e10a2a1587..7b4db7b0c3 100644 --- a/lib/et/test/Makefile +++ b/lib/et/test/Makefile @@ -70,12 +70,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) et.spec et.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_SCRIPT) ett $(RELSYSDIR) - $(INSTALL_DATA) $(INSTALL_PROGS) $(RELSYSDIR) -# chmod -R u+w $(RELSYSDIR) -# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) et.spec et.cover $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_SCRIPT) ett "$(RELSYSDIR)" + $(INSTALL_DATA) $(INSTALL_PROGS) "$(RELSYSDIR)" +# chmod -R u+w "$(RELSYSDIR)" +# @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc index ad449cb6fc..b4af31ae6a 100644 --- a/lib/eunit/doc/overview.edoc +++ b/lib/eunit/doc/overview.edoc @@ -723,8 +723,12 @@ A <em>simple test object</em> is one of the following: ```fun some_function/0''' ```fun some_module:some_function/0''' </li> - <li>A pair of atoms `{ModuleName, FunctionName}', referring to the - function `ModuleName:FunctionName/0'</li> + <li>A tuple `{test, ModuleName, FunctionName}', where `ModuleName' and + `FunctionName' are atoms, referring to the function + `ModuleName:FunctionName/0'</li> + <li>(Obsolete) A pair of atoms `{ModuleName, FunctionName}', equivalent to + `{test, ModuleName, FunctionName}' if nothing else matches first. This + might be removed in a future version.</li> <li>A pair `{LineNumber, SimpleTest}', where `LineNumber' is a nonnegative integer and `SimpleTest' is another simple test object. `LineNumber' should indicate the source line of the test. diff --git a/lib/eunit/doc/src/Makefile b/lib/eunit/doc/src/Makefile index 2cdc579275..cbcd4c1507 100644 --- a/lib/eunit/doc/src/Makefile +++ b/lib/eunit/doc/src/Makefile @@ -157,14 +157,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/eunit/examples/Makefile b/lib/eunit/examples/Makefile index 48ec2ebf2b..cfa0a7a645 100644 --- a/lib/eunit/examples/Makefile +++ b/lib/eunit/examples/Makefile @@ -49,8 +49,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(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/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl index db68d8ae60..fba840c3bd 100644 --- a/lib/eunit/include/eunit.hrl +++ b/lib/eunit/include/eunit.hrl @@ -25,11 +25,12 @@ %% will become undefined. NODEBUG also implies NOASSERT, unless testing %% is enabled. %% -%% If including this file causes TEST to be defined, then NOASSERT will -%% be undefined, even if it was previously defined and even if NODEBUG -%% is defined. If both ASSERT and NOASSERT are defined before the file -%% is included, then ASSERT takes precedence, and NOASSERT will become -%% undefined regardless of TEST. +%% Defining NOASSERT disables asserts. NODEBUG implies NOASSERT unless +%% testing is enabled. If including this file causes TEST to be defined, +%% then NOASSERT will be undefined, even if it was previously defined and +%% even if NODEBUG is defined. If both ASSERT and NOASSERT are defined +%% before the file is included, then ASSERT takes precedence, and NOASSERT +%% will become undefined regardless of TEST. %% %% After including this file, EUNIT will be defined if and only if TEST %% is defined. @@ -127,9 +128,9 @@ current_function)))). -endif. --ifdef(NOASSERT). %% The plain assert macro should be defined to do nothing if this file %% is included when debugging/testing is turned off. +-ifdef(NOASSERT). -ifndef(assert). -define(assert(BoolExpr),ok). -endif. diff --git a/lib/eunit/src/Makefile b/lib/eunit/src/Makefile index bec2fdbe0b..0a2e71cf7b 100644 --- a/lib/eunit/src/Makefile +++ b/lib/eunit/src/Makefile @@ -28,6 +28,9 @@ ERL_COMPILE_FLAGS += -pa $(EBIN) -I$(INCLUDE) +warn_unused_vars +nowarn_shadow_v PARSE_TRANSFORM = eunit_autoexport.erl +BEHAVIOUR_SOURCES= \ + eunit_listener.erl + SOURCES= \ eunit_striptests.erl \ eunit.erl \ @@ -40,13 +43,16 @@ SOURCES= \ eunit_data.erl \ eunit_tty.erl \ eunit_surefire.erl \ - eunit_listener.erl INCLUDE_FILES = eunit.hrl PARSE_TRANSFORM_BIN = $(PARSE_TRANSFORM:%.erl=$(EBIN)/%.$(EMULATOR)) -OBJECTS=$(SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET) +TARGET_FILES= $(SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) + +BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) + +OBJECTS= $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) INCLUDE_DELIVERABLES = $(INCLUDE_FILES:%=$(INCLUDE)/%) @@ -62,6 +68,8 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) # Targets # ---------------------------------------------------- +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) + debug opt: $(PARSE_TRANSFORM_BIN) $(OBJECTS) docs: @@ -107,12 +115,12 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(PARSE_TRANSFORM_BIN) $(OBJECTS) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(PARSE_TRANSFORM) $(SOURCES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(INCLUDE_DELIVERABLES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(PARSE_TRANSFORM_BIN) $(OBJECTS) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(PARSE_TRANSFORM) $(SOURCES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(INCLUDE_DELIVERABLES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/eunit/src/eunit.app.src b/lib/eunit/src/eunit.app.src index 5e16dfa2ce..431abac98b 100644 --- a/lib/eunit/src/eunit.app.src +++ b/lib/eunit/src/eunit.app.src @@ -14,7 +14,6 @@ eunit_striptests, eunit_surefire, eunit_test, - eunit_tests, eunit_tty]}, {registered,[]}, {applications, [kernel,stdlib]}, diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl index 95857e83c8..51846d73b3 100644 --- a/lib/eunit/src/eunit.erl +++ b/lib/eunit/src/eunit.erl @@ -139,7 +139,7 @@ test(Tests, Options) -> %% @private %% @doc See {@link test/2}. test(Server, Tests, Options) -> - Listeners = [eunit_tty:start(Options) | listeners(Options)], + Listeners = listeners(Options), Serial = eunit_serial:start(Listeners), case eunit_server:start_test(Server, Serial, Tests, Options) of {ok, Reference} -> test_run(Reference, Listeners); @@ -194,7 +194,10 @@ submit(Server, T, Options) -> eunit_server:start_test(Server, Dummy, T, Options). listeners(Options) -> - Ps = start_listeners(proplists:get_all_values(report, Options)), + %% note that eunit_tty must always run, because it sends the final + %% {result,...} message that the test_run() function is waiting for + Ls = [{eunit_tty, Options} | proplists:get_all_values(report, Options)], + Ps = start_listeners(Ls), %% the event_log option is for debugging, to view the raw events case proplists:get_value(event_log, Options) of undefined -> diff --git a/lib/eunit/src/eunit_data.erl b/lib/eunit/src/eunit_data.erl index 392d378a0e..0350f9bf6e 100644 --- a/lib/eunit/src/eunit_data.erl +++ b/lib/eunit/src/eunit_data.erl @@ -83,6 +83,7 @@ %% SimpleTest = TestFunction | {Line::integer(), SimpleTest} %% %% TestFunction = () -> any() +%% | {test, M::moduleName(), F::functionName()} %% | {M::moduleName(), F::functionName()}. %% %% AbstractTestFunction = (X::any()) -> any() @@ -95,7 +96,6 @@ %% %% @type moduleName() = atom() %% @type functionName() = atom() -%% @type arity() = integer() %% @type appName() = atom() %% @type fileName() = string() @@ -156,8 +156,9 @@ iter_prev(#iter{prev = [T | Ts]} = I) -> %% @spec (tests()) -> none | {testItem(), tests()} %% @type testItem() = #test{} | #group{} %% @throws {bad_test, term()} -%% | {generator_failed, exception()} -%% | {no_such_function, eunit_lib:mfa()} +%% | {generator_failed, {{M::atom(),F::atom(),A::integer()}, +%% exception()}} +%% | {no_such_function, mfa()} %% | {module_not_found, moduleName()} %% | {application_not_found, appName()} %% | {file_read_error, {Reason::atom(), Message::string(), @@ -221,17 +222,27 @@ parse({foreachx, P, S1, C1, Ps} = T) [] -> {data, []} end; -parse({generator, F} = T) when is_function(F) -> +parse({generator, F}) when is_function(F) -> + {module, M} = erlang:fun_info(F, module), + {name, N} = erlang:fun_info(F, name), + {arity, A} = erlang:fun_info(F, arity), + parse({generator, F, {M,N,A}}); +parse({generator, F, {M,N,A}} = T) + when is_function(F), is_atom(M), is_atom(N), is_integer(A) -> check_arity(F, 0, T), %% use run_testfun/1 to handle wrapper exceptions case eunit_test:run_testfun(F) of {ok, T1} -> + case eunit_lib:is_not_test(T1) of + true -> throw({bad_generator, {{M,N,A}, T1}}); + false -> ok + end, {data, T1}; {error, {Class, Reason, Trace}} -> - throw({generator_failed, {Class, Reason, Trace}}) + throw({generator_failed, {{M,N,A}, {Class, Reason, Trace}}}) end; parse({generator, M, F}) when is_atom(M), is_atom(F) -> - parse({generator, eunit_test:function_wrapper(M, F)}); + parse({generator, eunit_test:mf_wrapper(M, F), {M,F,0}}); parse({inorder, T}) -> group(#group{tests = T, order = inorder}); parse({inparallel, T}) -> @@ -421,8 +432,11 @@ parse_simple(F) -> parse_function(F) when is_function(F) -> check_arity(F, 0, F), #test{f = F, location = eunit_lib:fun_parent(F)}; -parse_function({M,F}) when is_atom(M), is_atom(F) -> - #test{f = eunit_test:function_wrapper(M, F), location = {M, F, 0}}; +parse_function({test, M, F}) when is_atom(M), is_atom(F) -> + #test{f = eunit_test:mf_wrapper(M, F), location = {M, F, 0}}; +parse_function({M, F}) when is_atom(M), is_atom(F) -> + %% {M,F} is now considered obsolete; use {test,M,F} instead + parse_function({test, M, F}); parse_function(F) -> bad_test(F). @@ -580,7 +594,7 @@ testfuns(Es, M, TestSuffix, GeneratorSuffix) -> N = atom_to_list(F), case lists:suffix(TestSuffix, N) of true -> - [{M,F} | Fs]; + [{test, M, F} | Fs]; false -> case lists:suffix(GeneratorSuffix, N) of true -> @@ -723,6 +737,7 @@ data_test_() -> Tests = [T,T,T], [?_assertMatch(ok, eunit:test(T)), ?_assertMatch(error, eunit:test(Fail)), + ?_assertMatch(ok, eunit:test({test, ?MODULE, trivial_test})), ?_assertMatch(ok, eunit:test({generator, fun () -> Tests end})), ?_assertMatch(ok, eunit:test({generator, fun generator/0})), ?_assertMatch(ok, eunit:test({generator, ?MODULE, generator_exported_})), @@ -740,6 +755,12 @@ data_test_() -> %%?_test({foreach, Setup, [T, T, T]}) ]. +trivial_test() -> + ok. + +trivial_generator_test_() -> + [?_test(ok)]. + lazy_test_() -> {spawn, [?_test(undefined = put(count, 0)), lazy_gen(7), diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl index 1c41e229c5..ea9e944d7e 100644 --- a/lib/eunit/src/eunit_lib.erl +++ b/lib/eunit/src/eunit_lib.erl @@ -30,7 +30,8 @@ -export([dlist_next/1, uniq/1, fun_parent/1, is_string/1, command/1, command/2, command/3, trie_new/0, trie_store/2, trie_match/2, split_node/1, consult_file/1, list_dir/1, format_exit_term/1, - format_exception/1, format_exception/2, format_error/1]). + format_exception/1, format_exception/2, format_error/1, + is_not_test/1]). %% Type definitions for describing exceptions @@ -39,13 +40,10 @@ %% %% @type exceptionClass() = error | exit | throw %% -%% @type stackTrace() = [{moduleName(), functionName(), -%% arity() | argList()}] +%% @type stackTrace() = [{moduleName(), functionName(), arity() | argList()}] %% %% @type moduleName() = atom() %% @type functionName() = atom() -%% @type arity() = integer() -%% @type mfa() = {moduleName(), functionName(), arity()} %% @type argList() = [term()] %% @type fileName() = string() @@ -59,8 +57,9 @@ format_exception({Class,Term,Trace}, Depth) when is_atom(Class), is_list(Trace) -> case is_stacktrace(Trace) of true -> - io_lib:format("~w:~P\n~s", - [Class, Term, Depth, format_stacktrace(Trace)]); + io_lib:format("~s**~w:~s", + [format_stacktrace(Trace), Class, + format_term(Term, Depth)]); false -> format_term(Term, Depth) end; @@ -86,6 +85,12 @@ analyze_exit_term(Term) -> is_stacktrace([]) -> true; +is_stacktrace([{M,F,A,L}|Fs]) + when is_atom(M), is_atom(F), is_integer(A), is_list(L) -> + is_stacktrace(Fs); +is_stacktrace([{M,F,As,L}|Fs]) + when is_atom(M), is_atom(F), is_list(As), is_list(L) -> + is_stacktrace(Fs); is_stacktrace([{M,F,A}|Fs]) when is_atom(M), is_atom(F), is_integer(A) -> is_stacktrace(Fs); is_stacktrace([{M,F,As}|Fs]) when is_atom(M), is_atom(F), is_list(As) -> @@ -96,10 +101,11 @@ is_stacktrace(_) -> format_stacktrace(Trace) -> format_stacktrace(Trace, "in function", "in call from"). -format_stacktrace([{M,F,A}|Fs], Pre, Pre1) when is_integer(A) -> - [io_lib:fwrite(" ~s ~w:~w/~w\n", [Pre, M, F, A]) +format_stacktrace([{M,F,A,L}|Fs], Pre, Pre1) when is_integer(A) -> + [io_lib:fwrite("~s ~w:~w/~w~s\n", + [Pre, M, F, A, format_stacktrace_location(L)]) | format_stacktrace(Fs, Pre1, Pre1)]; -format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) -> +format_stacktrace([{M,F,As,L}|Fs], Pre, Pre1) when is_list(As) -> A = length(As), C = case is_op(M,F,A) of true when A =:= 1 -> @@ -112,12 +118,23 @@ format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) -> false -> io_lib:fwrite("~w(~s)", [F,format_arglist(As)]) end, - [io_lib:fwrite(" ~s ~w:~w/~w\n called as ~s\n", - [Pre,M,F,A,C]) + [io_lib:fwrite("~s ~w:~w/~w~s\n called as ~s\n", + [Pre,M,F,A,format_stacktrace_location(L),C]) | format_stacktrace(Fs,Pre1,Pre1)]; +format_stacktrace([{M,F,As}|Fs], Pre, Pre1) -> + format_stacktrace([{M,F,As,[]}|Fs], Pre, Pre1); format_stacktrace([],_Pre,_Pre1) -> "". +format_stacktrace_location(Location) -> + File = proplists:get_value(file, Location), + Line = proplists:get_value(line, Location), + if File =/= undefined, Line =/= undefined -> + io_lib:format(" (~s, line ~w)", [File, Line]); + true -> + "" + end. + format_arg(A) -> io_lib:format("~P",[A,15]). @@ -139,9 +156,13 @@ is_op(_M, _F, _A) -> format_error({bad_test, Term}) -> error_msg("bad test descriptor", "~P", [Term, 15]); -format_error({generator_failed, Exception}) -> - error_msg("test generator failed", "~s", - [format_exception(Exception)]); +format_error({bad_generator, {{M,F,A}, Term}}) -> + error_msg(io_lib:format("result from generator ~w:~w/~w is not a test", + [M,F,A]), + "~P", [Term, 15]); +format_error({generator_failed, {{M,F,A}, Exception}}) -> + error_msg(io_lib:format("test generator ~w:~w/~w failed",[M,F,A]), + "~s", [format_exception(Exception)]); format_error({no_such_function, {M,F,A}}) when is_atom(M), is_atom(F), is_integer(A) -> error_msg(io_lib:format("no such function: ~w:~w/~w", [M,F,A]), @@ -158,14 +179,55 @@ format_error({setup_failed, Exception}) -> format_error({cleanup_failed, Exception}) -> error_msg("context cleanup failed", "~s", [format_exception(Exception)]); +format_error({{bad_instantiator, {{M,F,A}, Term}}, _DummyException}) -> + error_msg(io_lib:format("result from instantiator ~w:~w/~w is not a test", + [M,F,A]), + "~P", [Term, 15]); format_error({instantiation_failed, Exception}) -> error_msg("instantiation of subtests failed", "~s", [format_exception(Exception)]). error_msg(Title, Fmt, Args) -> - Msg = io_lib:format("::"++Fmt, Args), % gets indentation right + Msg = io_lib:format("**"++Fmt, Args), % gets indentation right io_lib:fwrite("*** ~s ***\n~s\n\n", [Title, Msg]). +-ifdef(TEST). +format_exception_test_() -> + [?_assertMatch( + "\nymmud:rorre"++_, + lists:reverse(lists:flatten( + format_exception(try erlang:error(dummy) + catch C:R -> {C, R, erlang:get_stacktrace()} + end)))), + ?_assertMatch( + "\nymmud:rorre"++_, + lists:reverse(lists:flatten( + format_exception(try erlang:error(dummy, [a]) + catch C:R -> {C, R, erlang:get_stacktrace()} + end))))]. +-endif. + +%% --------------------------------------------------------------------- +%% detect common return values that are definitely not tests + +is_not_test(T) -> + case T of + ok -> true; + error -> true; + true -> true; + false -> true; + undefined -> true; + {ok, _} -> true; + {error, _} -> true; + {'EXIT', _} -> true; + N when is_number(N) -> true; + [N|_] when is_number(N) -> true; + X when is_binary(X) -> true; + X when is_pid(X) -> true; + X when is_port(X) -> true; + X when is_reference(X) -> true; + _ -> false + end. %% --------------------------------------------------------------------- %% Deep list iterator; accepts improper lists/sublists, and also accepts diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl index 2a6cbca14d..46b8c8b503 100644 --- a/lib/eunit/src/eunit_surefire.erl +++ b/lib/eunit/src/eunit_surefire.erl @@ -156,9 +156,33 @@ handle_end(test, Data, St) -> St#state{testsuites=store_suite(NewTestSuite, TestSuites)}. %% Cancel group does not give information on the individual cancelled test case -%% We ignore this event -handle_cancel(group, _Data, St) -> - St; +%% We ignore this event... +handle_cancel(group, Data, St) -> + %% ...except when it tells us that a fixture setup or cleanup failed. + case proplists:get_value(reason, Data) of + {abort, {SomethingFailed, Exception}} + when SomethingFailed =:= setup_failed; + SomethingFailed =:= cleanup_failed -> + [GroupId|_] = proplists:get_value(id, Data), + TestSuites = St#state.testsuites, + TestSuite = lookup_suite_by_group_id(GroupId, TestSuites), + + %% We don't have any proper name. Let's give all the + %% clues that we have. + Name = case SomethingFailed of + setup_failed -> "fixture setup "; + cleanup_failed -> "fixture cleanup " + end + ++ io_lib:format("~p", [proplists:get_value(id, Data)]), + Desc = format_desc(proplists:get_value(desc, Data)), + TestCase = #testcase{ + name = Name, description = Desc, + time = 0, output = <<>>}, + NewTestSuite = add_testcase_to_testsuite({error, Exception}, TestCase, TestSuite), + St#state{testsuites=store_suite(NewTestSuite, TestSuites)}; + _ -> + St + end; handle_cancel(test, Data, St) -> %% Retrieve existing test suite: [GroupId|_] = proplists:get_value(id, Data), @@ -232,7 +256,7 @@ write_reports(TestSuites, XmlDir) -> write_report(#testsuite{name = Name} = TestSuite, XmlDir) -> Filename = filename:join(XmlDir, lists:flatten(["TEST-", escape_suitename(Name)], ".xml")), - case file:open(Filename, [write, raw]) of + case file:open(Filename, [write,{encoding,utf8}]) of {ok, FileDescriptor} -> try write_report_to(TestSuite, FileDescriptor) diff --git a/lib/eunit/src/eunit_test.erl b/lib/eunit/src/eunit_test.erl index bca49ae626..9cf40a738d 100644 --- a/lib/eunit/src/eunit_test.erl +++ b/lib/eunit/src/eunit_test.erl @@ -21,8 +21,7 @@ -module(eunit_test). --export([run_testfun/1, function_wrapper/2, enter_context/4, - multi_setup/1]). +-export([run_testfun/1, mf_wrapper/2, enter_context/4, multi_setup/1]). -include("eunit.hrl"). @@ -43,8 +42,12 @@ get_stacktrace(Ts) -> prune_trace([{eunit_data, _, _} | Rest], Tail) -> prune_trace(Rest, Tail); +prune_trace([{eunit_data, _, _, _} | Rest], Tail) -> + prune_trace(Rest, Tail); prune_trace([{?MODULE, _, _} | _Rest], Tail) -> Tail; +prune_trace([{?MODULE, _, _, _} | _Rest], Tail) -> + Tail; prune_trace([T | Ts], Tail) -> [T | prune_trace(Ts, Tail)]; prune_trace([], Tail) -> @@ -258,7 +261,7 @@ macro_test_() -> %% @type wrapperError() = {no_such_function, mfa()} %% | {module_not_found, moduleName()} -function_wrapper(M, F) -> +mf_wrapper(M, F) -> fun () -> try M:F() catch @@ -289,12 +292,12 @@ fail(Term) -> wrapper_test_() -> {"error handling in function wrapper", [?_assertException(throw, {module_not_found, eunit_nonexisting}, - run_testfun(function_wrapper(eunit_nonexisting,test))), + run_testfun(mf_wrapper(eunit_nonexisting,test))), ?_assertException(throw, {no_such_function, {?MODULE,nonexisting_test,0}}, - run_testfun(function_wrapper(?MODULE,nonexisting_test))), + run_testfun(mf_wrapper(?MODULE,nonexisting_test))), ?_test({error, {error, undef, _T}} - = run_testfun(function_wrapper(?MODULE,wrapper_test_exported_))) + = run_testfun(mf_wrapper(?MODULE,wrapper_test_exported_))) ]}. %% this must be exported (done automatically by the autoexport transform) @@ -319,6 +322,17 @@ enter_context(Setup, Cleanup, Instantiate, Callback) -> R -> try Instantiate(R) of T -> + case eunit_lib:is_not_test(T) of + true -> + catch throw(error), % generate a stack trace + {module,M} = erlang:fun_info(Instantiate, module), + {name,N} = erlang:fun_info(Instantiate, name), + {arity,A} = erlang:fun_info(Instantiate, arity), + context_error({bad_instantiator, {{M,N,A},T}}, + error, badarg); + false -> + ok + end, try Callback(T) %% call back to client code after %% Always run cleanup; client may be an idiot diff --git a/lib/eunit/src/eunit_tty.erl b/lib/eunit/src/eunit_tty.erl index e3e7b710b2..f21b2da3d3 100644 --- a/lib/eunit/src/eunit_tty.erl +++ b/lib/eunit/src/eunit_tty.erl @@ -44,6 +44,7 @@ start(Options) -> init(Options) -> St = #state{verbose = proplists:get_bool(verbose, Options)}, + put(no_tty, proplists:get_bool(no_tty, Options)), receive {start, _Reference} -> if St#state.verbose -> print_header(); @@ -59,30 +60,30 @@ terminate({ok, Data}, St) -> Cancel = proplists:get_value(cancel, Data, 0), if Fail =:= 0, Skip =:= 0, Cancel =:= 0 -> if Pass =:= 0 -> - io:fwrite(" There were no tests to run.\n"); + fwrite(" There were no tests to run.\n"); true -> if St#state.verbose -> print_bar(); true -> ok end, if Pass =:= 1 -> - io:fwrite(" Test passed.\n"); + fwrite(" Test passed.\n"); true -> - io:fwrite(" All ~w tests passed.\n", [Pass]) + fwrite(" All ~w tests passed.\n", [Pass]) end end, sync_end(ok); true -> print_bar(), - io:fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n", - [Fail, Skip, Pass]), + fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n", + [Fail, Skip, Pass]), if Cancel =/= 0 -> - io:fwrite("One or more tests were cancelled.\n"); + fwrite("One or more tests were cancelled.\n"); true -> ok end, sync_end(error) end; terminate({error, Reason}, _St) -> - io:fwrite("Internal error: ~P.\n", [Reason, 25]), + fwrite("Internal error: ~P.\n", [Reason, 25]), sync_end(error). sync_end(Result) -> @@ -93,10 +94,10 @@ sync_end(Result) -> end. print_header() -> - io:fwrite("======================== EUnit ========================\n"). + fwrite("======================== EUnit ========================\n"). print_bar() -> - io:fwrite("=======================================================\n"). + fwrite("=======================================================\n"). handle_begin(group, Data, St) -> @@ -170,18 +171,18 @@ handle_cancel(test, Data, St) -> indent(N) when is_integer(N), N >= 1 -> - io:put_chars(lists:duplicate(N * 2, $\s)); + fwrite(lists:duplicate(N * 2, $\s)); indent(_N) -> ok. print_group_start(I, Desc) -> indent(I), - io:fwrite("~s\n", [Desc]). + fwrite("~s\n", [Desc]). print_group_end(I, Time) -> if Time > 0 -> indent(I), - io:fwrite("[done in ~.3f s]\n", [Time/1000]); + fwrite("[done in ~.3f s]\n", [Time/1000]); true -> ok end. @@ -198,9 +199,9 @@ print_test_begin(I, Data) -> end, case proplists:get_value(source, Data) of {Module, Name, _Arity} -> - io:fwrite("~s:~s ~s~s...", [Module, L, Name, D]); + fwrite("~s:~s ~s~s...", [Module, L, Name, D]); _ -> - io:fwrite("~s~s...", [L, D]) + fwrite("~s~s...", [L, D]) end. print_test_end(Data) -> @@ -208,36 +209,35 @@ print_test_end(Data) -> T = if Time > 0 -> io_lib:fwrite("[~.3f s] ", [Time/1000]); true -> "" end, - io:fwrite("~sok\n", [T]). + fwrite("~sok\n", [T]). print_test_error({error, Exception}, Data) -> Output = proplists:get_value(output, Data), - io:fwrite("*failed*\n::~s", - [eunit_lib:format_exception(Exception)]), + fwrite("*failed*\n~s", [eunit_lib:format_exception(Exception)]), case Output of <<>> -> - io:put_chars("\n\n"); + fwrite("\n\n"); <<Text:800/binary, _:1/binary, _/binary>> -> - io:fwrite(" output:<<\"~s\">>...\n\n", [Text]); + fwrite(" output:<<\"~s\">>...\n\n", [Text]); _ -> - io:fwrite(" output:<<\"~s\">>\n\n", [Output]) + fwrite(" output:<<\"~s\">>\n\n", [Output]) end; print_test_error({skipped, Reason}, _) -> - io:fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]). + fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]). format_skipped({module_not_found, M}) -> - io_lib:format("missing module: ~w", [M]); + io_lib:fwrite("missing module: ~w", [M]); format_skipped({no_such_function, {M,F,A}}) -> - io_lib:format("no such function: ~w:~w/~w", [M,F,A]). + io_lib:fwrite("no such function: ~w:~w/~w", [M,F,A]). print_test_cancel(Reason) -> - io:fwrite(format_cancel(Reason)). + fwrite(format_cancel(Reason)). print_group_cancel(_I, {blame, _}) -> ok; print_group_cancel(I, Reason) -> indent(I), - io:fwrite(format_cancel(Reason)). + fwrite(format_cancel(Reason)). format_cancel(undefined) -> "*skipped*\n"; @@ -253,3 +253,12 @@ format_cancel({exit, Reason}) -> [Reason, 15]); format_cancel({abort, Reason}) -> eunit_lib:format_error(Reason). + +fwrite(String) -> + fwrite(String, []). + +fwrite(String, Args) -> + case get(no_tty) of + false -> io:fwrite(String, Args); + true -> ok + end. diff --git a/lib/eunit/test/Makefile b/lib/eunit/test/Makefile index a2d276f619..1586dd8857 100644 --- a/lib/eunit/test/Makefile +++ b/lib/eunit/test/Makefile @@ -74,9 +74,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) eunit.spec $(EMAKEFILE) \ $(COVERFILE) $(ERL_FILES) \ - $(RELSYSDIR) + "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk index 445c070e96..174d197117 100644 --- a/lib/eunit/vsn.mk +++ b/lib/eunit/vsn.mk @@ -1 +1 @@ -EUNIT_VSN = 2.2.2 +EUNIT_VSN = 2.2.3 diff --git a/lib/gs/contribs/bonk/Makefile b/lib/gs/contribs/bonk/Makefile index be096824dd..dc92149784 100644 --- a/lib/gs/contribs/bonk/Makefile +++ b/lib/gs/contribs/bonk/Makefile @@ -94,14 +94,14 @@ $(EBIN)/$(TOOLNAME).tool: $(TOOLNAME).tool include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/bonk/bitmaps - $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) $(RELSYSDIR)/bonk/bitmaps - $(INSTALL_DIR) $(RELSYSDIR)/bonk/sounds - $(INSTALL_DATA) $(SOUNDS) $(RELSYSDIR)/bonk/sounds - $(INSTALL_DIR) $(RELSYSDIR)/bonk - $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) $(RELSYSDIR)/bonk + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/bonk/bitmaps" + $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) "$(RELSYSDIR)/bonk/bitmaps" + $(INSTALL_DIR) "$(RELSYSDIR)/bonk/sounds" + $(INSTALL_DATA) $(SOUNDS) "$(RELSYSDIR)/bonk/sounds" + $(INSTALL_DIR) "$(RELSYSDIR)/bonk" + $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) "$(RELSYSDIR)/bonk" release_docs_spec: diff --git a/lib/gs/contribs/cols/Makefile b/lib/gs/contribs/cols/Makefile index 75ca75ffc0..4ce4204c92 100644 --- a/lib/gs/contribs/cols/Makefile +++ b/lib/gs/contribs/cols/Makefile @@ -91,12 +91,12 @@ $(EBIN)/help.gif: help.gif include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/cols/bitmaps - $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) $(RELSYSDIR)/cols/bitmaps - $(INSTALL_DIR) $(RELSYSDIR)/cols - $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) $(RELSYSDIR)/cols + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/cols/bitmaps" + $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) "$(RELSYSDIR)/cols/bitmaps" + $(INSTALL_DIR) "$(RELSYSDIR)/cols" + $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) "$(RELSYSDIR)/cols" release_docs_spec: diff --git a/lib/gs/contribs/mandel/Makefile b/lib/gs/contribs/mandel/Makefile index 61a7a612e0..451ece2c8f 100644 --- a/lib/gs/contribs/mandel/Makefile +++ b/lib/gs/contribs/mandel/Makefile @@ -90,11 +90,11 @@ $(EBIN)/help.gif: help.gif include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/mandel/bitmaps - $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) $(RELSYSDIR)/mandel/bitmaps - $(INSTALL_DIR) $(RELSYSDIR)/mandel - $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) $(RELSYSDIR)/mandel + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/mandel/bitmaps" + $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) "$(RELSYSDIR)/mandel/bitmaps" + $(INSTALL_DIR) "$(RELSYSDIR)/mandel" + $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) "$(RELSYSDIR)/mandel" release_docs_spec: diff --git a/lib/gs/contribs/othello/Makefile b/lib/gs/contribs/othello/Makefile index b81b35fb55..5f09f2ed13 100644 --- a/lib/gs/contribs/othello/Makefile +++ b/lib/gs/contribs/othello/Makefile @@ -89,12 +89,12 @@ $(EBIN)/$(TOOLNAME).tool: $(TOOLNAME).tool include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/othello - $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) $(RELSYSDIR)/othello - $(INSTALL_DIR) $(RELSYSDIR)/othello/priv - $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) $(RELSYSDIR)/othello/priv + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/othello" + $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) "$(RELSYSDIR)/othello" + $(INSTALL_DIR) "$(RELSYSDIR)/othello/priv" + $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) "$(RELSYSDIR)/othello/priv" release_docs_spec: diff --git a/lib/gs/doc/src/Makefile b/lib/gs/doc/src/Makefile index 192dd67a52..14204c52fc 100644 --- a/lib/gs/doc/src/Makefile +++ b/lib/gs/doc/src/Makefile @@ -144,13 +144,13 @@ debug opt: 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 - (/bin/cp -rf $(HTMLDIR) $(RELSYSDIR)/doc) - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc") + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/gs/examples/Makefile b/lib/gs/examples/Makefile index 10b166b207..d214cd2256 100644 --- a/lib/gs/examples/Makefile +++ b/lib/gs/examples/Makefile @@ -87,9 +87,9 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" release_docs_spec: diff --git a/lib/gs/src/Makefile b/lib/gs/src/Makefile index 43b530295b..a9904161a4 100644 --- a/lib/gs/src/Makefile +++ b/lib/gs/src/Makefile @@ -107,13 +107,13 @@ $(GSTK_GENERIC_TARGET): gstk_generic.hrl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/src" $(INSTALL_DATA) $(APP_SRC) $(ERL_FILES) $(HRL_FILES) $(GEN_HRL_FILES) \ - $(GSTK_GENERIC) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/priv/bitmap - $(INSTALL_DATA) $(IMAGES) $(RELSYSDIR)/priv/bitmap + $(GSTK_GENERIC) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bitmap" + $(INSTALL_DATA) $(IMAGES) "$(RELSYSDIR)/priv/bitmap" release_docs_spec: diff --git a/lib/gs/src/gstk_generic.erl b/lib/gs/src/gstk_generic.erl index 10cb890013..9b0efd07c1 100644 --- a/lib/gs/src/gstk_generic.erl +++ b/lib/gs/src/gstk_generic.erl @@ -323,7 +323,7 @@ handle_external_opt_call([Opt|Options],Gstkid,TkW,DB,ExtraArg,ExtRes,S,P,C) -> end. handle_external_read(Res) -> - case Res of + _ = case Res of {bad_result,{Objtype,Reason,Option}} -> {error,{Objtype,Reason,Option}}; _ -> ok diff --git a/lib/gs/tcl/Makefile.in b/lib/gs/tcl/Makefile.in index dce34a4baa..0bccb60c7b 100644 --- a/lib/gs/tcl/Makefile.in +++ b/lib/gs/tcl/Makefile.in @@ -74,10 +74,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(TCL_FILES) $(EXTRA_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(TCL_FILES) $(EXTRA_FILES) "$(RELSYSDIR)/priv" ifneq ($(TCL_TAR),) - gzip -dc $(TCL_TAR) | (cd $(RELSYSDIR)/priv && tar -xf -) + gzip -dc $(TCL_TAR) | (cd "$(RELSYSDIR)/priv" && tar -xf -) endif release_docs_spec: diff --git a/lib/hipe/amd64/Makefile b/lib/hipe/amd64/Makefile index 15291e9bbe..50cd024d72 100644 --- a/lib/hipe/amd64/Makefile +++ b/lib/hipe/amd64/Makefile @@ -105,8 +105,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/arm/Makefile b/lib/hipe/arm/Makefile index a64f133679..651b82f1ed 100644 --- a/lib/hipe/arm/Makefile +++ b/lib/hipe/arm/Makefile @@ -106,8 +106,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/cerl/Makefile b/lib/hipe/cerl/Makefile index 14e68f5233..506e993ff4 100644 --- a/lib/hipe/cerl/Makefile +++ b/lib/hipe/cerl/Makefile @@ -42,7 +42,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN) # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- -MODULES = cerl_cconv cerl_closurean cerl_hipeify cerl_hybrid_transform \ +MODULES = cerl_cconv cerl_closurean cerl_hipeify \ cerl_lib cerl_messagean cerl_pmatch cerl_prettypr cerl_to_icode \ cerl_typean erl_bif_types erl_types @@ -100,10 +100,10 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/cerl - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/cerl - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/cerl" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/cerl" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/cerl/cerl_hybrid_transform.erl b/lib/hipe/cerl/cerl_hybrid_transform.erl deleted file mode 100644 index b248b0ccd0..0000000000 --- a/lib/hipe/cerl/cerl_hybrid_transform.erl +++ /dev/null @@ -1,153 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2009. 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(cerl_hybrid_transform). - -%% Use compile option `{core_transform, cerl_hybrid_transform}' to -%% insert this as a compilation pass. - --export([transform/2, core_transform/2]). - --spec core_transform(cerl:cerl(), [term()]) -> cerl:cerl(). - -core_transform(Code, Opts) -> - cerl:to_records(transform(cerl:from_records(Code), Opts)). - --spec transform(cerl:cerl(), [term()]) -> cerl:cerl(). - -transform(Code, _Opts) -> - Code0 = cerl_trees:map(fun unfold_literal/1, Code), - {Code1, _} = cerl_trees:label(Code0), - io:fwrite("Running hybrid heap analysis..."), - {T1,_} = statistics(runtime), - {Code2, _, Vars} = cerl_messagean:annotate(Code1), - {T2,_} = statistics(runtime), - io:fwrite("(~w ms), transform...", [T2 - T1]), - Code3 = rewrite(Code2, Vars), - io:fwrite("done.\n"), - cerl_trees:map(fun fold_literal/1, Code3). - -unfold_literal(T) -> - cerl:unfold_literal(T). - -fold_literal(T) -> - cerl:fold_literal(T). - -%% If escape-annotated: -%% {...} => hybrid:tuple([...]) -%% [H | T] => hybrid:cons(H, T) -%% -%% Wrapper for args to hybrid:cons/hybrid:tuple that may need copying: -%% hybrid:copy(A) - -rewrite(Node, Vars) -> - case cerl:type(Node) of - tuple -> - Es = rewrite_list(cerl:tuple_es(Node), Vars), - case is_escaping(Node) of - false -> - cerl:update_c_tuple(Node, Es); - true -> - Es1 = wrap(Es, Node, Vars), - cerl:update_c_call(Node, - cerl:abstract(hybrid), - cerl:abstract(tuple), - [cerl:make_list(Es1)]) -%%% cerl:update_c_call(Node, cerl:abstract(hybrid), -%%% cerl:abstract(tuple), Es1) - end; - cons -> - H = rewrite(cerl:cons_hd(Node), Vars), - T = rewrite(cerl:cons_tl(Node), Vars), - case is_escaping(Node) of - false -> - cerl:update_c_cons(Node, H, T); - true -> - Es = wrap([H, T], Node, Vars), - cerl:update_c_call(Node, - cerl:abstract(hybrid), - cerl:abstract(cons), - Es) - end; -%%% call -> -%%% M = rewrite(cerl:call_module(Node)), -%%% F = rewrite(cerl:call_name(Node)), -%%% As = rewrite_list(cerl:call_args(Node)), -%%% case cerl:is_c_atom(M) andalso cerl:is_c_atom(F) of -%%% true -> -%%% case {cerl:atom_val(M), cerl:atom_val(F), length(As)} of -%%% {erlang, '!', 2} -> -%%% cerl:update_c_call(Node, -%%% cerl:abstract(hipe_bifs), -%%% cerl:abstract(send), -%%% [cerl:make_list(As)]); -%%% _ -> -%%% cerl:update_c_call(Node, M, F, As) -%%% end; -%%% false -> -%%% cerl:update_c_call(Node, M, F, As) -%%% end; - clause -> - B = rewrite(cerl:clause_body(Node), Vars), - cerl:update_c_clause(Node, cerl:clause_pats(Node), - cerl:clause_guard(Node), B); - primop -> - case cerl:atom_val(cerl:primop_name(Node)) of - match_fail -> - Node; - _ -> - As = rewrite_list(cerl:primop_args(Node), Vars), - cerl:update_c_primop(Node, cerl:primop_name(Node), As) - end; - _T -> - case cerl:subtrees(Node) of - [] -> - Node; - Gs -> - cerl:update_tree(Node, [rewrite_list(Ns, Vars) - || Ns <- Gs]) - end - end. - -rewrite_list([N | Ns], Vars) -> - [rewrite(N, Vars) | rewrite_list(Ns, Vars)]; -rewrite_list([], _) -> - []. - -is_escaping(T) -> - lists:member(escapes, cerl:get_ann(T)). - -wrap(Es, Node, Vars) -> - L = cerl_trees:get_label(Node), - Xs = dict:fetch(L, Vars), - wrap(Es, Xs). - -wrap([E | Es], [{S, _} | Xs]) -> - case ordsets:is_element(unsafe, S) of -%% case cerl:type(E) =/= literal of - true -> - [cerl:c_call(cerl:abstract(hybrid), - cerl:abstract(copy), - [E]) - | wrap(Es, Xs)]; - false -> - [E | wrap(Es, Xs)] - end; -wrap([], _) -> - []. diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 0c2e846010..fbb77b6a42 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -1186,6 +1186,7 @@ type(erlang, port_info, 2, Xs) -> ['links'] -> t_tuple([Item, t_list(t_pid())]); ['name'] -> t_tuple([Item, t_string()]); ['output'] -> t_tuple([Item, t_integer()]); + ['os_pid'] -> t_tuple([Item, t_sup(t_non_neg_integer(),t_atom('undefined'))]); ['registered_name'] -> t_tuple([Item, t_atom()]); List when is_list(List) -> t_tuple([t_sup([t_atom(A) || A <- List]), @@ -1268,7 +1269,6 @@ type(erlang, process_info, 2, Xs) -> ['links'] -> t_tuple([InfoItem, t_list(t_pid())]); ['memory'] -> t_tuple([InfoItem, t_non_neg_integer()]); - ['message_binary'] -> t_tuple([InfoItem, t_list()]); ['message_queue_len'] -> t_tuple([InfoItem, t_non_neg_integer()]); ['messages'] -> t_tuple([InfoItem, t_list()]); @@ -1483,6 +1483,8 @@ type(erlang, statistics, 1, Xs) -> t_tuple([t_non_neg_integer(), t_integer(0)]); ['wall_clock'] -> t_tuple([t_non_neg_integer(), t_integer(0)]); + ['scheduler_wall_time'] -> + t_list(t_tuple([t_integer(), t_number(), t_number()])); List when is_list(List) -> T_statistics_1; unknown -> @@ -1532,6 +1534,8 @@ type(erlang, system_flag, 2, Xs) -> t_sequential_tracer(); ['trace_control_word'] -> t_integer(); + ['scheduler_wall_time'] -> + t_boolean(); List when is_list(List) -> T_system_flag_2; unknown -> @@ -1593,14 +1597,10 @@ type(erlang, system_info, 1, Xs) -> t_tuple([t_atom('fullsweep_after'), t_non_neg_integer()]); ['garbage_collection'] -> t_list(); - ['global_heaps_size'] -> - t_non_neg_integer(); ['heap_sizes'] -> t_list(t_integer()); ['heap_type'] -> - t_sup([t_atom('private'), - t_atom('shared'), - t_atom('hybrid')]); + t_atom('private'); ['hipe_architecture'] -> t_atoms(['amd64', 'arm', 'powerpc', 'ppc64', 'undefined', 'ultrasparc', 'x86']); @@ -3789,7 +3789,7 @@ arg_types(erlang, port_info, 1) -> arg_types(erlang, port_info, 2) -> [t_sup(t_port(), t_atom()), t_atoms(['registered_name', 'id', 'connected', - 'links', 'name', 'input', 'output'])]; + 'links', 'name', 'input', 'output', 'os_pid'])]; arg_types(erlang, port_to_list, 1) -> [t_port()]; arg_types(erlang, ports, 0) -> @@ -3905,6 +3905,7 @@ arg_types(erlang, statistics, 1) -> t_atom('reductions'), t_atom('run_queue'), t_atom('runtime'), + t_atom('scheduler_wall_time'), t_atom('wall_clock')])]; arg_types(erlang, subtract, 2) -> arg_types(erlang, '--', 2); @@ -3929,6 +3930,7 @@ arg_types(erlang, system_flag, 2) -> t_atom('trace_control_word'), %% 'internal_cpu_topology' is an undocumented internal feature. t_atom('internal_cpu_topology'), + t_atom('scheduler_wall_time'), t_integer()]), t_sup([t_integer(), %% 'cpu_topology' @@ -3944,6 +3946,9 @@ arg_types(erlang, system_flag, 2) -> %% The following two are for 'multi_scheduling' t_atom('block'), t_atom('unblock'), + %% For 'scheduler_wall_time' + t_atom('true'), + t_atom('false'), %% The following is for 'internal_cpu_topology' t_internal_cpu_topology()])]; arg_types(erlang, system_info, 1) -> @@ -4271,7 +4276,7 @@ arg_types(hipe_bifs, ref_get, 1) -> arg_types(hipe_bifs, ref_set, 2) -> [t_hiperef(), t_immediate()]; arg_types(hipe_bifs, remove_refs_from, 1) -> - [t_mfa()]; + [t_sup([t_mfa(), t_atom('all')])]; arg_types(hipe_bifs, set_funinfo_native_address, 3) -> arg_types(hipe_bifs, set_native_address, 3); arg_types(hipe_bifs, set_native_address, 3) -> @@ -4742,7 +4747,6 @@ t_pinfo_item() -> t_atom('last_calls'), t_atom('links'), t_atom('memory'), - t_atom('message_binary'), % for hybrid heap only t_atom('message_queue_len'), t_atom('messages'), t_atom('monitored_by'), diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index ceec31742e..1579735773 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -275,8 +275,8 @@ -type tag() :: ?atom_tag | ?binary_tag | ?function_tag | ?identifier_tag | ?list_tag | ?matchstate_tag | ?nil_tag | ?number_tag - | ?opaque_tag | ?product_tag | ?tuple_tag | ?tuple_set_tag - | ?union_tag | ?var_tag. + | ?opaque_tag | ?product_tag | ?remote_tag + | ?tuple_tag | ?tuple_set_tag | ?union_tag | ?var_tag. -define(float_qual, float). -define(integer_qual, integer). @@ -2318,10 +2318,14 @@ t_inf(?product(_), _, _Mode) -> ?none; t_inf(_, ?product(_), _Mode) -> ?none; -t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Mode) -> T; -t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Mode) -> T; -t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Mode) -> T; -t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Mode) -> T; +t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Mode) -> + subst_all_vars_to_any(T); +t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Mode) -> + subst_all_vars_to_any(T); +t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Mode) -> + subst_all_vars_to_any(T); +t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Mode) -> + subst_all_vars_to_any(T); t_inf(?tuple(Elements1, Arity, _Tag1), ?tuple(Elements2, Arity, _Tag2), Mode) -> case t_inf_lists_strict(Elements1, Elements2, Mode) of bottom -> ?none; @@ -2555,8 +2559,8 @@ t_subst_dict(?list(Contents, Termination, Size), Dict) -> ?nil -> ?list(NewContents, ?nil, Size); ?any -> ?list(NewContents, ?any, Size); Other -> - ?list(NewContents, NewTermination, _) = t_cons(NewContents, Other), - ?list(NewContents, NewTermination, Size) + ?list(NewContents2, NewTermination, _) = t_cons(NewContents, Other), + ?list(NewContents2, NewTermination, Size) end end; t_subst_dict(?function(Domain, Range), Dict) -> @@ -2597,8 +2601,8 @@ t_subst_aux(?list(Contents, Termination, Size), VarMap) -> ?nil -> ?list(NewContents, ?nil, Size); ?any -> ?list(NewContents, ?any, Size); Other -> - ?list(NewContents, NewTermination, _) = t_cons(NewContents, Other), - ?list(NewContents, NewTermination, Size) + ?list(NewContents2, NewTermination, _) = t_cons(NewContents, Other), + ?list(NewContents2, NewTermination, Size) end end; t_subst_aux(?function(Domain, Range), VarMap) -> @@ -3186,8 +3190,8 @@ t_abstract_records(?list(Contents, Termination, Size), RecDict) -> ?nil -> ?list(NewContents, ?nil, Size); ?any -> ?list(NewContents, ?any, Size); Other -> - ?list(NewContents, NewTermination, _) = t_cons(NewContents, Other), - ?list(NewContents, NewTermination, Size) + ?list(NewContents2, NewTermination, _) = t_cons(NewContents, Other), + ?list(NewContents2, NewTermination, Size) end end; t_abstract_records(?function(Domain, Range), RecDict) -> diff --git a/lib/hipe/doc/src/Makefile b/lib/hipe/doc/src/Makefile index a9cd583ff4..fbfd4ca327 100644 --- a/lib/hipe/doc/src/Makefile +++ b/lib/hipe/doc/src/Makefile @@ -106,12 +106,12 @@ realclean: clean 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) + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" release_spec: diff --git a/lib/hipe/flow/Makefile b/lib/hipe/flow/Makefile index 1a531fdfe5..75e156b542 100644 --- a/lib/hipe/flow/Makefile +++ b/lib/hipe/flow/Makefile @@ -100,10 +100,10 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/flow - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(INC_FILES) $(RELSYSDIR)/flow - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/flow" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(INC_FILES) "$(RELSYSDIR)/flow" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/icode/Makefile b/lib/hipe/icode/Makefile index 0d940d4b28..0f2d6db39b 100644 --- a/lib/hipe/icode/Makefile +++ b/lib/hipe/icode/Makefile @@ -118,10 +118,10 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/icode - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/icode - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/icode" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/icode" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl index a44171b2f5..ecf1c77abc 100644 --- a/lib/hipe/icode/hipe_beam_to_icode.erl +++ b/lib/hipe/icode/hipe_beam_to_icode.erl @@ -72,12 +72,11 @@ end). %%----------------------------------------------------------------------- -%% Exported types +%% Types %%----------------------------------------------------------------------- -type hipe_beam_to_icode_ret() :: [{mfa(),#icode{}}]. - %%----------------------------------------------------------------------- %% Internal data structures %%----------------------------------------------------------------------- @@ -315,19 +314,19 @@ trans_fun([{call_ext_last,_N,{extfunc,M,F,A},_}|Instructions], Env) -> %%--- bif0 --- trans_fun([{bif,BifName,nofail,[],Reg}|Instructions], Env) -> BifInst = trans_bif0(BifName,Reg), - [hipe_icode:mk_comment({bif0,BifName}),BifInst|trans_fun(Instructions,Env)]; + [BifInst|trans_fun(Instructions,Env)]; %%--- bif1 --- trans_fun([{bif,BifName,{f,Lbl},[_] = Args,Reg}|Instructions], Env) -> {BifInsts,Env1} = trans_bif(1,BifName,Lbl,Args,Reg,Env), - [hipe_icode:mk_comment({bif1,BifName})|BifInsts] ++ trans_fun(Instructions,Env1); + BifInsts ++ trans_fun(Instructions,Env1); %%--- bif2 --- trans_fun([{bif,BifName,{f,Lbl},[_,_] = Args,Reg}|Instructions], Env) -> {BifInsts,Env1} = trans_bif(2,BifName,Lbl,Args,Reg,Env), - [hipe_icode:mk_comment({bif2,BifName})|BifInsts] ++ trans_fun(Instructions,Env1); + BifInsts ++ trans_fun(Instructions,Env1); %%--- bif3 --- trans_fun([{bif,BifName,{f,Lbl},[_,_,_] = Args,Reg}|Instructions], Env) -> {BifInsts,Env1} = trans_bif(3,BifName,Lbl,Args,Reg,Env), - [hipe_icode:mk_comment({bif3,BifName})|BifInsts] ++ trans_fun(Instructions,Env1); + BifInsts ++ trans_fun(Instructions,Env1); %%--- allocate trans_fun([{allocate,StackSlots,_}|Instructions], Env) -> trans_allocate(StackSlots) ++ trans_fun(Instructions,Env); @@ -816,7 +815,7 @@ trans_fun([{test,bs_test_tail2,{f,Lbl},[Ms,Numbits]}| Instructions], Env) -> trans_op_call({hipe_bs_primop,{bs_test_tail,Numbits}}, Lbl, [MsVar], [], Env, Instructions); %%-------------------------------------------------------------------- -%% New bit syntax instructions added in February 2004 (R10B). +%% bit syntax instructions added in February 2004 (R10B). %%-------------------------------------------------------------------- trans_fun([{bs_init2,{f,Lbl},Size,_Words,_LiveRegs,{field_flags,Flags0},X}| Instructions], Env) -> @@ -1031,7 +1030,7 @@ trans_fun([{arithfbif,fnegate,Lab,[SrcR],DestR}|Instructions], Env) -> trans_fun([{arithbif,'-',Lab,[{float,0.0},SrcR],DestR}|Instructions], Env) end; %%-------------------------------------------------------------------- -%% New apply instructions added in April 2004 (R10B). +%% apply instructions added in April 2004 (R10B). %%-------------------------------------------------------------------- trans_fun([{apply,Arity}|Instructions], Env) -> BeamArgs = extract_fun_args(Arity+2), %% +2 is for M and F @@ -1047,21 +1046,21 @@ trans_fun([{apply_last,Arity,_N}|Instructions], Env) -> % N is StackAdjustment? hipe_icode:mk_enter_primop(#apply_N{arity=Arity}, [M,F|Args]) | trans_fun(Instructions,Env)]; %%-------------------------------------------------------------------- -%% New test instruction added in April 2004 (R10B). +%% test for boolean added in April 2004 (R10B). %%-------------------------------------------------------------------- %%--- is_boolean --- trans_fun([{test,is_boolean,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(boolean,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%-------------------------------------------------------------------- -%% New test instruction added in June 2005 for R11 +%% test for function with specific arity added in June 2005 (R11). %%-------------------------------------------------------------------- %%--- is_function2 --- trans_fun([{test,is_function2,{f,Lbl},[Arg,Arity]}|Instructions], Env) -> {Code,Env1} = trans_type_test2(function2,Lbl,Arg,Arity,Env), [Code | trans_fun(Instructions,Env1)]; %%-------------------------------------------------------------------- -%% New garbage-collecting BIFs added in January 2006 for R11B. +%% garbage collecting BIFs added in January 2006 (R11B). %%-------------------------------------------------------------------- trans_fun([{gc_bif,'-',Fail,_Live,[SrcR],DstR}|Instructions], Env) -> %% Unary minus. Change this to binary minus. @@ -1079,21 +1078,21 @@ trans_fun([{gc_bif,Name,Fail,_Live,SrcRs,DstR}|Instructions], Env) -> trans_fun([{bif,Name,Fail,SrcRs,DstR}|Instructions], Env) end; %%-------------------------------------------------------------------- -%% New test instruction added in July 2007 for R12. +%% test for bitstream added in July 2007 (R12). %%-------------------------------------------------------------------- %%--- is_bitstr --- trans_fun([{test,is_bitstr,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(bitstr, Lbl, Arg, Env), [Code | trans_fun(Instructions, Env1)]; %%-------------------------------------------------------------------- -%% New stack triming instruction added in October 2007 for R12. +%% stack triming instruction added in October 2007 (R12). %%-------------------------------------------------------------------- trans_fun([{trim,N,NY}|Instructions], Env) -> %% trim away N registers leaving NY registers Moves = trans_trim(N, NY), Moves ++ trans_fun(Instructions, Env); %%-------------------------------------------------------------------- -%% New line/1 instruction in R15. +%% line instruction added in Fall 2012 (R15). %%-------------------------------------------------------------------- trans_fun([{line,_}|Instructions], Env) -> trans_fun(Instructions,Env); @@ -1297,7 +1296,7 @@ trans_bin([{bs_put_integer,{f,Lbl},Size,Unit,{field_flags,Flags0},Source}| SrcInstrs ++ trans_bin_call({hipe_bs_primop, Name}, Lbl, [Src|Args], [Offset], Base, Offset, Env2, Instructions); %%---------------------------------------------------------------- -%% New binary construction instructions added in R12B-5 (Fall 2008). +%% binary construction instructions added in Fall 2008 (R12B-5). %%---------------------------------------------------------------- trans_bin([{bs_put_utf8,{f,Lbl},_FF,A3}|Instructions], Base, Offset, Env) -> Src = trans_arg(A3), @@ -1348,7 +1347,7 @@ trans_bs_get_or_skip_utf32(Lbl, Ms, Flags0, X, Instructions, Env) -> Lbl, [Dst,MsVar], [MsVar], Env1, Instructions). %%----------------------------------------------------------------------- -%% trans_arith(Op, SrcVars, Des, Lab, Env) -> { Icode, NewEnv } +%% trans_arith(Op, SrcVars, Des, Lab, Env) -> {Icode, NewEnv} %% A failure label of type {f,0} means in a body. %% A failure label of type {f,L} where L>0 means in a guard. %% Within a guard a failure should branch to the next guard and @@ -1454,7 +1453,7 @@ clone_dst(Dest) -> %%----------------------------------------------------------------------- -%% trans_type_test(Test, Lbl, Arg, Env) -> { Icode, NewEnv } +%% trans_type_test(Test, Lbl, Arg, Env) -> {Icode, NewEnv} %% Handles all unary type tests like is_integer etc. %%----------------------------------------------------------------------- @@ -1466,7 +1465,7 @@ trans_type_test(Test, Lbl, Arg, Env) -> {[Move,I,True],Env1}. %% -%% This handles binary type tests. Currently, the only such is the new +%% This handles binary type tests. Currently, the only such is the %% is_function/2 BIF. %% trans_type_test2(function2, Lbl, Arg, Arity, Env) -> @@ -1479,7 +1478,7 @@ trans_type_test2(function2, Lbl, Arg, Arity, Env) -> %%----------------------------------------------------------------------- %% trans_puts(Code, Environment) -> -%% { Movs, Code, Vars, NewEnv } +%% {Movs, Code, Vars, NewEnv} %%----------------------------------------------------------------------- trans_puts(Code, Env) -> diff --git a/lib/hipe/icode/hipe_icode.hrl b/lib/hipe/icode/hipe_icode.hrl index 65deaf6d7c..3b21276209 100644 --- a/lib/hipe/icode/hipe_icode.hrl +++ b/lib/hipe/icode/hipe_icode.hrl @@ -64,9 +64,9 @@ -type icode_if_op() :: '>' | '<' | '>=' | '=<' | '=:=' | '=/=' | '==' | '/=' | 'fixnum_eq' | 'fixnum_neq' | 'fixnum_lt' | 'fixnum_le' | 'fixnum_ge' | 'fixnum_gt' - | 'suspend_msg_timeout'. + | 'op_exact_eqeq_2' | 'suspend_msg_timeout'. --type icode_type_test() :: 'atom' | 'bignum' | 'binary' | 'bitrst' | 'boolean' +-type icode_type_test() :: 'atom' | 'bignum' | 'binary' | 'bitstr' | 'boolean' | 'cons' | 'constant' | 'fixnum' | 'float' | 'function' | 'function2' | 'integer' | 'list' | 'nil' | 'number' | 'pid' | 'port' | 'reference' | 'tuple' @@ -88,7 +88,7 @@ -type icode_call_type() :: 'local' | 'primop' | 'remote'. -type icode_exit_class() :: 'error' | 'exit' | 'rethrow' | 'throw'. --type icode_comment_text() :: atom() | string() | {atom(), term()}. +-type icode_comment_text() :: atom() | string(). -type icode_info() :: [{'arg_types', [erl_types:erl_type()]}]. diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl index f98d859822..046949d2f2 100644 --- a/lib/hipe/icode/hipe_icode_type.erl +++ b/lib/hipe/icode/hipe_icode_type.erl @@ -79,13 +79,13 @@ -import(erl_types, [number_min/1, number_max/1, t_any/0, t_atom/1, t_atom/0, t_atom_vals/1, t_binary/0, t_bitstr/0, t_bitstr_base/1, t_bitstr_unit/1, - t_boolean/0, t_cons/0, t_constant/0, + t_boolean/0, t_cons/0, t_float/0, t_from_term/1, t_from_range/2, t_fun/0, t_fun/1, t_fun/2, t_fun_args/1, t_fun_arity/1, t_inf/2, t_inf_lists/2, t_integer/0, t_integer/1, t_is_atom/1, t_is_any/1, t_is_binary/1, t_is_bitstr/1, t_is_bitwidth/1, t_is_boolean/1, - t_is_fixnum/1, t_is_cons/1, t_is_constant/1, + t_is_fixnum/1, t_is_cons/1, t_is_maybe_improper_list/1, t_is_equal/2, t_is_float/1, t_is_fun/1, t_is_integer/1, t_is_non_neg_integer/1, t_is_number/1, t_is_matchstate/1, @@ -587,7 +587,6 @@ do_type(I, Info, Var) -> TrueLab = hipe_icode:type_true_label(I), FalseLab = hipe_icode:type_false_label(I), None = t_none(), - case lookup(Var, Info) of None -> [{TrueLab, Info}, {FalseLab, Info}]; @@ -1699,7 +1698,6 @@ lookup_list0([H|T], Info, Acc) -> lookup_list0([], _, Acc) -> lists:reverse(Acc). - %% safe_lookup treats anything that is neither in the map nor a %% constant as t_any(). Use this during transformations. diff --git a/lib/hipe/main/Makefile b/lib/hipe/main/Makefile index fc8923db6c..673431a175 100644 --- a/lib/hipe/main/Makefile +++ b/lib/hipe/main/Makefile @@ -115,10 +115,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DATA) ../vsn.mk $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/main - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/main - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DATA) ../vsn.mk "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/main" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/main" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src index d38b9ea7b1..7db4db8a57 100644 --- a/lib/hipe/main/hipe.app.src +++ b/lib/hipe/main/hipe.app.src @@ -24,7 +24,6 @@ {modules, [cerl_cconv, cerl_closurean, cerl_hipeify, - cerl_hybrid_transform, cerl_lib, cerl_messagean, cerl_pmatch, diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl index c73db872ac..b2789978a4 100644 --- a/lib/hipe/main/hipe.erl +++ b/lib/hipe/main/hipe.erl @@ -482,12 +482,7 @@ compile(Name, File, Opts0) when is_atom(Name) -> compile_core(Name, Core0, File, Opts) -> Core = cerl:from_records(Core0), - Core1 = case (erlang:system_info(heap_type) =:= hybrid) - andalso proplists:get_bool(hybrid, Opts) of - true -> cerl_hybrid_transform:transform(Core, Opts); - false -> Core - end, - compile(Name, Core1, File, Opts). + compile(Name, Core, File, Opts). %% @spec compile(Name, Core, File, options()) -> %% {ok, {Target, Binary}} | {error, Reason} diff --git a/lib/hipe/misc/Makefile b/lib/hipe/misc/Makefile index 98a69d62c7..1204ab7c0b 100644 --- a/lib/hipe/misc/Makefile +++ b/lib/hipe/misc/Makefile @@ -100,10 +100,10 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/misc - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/misc - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/misc" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/misc" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/opt/Makefile b/lib/hipe/opt/Makefile index 426afd8052..a21c543574 100644 --- a/lib/hipe/opt/Makefile +++ b/lib/hipe/opt/Makefile @@ -97,8 +97,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/ppc/Makefile b/lib/hipe/ppc/Makefile index c4b0c3ba9d..8bd2a8226c 100644 --- a/lib/hipe/ppc/Makefile +++ b/lib/hipe/ppc/Makefile @@ -108,8 +108,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/regalloc/Makefile b/lib/hipe/regalloc/Makefile index d33f641640..d4be79ea85 100644 --- a/lib/hipe/regalloc/Makefile +++ b/lib/hipe/regalloc/Makefile @@ -112,8 +112,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/rtl/Makefile b/lib/hipe/rtl/Makefile index 30026831b7..426d1bd3ee 100644 --- a/lib/hipe/rtl/Makefile +++ b/lib/hipe/rtl/Makefile @@ -103,10 +103,10 @@ realclean: clean include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/rtl - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/rtl - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/rtl" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/rtl" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/sparc/Makefile b/lib/hipe/sparc/Makefile index e9e9d54e0c..eb40117691 100644 --- a/lib/hipe/sparc/Makefile +++ b/lib/hipe/sparc/Makefile @@ -108,8 +108,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/sparc/hipe_rtl_to_sparc.erl b/lib/hipe/sparc/hipe_rtl_to_sparc.erl index df5e2b0077..c93f603826 100644 --- a/lib/hipe/sparc/hipe_rtl_to_sparc.erl +++ b/lib/hipe/sparc/hipe_rtl_to_sparc.erl @@ -19,6 +19,7 @@ %% -module(hipe_rtl_to_sparc). + -export([translate/1]). -include("../rtl/hipe_rtl.hrl"). @@ -142,8 +143,7 @@ mk_fload_rr(Base1, Base2, Dst) -> mk_fload_ii(Base1, Base2, Dst) -> io:format("~w: RTL fload with two immediates\n", [?MODULE]), Tmp = new_untagged_temp(), - mk_set(Base1, Tmp, - mk_fload_ri(Tmp, Base2, Dst)). + mk_set(Base1, Tmp, mk_fload_ri(Tmp, Base2, Dst)). mk_fload_ri(Base, Disp, Dst) -> hipe_sparc:mk_fload(Base, Disp, Dst, 'new'). @@ -239,8 +239,7 @@ mk_alu_ii(XAluOp, Src1, Src2, Dst) -> io:format("~w: ALU with two immediates (~w ~w ~w ~w)\n", [?MODULE, XAluOp, Src1, Src2, Dst]), Tmp = new_untagged_temp(), - mk_set(Src1, Tmp, - mk_alu_ri(XAluOp, Tmp, Src2, Dst)). + mk_set(Src1, Tmp, mk_alu_ri(XAluOp, Tmp, Src2, Dst)). mk_alu_ir(XAluOp, Src1, Src2, Dst) -> case xaluop_commutes(XAluOp) of @@ -249,8 +248,7 @@ mk_alu_ir(XAluOp, Src1, Src2, Dst) -> true}; _ -> Tmp = new_untagged_temp(), - {mk_set(Src1, Tmp, - mk_alu_rs(XAluOp, Tmp, Src2, Dst)), + {mk_set(Src1, Tmp, mk_alu_rs(XAluOp, Tmp, Src2, Dst)), false} end. @@ -274,8 +272,7 @@ mk_arith_ri(XAluOp, Src1, Src2, Dst) when is_integer(Src2) -> mk_alu_rs(XAluOp, Src1, hipe_sparc:mk_simm13(Src2), Dst); true -> Tmp = new_untagged_temp(), - mk_set(Src2, Tmp, - mk_alu_rs(XAluOp, Src1, Tmp, Dst)) + mk_set(Src2, Tmp, mk_alu_rs(XAluOp, Src1, Tmp, Dst)) end. mk_alu_rs(XAluOp, Src1, Src2, Dst) -> @@ -623,8 +620,7 @@ mk_move(Src, Dst, Tail) -> conv_return(I, Map, Data) -> %% TODO: multiple-value returns {[Arg], Map0} = conv_src_list(hipe_rtl:return_varlist(I), Map), - I2 = mk_move(Arg, hipe_sparc:mk_rv(), - [hipe_sparc:mk_pseudo_ret()]), + I2 = mk_move(Arg, hipe_sparc:mk_rv(), [hipe_sparc:mk_pseudo_ret()]), {I2, Map0, Data}. conv_store(I, Map, Data) -> @@ -648,8 +644,7 @@ mk_store(StOp, Src, Base1, Base2) -> mk_store2(StOp, Src, Base1, Base2); _ -> Tmp = new_untagged_temp(), - mk_set(Src, Tmp, - mk_store2(StOp, Tmp, Base1, Base2)) + mk_set(Src, Tmp, mk_store2(StOp, Tmp, Base1, Base2)) end. mk_store2(StOp, Src, Base1, Base2) -> @@ -674,8 +669,7 @@ conv_switch(I, Map, Data) -> [] -> hipe_consttab:insert_block(Data, word, LMap); SortOrder -> - hipe_consttab:insert_sorted_block( - Data, word, LMap, SortOrder) + hipe_consttab:insert_sorted_block(Data, word, LMap, SortOrder) end, %% no immediates allowed here {IndexR, Map1} = conv_dst(hipe_rtl:switch_src(I), Map), @@ -722,7 +716,7 @@ conv_aluop(RtlAluOp) -> xaluop_commutes(XAluOp) -> case XAluOp of - 'cmp' -> true; + %% 'cmp' -> true; 'cmpcc' -> true; 'add' -> true; 'addcc' -> true; @@ -739,16 +733,16 @@ xaluop_commutes(XAluOp) -> 'sll' -> false; 'srl' -> false; 'sra' -> false; - 'sllx' -> false; - 'srlx' -> false; - 'srax' -> false; + %% 'sllx' -> false; + %% 'srlx' -> false; + %% 'srax' -> false; 'ldsb' -> true; 'ldsh' -> true; - 'ldsw' -> true; + %% 'ldsw' -> true; 'ldub' -> true; 'lduh' -> true; - 'lduw' -> true; - 'ldx' -> true + 'lduw' -> true + %% 'ldx' -> true end. %%% Check if an extended SPARC AluOp is a shift. diff --git a/lib/hipe/tools/Makefile b/lib/hipe/tools/Makefile index f90d3c9f70..6761f11b01 100644 --- a/lib/hipe/tools/Makefile +++ b/lib/hipe/tools/Makefile @@ -96,8 +96,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/util/Makefile b/lib/hipe/util/Makefile index 2f17eee7f3..a5ee232057 100644 --- a/lib/hipe/util/Makefile +++ b/lib/hipe/util/Makefile @@ -103,10 +103,10 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/util - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/util - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/util" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/util" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/x86/Makefile b/lib/hipe/x86/Makefile index f92e7eeec1..3602949944 100644 --- a/lib/hipe/x86/Makefile +++ b/lib/hipe/x86/Makefile @@ -116,8 +116,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/ic/c_src/Makefile.in b/lib/ic/c_src/Makefile.in index 5e034c47c6..d8b8c85a8e 100644 --- a/lib/ic/c_src/Makefile.in +++ b/lib/ic/c_src/Makefile.in @@ -140,12 +140,12 @@ $(OBJDIR)/%.o: %.c include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/c_src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_DATA) ic.c ic_tmo.c $(RELSYSDIR)/c_src - $(INSTALL_DATA) $(IDL_FILES) $(H_FILES) $(RELSYSDIR)/include - $(INSTALL_DATA) $(LIBRARY) $(RELSYSDIR)/priv/lib + $(INSTALL_DIR) "$(RELSYSDIR)/c_src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" + $(INSTALL_DATA) ic.c ic_tmo.c "$(RELSYSDIR)/c_src" + $(INSTALL_DATA) $(IDL_FILES) $(H_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(LIBRARY) "$(RELSYSDIR)/priv/lib" release_docs_spec: diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile index 208f16e441..0ee242b234 100644 --- a/lib/ic/doc/src/Makefile +++ b/lib/ic/doc/src/Makefile @@ -220,12 +220,12 @@ debug opt: 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_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/doc/html - (/bin/cp -rf $(HTMLDIR) $(RELSYSDIR)/doc) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc") + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/ic/doc/src/ic_clib.xml b/lib/ic/doc/src/ic_clib.xml index b557c4b5f6..ebeaabae91 100644 --- a/lib/ic/doc/src/ic_clib.xml +++ b/lib/ic/doc/src/ic_clib.xml @@ -4,7 +4,7 @@ <cref> <header> <copyright> - <year>2003</year><year>2009</year> + <year>2003</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -41,7 +41,7 @@ </section> <funcs> <func> - <name><ret>CORBA_Environment*</ret><nametext>CORBA_Environment_alloc(int inbufsz, int outbufsz)</nametext></name> + <name><ret>CORBA_Environment *</ret><nametext>CORBA_Environment_alloc(int inbufsz, int outbufsz)</nametext></name> <fsummary>Allocate environment data.</fsummary> <desc> <p>This function is used to allocate and initiate the @@ -79,14 +79,14 @@ </desc> </func> <func> - <name><ret>CORBA_char*</ret><nametext>CORBA_string_alloc(CORBA_unsigned_long len)</nametext></name> + <name><ret>CORBA_char *</ret><nametext>CORBA_string_alloc(CORBA_unsigned_long len)</nametext></name> <fsummary>Allocate a string.</fsummary> <desc> <p>Allocates a (simple) CORBA character string of length <c>len + 1</c>.</p> </desc> </func> <func> - <name><ret>CORBA_wchar*</ret><nametext>CORBA_wstring_alloc(CORBA_unsigned_long len)</nametext></name> + <name><ret>CORBA_wchar *</ret><nametext>CORBA_wstring_alloc(CORBA_unsigned_long len)</nametext></name> <fsummary>Allocate a wide string.</fsummary> <desc> <p>Allocates a CORBA wide string of length <c>len + 1</c>.</p> @@ -101,7 +101,7 @@ </section> <funcs> <func> - <name><ret>CORBA_char*</ret><nametext>CORBA_exception_id(CORBA_Environment *env)</nametext></name> + <name><ret>CORBA_char *</ret><nametext>CORBA_exception_id(CORBA_Environment *env)</nametext></name> <fsummary>Get exception identity.</fsummary> <desc> <p>Returns the exception identity if an exception is set, otherwise @@ -109,7 +109,7 @@ </desc> </func> <func> - <name><ret>void*</ret><nametext>CORBA_exception_value(CORBA_Environment *env)</nametext></name> + <name><ret>void *</ret><nametext>CORBA_exception_value(CORBA_Environment *env)</nametext></name> <fsummary>Get exception value.</fsummary> <desc> <p>Returns the exception value, if an exception is set, otherwise @@ -160,7 +160,7 @@ </desc> </func> <func> - <name><ret>oe_map_t*</ret><nametext>oe_merge_maps(oe_map_t *maps, int size)</nametext></name> + <name><ret>oe_map_t *</ret><nametext>oe_merge_maps(oe_map_t *maps, int size)</nametext></name> <fsummary>Merge an array of server maps to one single map.</fsummary> <desc> <p>Merge an array of server maps to one single map.</p> diff --git a/lib/ic/examples/pre_post_condition/Makefile b/lib/ic/examples/pre_post_condition/Makefile index 8f85babb1a..8ae37394ed 100644 --- a/lib/ic/examples/pre_post_condition/Makefile +++ b/lib/ic/examples/pre_post_condition/Makefile @@ -124,8 +124,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/pre_post_condition - $(INSTALL_DATA) $(ERL_FILES) $(IDL_FILES) $(TXT_FILES) $(RELSYSDIR)/examples/pre_post_condition + $(INSTALL_DIR) "$(RELSYSDIR)/examples/pre_post_condition" + $(INSTALL_DATA) $(ERL_FILES) $(IDL_FILES) $(TXT_FILES) "$(RELSYSDIR)/examples/pre_post_condition" release_docs_spec: diff --git a/lib/ic/java_src/com/ericsson/otp/ic/Makefile b/lib/ic/java_src/com/ericsson/otp/ic/Makefile index f730749ccb..1b14d4430f 100644 --- a/lib/ic/java_src/com/ericsson/otp/ic/Makefile +++ b/lib/ic/java_src/com/ericsson/otp/ic/Makefile @@ -109,10 +109,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/java_src/com/ericsson/otp/ic - $(INSTALL_DATA) $(JAVA_FILES) $(RELSYSDIR)/java_src/com/ericsson/otp/ic - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/java_src/com/ericsson/otp/ic" + $(INSTALL_DATA) $(JAVA_FILES) "$(RELSYSDIR)/java_src/com/ericsson/otp/ic" + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/ic/src/Makefile b/lib/ic/src/Makefile index 5dac304e32..c830ebec95 100644 --- a/lib/ic/src/Makefile +++ b/lib/ic/src/Makefile @@ -196,23 +196,23 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DIR) $(RELSYSDIR)/examples/c-client - $(INSTALL_DATA) $(CCL_EX_FILES) $(RELSYSDIR)/examples/c-client - $(INSTALL_DIR) $(RELSYSDIR)/examples/c-server - $(INSTALL_DATA) $(CSRV_EX_FILES) $(RELSYSDIR)/examples/c-server - $(INSTALL_DIR) $(RELSYSDIR)/examples/erl-plain - $(INSTALL_DATA) $(EPL_EX_FILES) $(RELSYSDIR)/examples/erl-plain - $(INSTALL_DIR) $(RELSYSDIR)/examples/erl-genserv - $(INSTALL_DATA) $(ESRV_EX_FILES) $(RELSYSDIR)/examples/erl-genserv - $(INSTALL_DIR) $(RELSYSDIR)/examples/java-client-server - $(INSTALL_DATA) $(JAVA_EX_FILES) $(RELSYSDIR)/examples/java-client-server - $(INSTALL_DIR) $(RELSYSDIR)/examples/all-against-all - $(INSTALL_DATA) $(MIXED_EX_FILES) $(RELSYSDIR)/examples/all-against-all + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/c-client" + $(INSTALL_DATA) $(CCL_EX_FILES) "$(RELSYSDIR)/examples/c-client" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/c-server" + $(INSTALL_DATA) $(CSRV_EX_FILES) "$(RELSYSDIR)/examples/c-server" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/erl-plain" + $(INSTALL_DATA) $(EPL_EX_FILES) "$(RELSYSDIR)/examples/erl-plain" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/erl-genserv" + $(INSTALL_DATA) $(ESRV_EX_FILES) "$(RELSYSDIR)/examples/erl-genserv" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/java-client-server" + $(INSTALL_DATA) $(JAVA_EX_FILES) "$(RELSYSDIR)/examples/java-client-server" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/all-against-all" + $(INSTALL_DATA) $(MIXED_EX_FILES) "$(RELSYSDIR)/examples/all-against-all" release_docs_spec: diff --git a/lib/ic/src/ic.erl b/lib/ic/src/ic.erl index 50fad921c4..bc7ec8ba91 100644 --- a/lib/ic/src/ic.erl +++ b/lib/ic/src/ic.erl @@ -250,7 +250,7 @@ make_erl_options(Opts) -> Optimize = Opts#options.optimize, PreProc = lists:flatten( - lists:map(fun(D) -> io_lib:format("-I~s ", [ic_util:to_list(D)]) end, + lists:map(fun(D) -> io_lib:format("-I\"~s\" ", [ic_util:to_list(D)]) end, Includes1)++ lists:map( fun ({Name, Value}) -> diff --git a/lib/ic/src/ic_pp.erl b/lib/ic/src/ic_pp.erl index 8b53473caa..50eeeab48c 100644 --- a/lib/ic/src/ic_pp.erl +++ b/lib/ic/src/ic_pp.erl @@ -1894,23 +1894,37 @@ include_dir(Flags) when is_list(Flags)-> include_dir(_Flags) -> []. -include_dir(Flags,IncDir) -> +include_dir(Flags,IncDirs) -> case string:str(Flags,"-I") of 0 -> - lists:reverse(IncDir); + lists:reverse(IncDirs); X -> - Rem2 = string:sub_string(Flags, X+2), - Rem = string:strip(Rem2, left), - Y = string:str(Rem," "), - case string:str(Rem," ") of - 0 -> - lists:reverse([string:sub_string(Rem, Y+1)|IncDir]); - Y -> - include_dir(string:sub_string(Rem, Y+1), - [string:sub_string(Rem,1,Y-1)|IncDir]) - end + {NewDir, RemainingFlags} = + gobble_inc_dir(string:sub_string(Flags, X+2),nq,[]), + include_dir(RemainingFlags, [NewDir|IncDirs]) end. +% nq = not-quoted, q = quoted. +% Possible strange scenarios: +% /usr/test\ ing/ +% "/usr/test ing/" +% /usr/test\"ing/ +% "/usr/test\"ing/" +gobble_inc_dir([],nq,Acc) -> + % Only accept nq here, if we end up here in q mode the user has missed a " + {lists:reverse(Acc),[]}; +gobble_inc_dir([$\\,$"|R],Q,Acc) -> + gobble_inc_dir(R,Q,[$"|Acc]); +gobble_inc_dir([$"|R],nq,Acc) -> + gobble_inc_dir(R,q,Acc); +gobble_inc_dir([$"|R],q,Acc) -> + gobble_inc_dir(R,nq,Acc); +gobble_inc_dir([$\\,$ |R],nq,Acc) -> + gobble_inc_dir(R,nq,[$ |Acc]); +gobble_inc_dir([$ |R],nq,Acc) -> + {lists:reverse(Acc),R}; +gobble_inc_dir([C|R],Q,Acc) -> + gobble_inc_dir(R,Q,[C|Acc]). %%=============================================================== @@ -1954,7 +1968,6 @@ find_inc_file2(FileName, [D|Rem]) -> _ -> D++"/" end, - case catch file:read_file_info(Dir++FileName) of {ok, _} -> {ok, Dir++FileName}; diff --git a/lib/ic/test/Makefile b/lib/ic/test/Makefile index 1d90a1bc17..65003d703b 100644 --- a/lib/ic/test/Makefile +++ b/lib/ic/test/Makefile @@ -239,39 +239,39 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/ic_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/ic_register_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/ic_pragma_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/ic_pp_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/ic_be_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/c_client_erl_server_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/c_client_erl_server_proto_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/c_client_erl_server_proto_tmo_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/erl_client_c_server_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/erl_client_c_server_proto_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/java_client_erl_server_SUITE_data + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/ic_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/ic_register_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/ic_pragma_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/ic_pp_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/ic_be_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/c_client_erl_server_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/c_client_erl_server_proto_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/c_client_erl_server_proto_tmo_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/erl_client_c_server_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/erl_client_c_server_proto_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/java_client_erl_server_SUITE_data" $(INSTALL_DATA) $(IDL_FILES) ic.cover $(TEST_SPEC_FILE) $(ERL_FILES) \ - $(RELSYSDIR) - $(INSTALL_DATA) $(COMPILER_TEST_FILES) $(RELSYSDIR)/ic_SUITE_data + "$(RELSYSDIR)" + $(INSTALL_DATA) $(COMPILER_TEST_FILES) "$(RELSYSDIR)/ic_SUITE_data" $(INSTALL_DATA) $(COMPILER_TEST_FILES2) \ - $(RELSYSDIR)/ic_register_SUITE_data + "$(RELSYSDIR)/ic_register_SUITE_data" $(INSTALL_DATA) $(COMPILER_TEST_FILES3) \ - $(RELSYSDIR)/ic_pragma_SUITE_data + "$(RELSYSDIR)/ic_pragma_SUITE_data" $(INSTALL_DATA) $(COMPILER_TEST_FILES4) \ - $(RELSYSDIR)/ic_be_SUITE_data + "$(RELSYSDIR)/ic_be_SUITE_data" $(INSTALL_DATA) $(PREPROCESSOR_TEST_FILES) \ - $(RELSYSDIR)/ic_pp_SUITE_data + "$(RELSYSDIR)/ic_pp_SUITE_data" $(INSTALL_DATA) $(C_CLIENT_ERL_SERVER_TEST_FILES) \ - $(RELSYSDIR)/c_client_erl_server_SUITE_data + "$(RELSYSDIR)/c_client_erl_server_SUITE_data" $(INSTALL_DATA) $(C_CLIENT_ERL_SERVER_PROTO_TEST_FILES) \ - $(RELSYSDIR)/c_client_erl_server_proto_SUITE_data + "$(RELSYSDIR)/c_client_erl_server_proto_SUITE_data" $(INSTALL_DATA) $(C_CLIENT_ERL_SERVER_PROTO_TMO_TEST_FILES) \ - $(RELSYSDIR)/c_client_erl_server_proto_tmo_SUITE_data + "$(RELSYSDIR)/c_client_erl_server_proto_tmo_SUITE_data" $(INSTALL_DATA) $(ERL_CLIENT_C_SERVER_TEST_FILES) \ - $(RELSYSDIR)/erl_client_c_server_SUITE_data + "$(RELSYSDIR)/erl_client_c_server_SUITE_data" $(INSTALL_DATA) $(ERL_CLIENT_C_SERVER_PROTO_TEST_FILES) \ - $(RELSYSDIR)/erl_client_c_server_proto_SUITE_data - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) + "$(RELSYSDIR)/erl_client_c_server_proto_SUITE_data" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(JAVA_CLIENT_ERL_SERVER_TEST_FILES) \ - $(RELSYSDIR)/java_client_erl_server_SUITE_data + "$(RELSYSDIR)/java_client_erl_server_SUITE_data" diff --git a/lib/ic/test/java_client_erl_server_SUITE.erl b/lib/ic/test/java_client_erl_server_SUITE.erl index 407c3d2d44..d4523dc2ad 100644 --- a/lib/ic/test/java_client_erl_server_SUITE.erl +++ b/lib/ic/test/java_client_erl_server_SUITE.erl @@ -249,7 +249,7 @@ marshal_any_2(Config) when is_list(Config) -> java(Java, Dir, ClassAndArgs) -> - cmd(Java++" -classpath "++classpath(Dir)++" "++ClassAndArgs). + cmd(Java++" -classpath \""++classpath(Dir)++"\" "++ClassAndArgs). java(Java, Dir, Class, Args) -> java(Java, Dir, Class++" "++to_string(Args)). diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile index b3a98fd6d5..1a8e1c7ca8 100644 --- a/lib/inets/doc/src/Makefile +++ b/lib/inets/doc/src/Makefile @@ -140,13 +140,13 @@ clean_man: 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_DATA) $(HTMLDIR)/* $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + $(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" release_spec: diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 70c845bade..14ce3cbe7f 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -480,66 +480,69 @@ apply(Module, Function, [ReplyInfo | Args]) <d>ex: "134.138" or "[FEDC:BA98" (all IP-addresses starting with 134.138 or FEDC:BA98), "66.35.250.150" or "[2010:836B:4179::836B:4179]" (a complete IP-address).</d> <v>MaxSessions = integer() </v> <d>Default is <c>2</c>. - Maximum number of persistent connections to a host.</d> + Maximum number of persistent connections to a host.</d> <v>MaxKeepAlive = integer() </v> - <d>Default is <c>5</c>. - Maximum number of outstanding requests on the same connection to - a host.</d> - <v>KeepAliveTimeout = integer() </v> - <d>Default is <c>120000</c> (= 2 min). - If a persistent connection is idle longer than the - <c>keep_alive_timeout</c> the client will close the connection. - The server may also have such a time out but you should - not count on it!</d> + <d>Default is <c>5</c>. + Maximum number of outstanding requests on the same connection to + a host.</d> + <v>KeepAliveTimeout = integer() </v> + <d>Default is <c>120000</c> (= 2 min). + If a persistent connection is idle longer than the + <c>keep_alive_timeout</c> in milliseconds, + the client will close the connection. + The server may also have such a time out but you should + not count on it!</d> <v>MaxPipeline = integer() </v> - <d>Default is <c>2</c>. - Maximum number of outstanding requests on a pipelined connection to a host.</d> - <v>PipelineTimeout = integer() </v> - <d>Default is <c>0</c>, - which will result in pipelining not being used. - If a persistent connection is idle longer than the - <c>pipeline_timeout</c> the client will close the connection. </d> + <d>Default is <c>2</c>. + Maximum number of outstanding requests on a pipelined connection + to a host.</d> + <v>PipelineTimeout = integer() </v> + <d>Default is <c>0</c>, + which will result in pipelining not being used. + If a persistent connection is idle longer than the + <c>pipeline_timeout</c> in milliseconds, + the client will close the connection. </d> <v>CookieMode = enabled | disabled | verify </v> <d>Default is <c>disabled</c>. - If Cookies are enabled all valid cookies will automatically be - saved in the client manager's cookie database. - If the option <c>verify</c> is used the function <c>store_cookies/2</c> - has to be called for the cookies to be saved.</d> - <v>IpFamily = inet | inet6 | inet6fb4 </v> - <d>By default <c>inet</c>. - When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6. - It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>. - The option is here to provide a workaround for buggy ipv6 stacks to ensure that - ipv4 will always work.</d> + If Cookies are enabled all valid cookies will automatically be + saved in the client manager's cookie database. + If the option <c>verify</c> is used the function <c>store_cookies/2</c> + has to be called for the cookies to be saved.</d> + <v>IpFamily = inet | inet6 | inet6fb4 </v> + <d>By default <c>inet</c>. + When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6. + It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>. + The option is here to provide a workaround for buggy ipv6 stacks to ensure that + ipv4 will always work.</d> <v>IpAddress = ip_address() </v> - <d>If the host has several network interfaces, this option specifies which one to use. - See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> + <d>If the host has several network interfaces, this option specifies which one to use. + See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> <v>Port = integer() </v> - <d>Specify which local port number to use. - See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> - <v>VerboseMode = false | verbose | debug | trace </v> - <d>Default is <c>false</c>. - This option is used to switch on (or off) - different levels of erlang trace on the client. - It is a debug feature.</d> + <d>Specify which local port number to use. + See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> + <v>VerboseMode = false | verbose | debug | trace </v> + <d>Default is <c>false</c>. + This option is used to switch on (or off) + different levels of erlang trace on the client. + It is a debug feature.</d> <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> - <p>Sets options to be used for subsequent requests.</p> - <note> - <p>If possible the client will keep its connections - alive and use persistent connections - with or without pipeline depending on configuration - and current circumstances. The HTTP/1.1 specification does not - provide a guideline for how many requests would be - ideal to be sent on a persistent connection, - this very much depends on the - application. Note that a very long queue of requests may cause a - user perceived delay as earlier requests may take a long time - to complete. The HTTP/1.1 specification does suggest a - limit of 2 persistent connections per server, which is the - default value of the <c>max_sessions</c> option. </p> - </note> + <p>Sets options to be used for subsequent requests.</p> + <note> + <p>If possible the client will keep its connections + alive and use persistent connections + with or without pipeline depending on configuration + and current circumstances. The HTTP/1.1 specification does not + provide a guideline for how many requests would be + ideal to be sent on a persistent connection, + this very much depends on the + application. Note that a very long queue of requests may cause a + user perceived delay as earlier requests may take a long time + to complete. The HTTP/1.1 specification does suggest a + limit of 2 persistent connections per server, which is the + default value of the <c>max_sessions</c> option. </p> + </note> <marker id="get_options"></marker> </desc> @@ -648,6 +651,8 @@ apply(Module, Function, [ReplyInfo | Args]) <p>Resets (clears) the cookie database for the specified <c>Profile</c>. If no profile is specified the default profile will be used. </p> + + <marker id="which_cookies"></marker> </desc> </func> @@ -667,6 +672,42 @@ apply(Module, Function, [ReplyInfo | Args]) <p>This function produces a list of the entire cookie database. It is intended for debugging/testing purposes. If no profile is specified the default profile will be used. </p> + + <marker id="which_sessions"></marker> + </desc> + </func> + + <func> + <name>which_sessions() -> session_info()</name> + <name>which_sessions(Profile) -> session_info()</name> + <fsummary>Produces a slightly processed dump of the sessions database.</fsummary> + <type> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + <v>session_info() = {GoodSessions, BadSessions, NonSessions}</v> + <v>GoodSessions = session()</v> + <v>BadSessions = tuple()</v> + <v>NonSessions = term()</v> + </type> + <desc> + <p>This function produces a slightly processed dump of the session + database. It is intended for debugging. + If no profile is specified the default profile will be used. </p> + + <marker id="info"></marker> + </desc> + </func> + + <func> + <name>info() -> list()</name> + <name>info(Profile) -> list()</name> + <fsummary>Produces a list of miscelleneous info</fsummary> + <type> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + </type> + <desc> + <p>This function produces a list of miscelleneous info. + It is intended for debugging. + If no profile is specified the default profile will be used. </p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index dfdeb4016c..3aae1ff70a 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>Inets Release Notes</title> @@ -32,16 +32,93 @@ <file>notes.xml</file> </header> + + <section> + <title>Inets 5.9.1</title> + + <section> + <title>Improvements and New Features</title> + <!-- + <p>-</p> + --> + + <list> + <item> + <p>Better handling of errorI(s) during update of the session + database. </p> + <p>Also added and updated some debugging functions + <seealso marker="httpc#which_sessions">which_sessions/10,1</seealso> + and + <seealso marker="httpc#info">info/0</seealso>. </p> + <p>Own Id: OTP-10093</p> + <p>Aux Id: Seq 12062</p> + </item> - <section><title>Inets 5.9</title> - - <section><title>Improvements and New Features</title> -<!-- - <p>-</p> ---> + <item> + <p>Removed R14B compatible version of (inets-service and + tftp) behaviour definition. </p> + <p>Own Id: OTP-10095</p> + </item> + + <item> + <p>[httpc] Documentation of KeepAlive and Pipeline timeout + options have been improved. </p> + <p>Own Id: OTP-10114</p> + </item> + </list> + + </section> + <section> + <title>Fixed Bugs and Malfunctions</title> + <!-- + <p>-</p> + --> + <list> <item> + <p>[httpc] Cancel request does not work due to incorrect + handler table creation (wrong keypos). </p> + <p>Vyacheslav Vorobyov</p> + <p>Own Id: OTP-10092</p> + </item> + + </list> + + </section> + + <section> + <title>Incompatibilities</title> + <p>-</p> + + <!-- + <list> + <item> + <p>[httpc|httpd] The old ssl implementation (based on OpenSSL), + has been deprecated. The config option that specified usage of + this version of the ssl app, <c>ossl</c>, has been removed. </p> + <p>Own Id: OTP-9522</p> + </item> + + </list> + --> + + </section> + + </section> <!-- 5.9.1 --> + + + <section> + <title>Inets 5.9</title> + + <section> + <title>Improvements and New Features</title> + <!-- + <p>-</p> + --> + + <list> + <item> <p>[httpd] Make the server header configurable with new config option <seealso marker="httpd#prop_server_tokens">server_tokens</seealso>. @@ -102,11 +179,11 @@ </section> +<!-- <section> <title>Incompatibilities</title> <p>-</p> -<!-- <list> <item> <p>[httpc|httpd] The old ssl implementation (based on OpenSSL), @@ -116,9 +193,9 @@ </item> </list> ---> </section> +--> </section> <!-- 5.9 --> @@ -285,31 +362,6 @@ </section> - <section> - <title>Incompatibilities</title> -<!-- - <p>-</p> ---> - - <list> - <item> - <p>[httpc] Deprecated interface module <c>http</c> has been removed. - It has (long) been replaced by http client interface module - <seealso marker="httpc#">httpc</seealso>. </p> - <p>Own Id: OTP-9359</p> - </item> - - <item> - <p>[httpc|httpd] The old ssl implementation (based on OpenSSL), - has been deprecated. The config option that specified usage of - this version of the ssl app, <c>ossl</c>, has been removed. </p> - <p>Own Id: OTP-9522</p> - </item> - - </list> - - </section> - <section><title>Fixed Bugs and Malfunctions</title> <!-- <p>-</p> @@ -332,6 +384,24 @@ </list> </section> +<!-- + <section> + <title>Incompatibilities</title> + <p>-</p> + + <list> + <item> + <p>[httpc] Deprecated interface module <c>http</c> has been removed. + It has (long) been replaced by http client interface module + <seealso marker="httpc#">httpc</seealso>. </p> + <p>Own Id: OTP-9359</p> + </item> + + </list> + + </section> +--> + </section> <!-- 5.7.2 --> diff --git a/lib/inets/examples/httpd_load_test/Makefile b/lib/inets/examples/httpd_load_test/Makefile index 1cc61ad8ae..de1b2a289f 100644 --- a/lib/inets/examples/httpd_load_test/Makefile +++ b/lib/inets/examples/httpd_load_test/Makefile @@ -34,7 +34,7 @@ VSN=$(INETS_VSN) # Release directory specification # ---------------------------------------------------- RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN) -EXAMPLE_RELSYSDIR = $(RELSYSDIR)/examples +EXAMPLE_RELSYSDIR = "$(RELSYSDIR)/examples" HDLT_RELSYSDIR = $(EXAMPLE_RELSYSDIR)/httpd_load_test diff --git a/lib/inets/examples/server_root/Makefile b/lib/inets/examples/server_root/Makefile index d7a3231068..2230a7eafc 100644 --- a/lib/inets/examples/server_root/Makefile +++ b/lib/inets/examples/server_root/Makefile @@ -174,36 +174,36 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth - $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin - $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf - $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/auth" + $(INSTALL_DATA) $(AUTH_FILES) "$(RELSYSDIR)/examples/server_root/auth" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/cgi-bin" + $(INSTALL_SCRIPT) $(CGI_FILES) "$(RELSYSDIR)/examples/server_root/cgi-bin" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/conf" + $(INSTALL_DATA) $(CONF_FILES) "$(RELSYSDIR)/examples/server_root/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/htdocs/open" $(INSTALL_DATA) $(OPEN_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/open - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open + "$(RELSYSDIR)/examples/server_root/htdocs/open" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/htdocs/mnesia_open" $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc + "$(RELSYSDIR)/examples/server_root/htdocs/mnesia_open" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/htdocs/misc" $(INSTALL_DATA) $(MISC_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/misc + "$(RELSYSDIR)/examples/server_root/htdocs/misc" $(INSTALL_DIR) \ - $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret + "$(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret" $(INSTALL_DIR) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret + "$(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret" $(INSTALL_DATA) $(SECRET_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/secret + "$(RELSYSDIR)/examples/server_root/htdocs/secret" $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs - $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons - $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl - $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs + "$(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/htdocs" + $(INSTALL_DATA) $(HTDOCS_FILES) "$(RELSYSDIR)/examples/server_root/htdocs" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/icons" + $(INSTALL_DATA) $(ICON_FILES) "$(RELSYSDIR)/examples/server_root/icons" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/ssl" + $(INSTALL_DATA) $(SSL_FILES) "$(RELSYSDIR)/examples/server_root/ssl" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/logs" release_docs_spec: diff --git a/lib/inets/priv/Makefile b/lib/inets/priv/Makefile index 85da409e55..4dc521f618 100644 --- a/lib/inets/priv/Makefile +++ b/lib/inets/priv/Makefile @@ -57,8 +57,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(EXECUTABLES) $(RELSYSDIR)/priv/bin + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_SCRIPT) $(EXECUTABLES) "$(RELSYSDIR)/priv/bin" release_docs_spec: diff --git a/lib/inets/src/ftp/Makefile b/lib/inets/src/ftp/Makefile index 19b93870df..426286f254 100644 --- a/lib/inets/src/ftp/Makefile +++ b/lib/inets/src/ftp/Makefile @@ -88,11 +88,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/ftp - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/ftp - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/ftp" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/ftp" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile index d490e59929..850c790bd1 100644 --- a/lib/inets/src/http_client/Makefile +++ b/lib/inets/src/http_client/Makefile @@ -89,11 +89,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/http_client - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_client - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/http_client" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/http_client" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index f4802fb96d..b6e7708353 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -39,6 +39,7 @@ cookie_header/1, cookie_header/2, cookie_header/3, which_cookies/0, which_cookies/1, reset_cookies/0, reset_cookies/1, + which_sessions/0, which_sessions/1, stream_next/1, default_profile/0, profile_name/1, profile_name/2, @@ -267,6 +268,7 @@ set_option(Key, Value, Profile) -> %% Reason - term() %% Description: Retrieves the current options. %%------------------------------------------------------------------------- + get_options() -> record_info(fields, options). @@ -373,8 +375,6 @@ cookie_header(Url, Opts, Profile) {error, {not_started, Profile}} end. - - %%-------------------------------------------------------------------------- %% which_cookies() -> [cookie()] @@ -398,6 +398,28 @@ which_cookies(Profile) -> %%-------------------------------------------------------------------------- +%% which_sessions() -> {GoodSession, BadSessions, NonSessions} +%% which_sessions(Profile) -> {GoodSession, BadSessions, NonSessions} +%% +%% Description: Debug function, dumping the sessions database, sorted +%% into three groups (Good-, Bad- and Non-sessions). +%%------------------------------------------------------------------------- +which_sessions() -> + which_sessions(default_profile()). + +which_sessions(Profile) -> + ?hcrt("which sessions", [{profile, Profile}]), + try + begin + httpc_manager:which_sessions(profile_name(Profile)) + end + catch + exit:{noproc, _} -> + {[], [], []} + end. + + +%%-------------------------------------------------------------------------- %% info() -> list() %% info(Profile) -> list() %% diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index b8c34bd99b..923213d34d 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -55,7 +55,7 @@ status_line, % {Version, StatusCode, ReasonPharse} headers, % #http_response_h{} body, % binary() - mfa, % {Moduel, Function, Args} + mfa, % {Module, Function, Args} pipeline = queue:new(), % queue() keep_alive = queue:new(), % queue() status, % undefined | new | pipeline | keep_alive | close | ssl_tunnel @@ -1713,7 +1713,32 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) -> catch error:undef -> % This could happen during code upgrade Session2 = erlang:setelement(Pos, Session, Value), - insert_session(Session2, ProfileName) + insert_session(Session2, ProfileName); + T:E -> + error_logger:error_msg("Failed updating session: " + "~n ProfileName: ~p" + "~n SessionId: ~p" + "~n Pos: ~p" + "~n Value: ~p" + "~nwhen" + "~n Session (db) info: ~p" + "~n Session (db): ~p" + "~n Session (record): ~p" + "~n T: ~p" + "~n E: ~p", + [ProfileName, SessionId, Pos, Value, + (catch httpc_manager:which_session_info(ProfileName)), + Session, + (catch httpc_manager:lookup_session(ProfileName, SessionId)), + T, E]), + exit({failed_updating_session, + [{profile, ProfileName}, + {session_id, SessionId}, + {pos, Pos}, + {value, Value}, + {etype, T}, + {error, E}, + {stacktrace, erlang:get_stacktrace()}]}) end. diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index b225b43214..3612b331e7 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -34,8 +34,11 @@ retry_request/2, redirect_request/2, insert_session/2, + lookup_session/2, update_session/4, delete_session/2, + which_sessions/1, + which_session_info/1, set_options/2, get_options/2, store_cookies/3, @@ -59,17 +62,9 @@ options = #options{} }). --record(handler_info, - { - id, % Id of the request: request_id() - starter, % Pid of the handler starter process (temp): pid() - handler, % Pid of the handler process: pid() - from, % From for the request: from() - state % State of the handler: initiating | started | operational | canceled - }). - -define(DELAY, 500). + %%==================================================================== %% Internal Application API %%==================================================================== @@ -195,13 +190,28 @@ insert_session(Session, ProfileName) -> %%-------------------------------------------------------------------- +%% Function: lookup_session(SessionId, ProfileName) -> _ +%% SessionId - term() +%% ProfileName - atom() +%% +%% Description: Looks up a session record in the httpc manager +%% table <ProfileName>__session_db. +%%-------------------------------------------------------------------- + +lookup_session(SessionId, ProfileName) -> + SessionDbName = session_db_name(ProfileName), + ?hcrt("lookup session", [{session_id, SessionId}, {profile, ProfileName}]), + ets:lookup(SessionDbName, SessionId). + + +%%-------------------------------------------------------------------- %% Function: update_session(ProfileName, SessionId, Pos, Value) -> _ %% Session - #session{} %% ProfileName - atom() %% %% Description: Update, only one field (Pos) of the session record %% identified by the SessionId, the session information -%% of the httpc manager table <ProfileName>_session_db. +%% of the httpc manager table <ProfileName>__session_db. %% Intended to be called by the httpc request handler process. %%-------------------------------------------------------------------- @@ -216,12 +226,12 @@ update_session(ProfileName, SessionId, Pos, Value) -> %%-------------------------------------------------------------------- -%% Function: delete_session(SessionId, ProfileName) -> _ +%% Function: delete_session(SessionId, ProfileName) -> void() %% SessionId - {{Host, Port}, HandlerPid} %% ProfileName - atom() %% %% Description: Deletes session information from the httpc manager -%% table httpc_manager_session_db_<Profile>. Intended to be called by +%% table <ProfileName>__session_db. Intended to be called by %% the httpc request handler process. %%-------------------------------------------------------------------- @@ -232,6 +242,57 @@ delete_session(SessionId, ProfileName) -> %%-------------------------------------------------------------------- +%% Function: which sessions(ProfileName) -> SessionsInfo +%% ProfileName - atom() +%% SessionsInfo - {GoodSessions, BadSessions, NonSessions} +%% GoodSessions - [#session{}] +%% BadSessions - [tuple()] +%% NonSessions - [term()] +%% +%% Description: Produces a list of all sessions in the session db. +%% Used for debugging and since that is the intent, there is some +%% checking and transforming done, which produces the results. +%%-------------------------------------------------------------------- + +which_sessions(ProfileName) -> + ?hcrt("which_sessions", [{profile, ProfileName}]), + SessionDbName = session_db_name(ProfileName), + which_sessions2(SessionDbName). + +which_sessions2(SessionDbName) -> + Sessions = which_sessions_order(ets:tab2list(SessionDbName)), + GoodSessions = [GoodSession || {good_session, GoodSession} <- Sessions], + BadSessions = [BadSession || {bad_session, BadSession} <- Sessions], + NonSessions = [NonSession || {non_session, NonSession} <- Sessions], + {lists:keysort(#session.id, GoodSessions), + lists:keysort(#session.id, BadSessions), + lists:sort(NonSessions)}. + +which_sessions_order([]) -> + []; +which_sessions_order([Session|Sessions]) when is_record(Session, session) -> + [{good_session, Session} | which_sessions_order(Sessions)]; +which_sessions_order([BadSession|Sessions]) + when is_tuple(BadSession) andalso + (element(1, BadSession) =:= session) -> + [{bad_session, BadSession} | which_sessions_order(Sessions)]; +which_sessions_order([NonSession|Sessions]) -> + [{non_session, NonSession} | which_sessions_order(Sessions)]. + + +%%-------------------------------------------------------------------- +%% Function: which session_info(ProfileName) -> list() +%% +%% Description: Produces a ets table info list of the sessions table +%%-------------------------------------------------------------------- + +which_session_info(ProfileName) -> + SessionDbName = session_db_name(ProfileName), + ?hcrt("which_session_info", [{profile, ProfileName}]), + ets:info(SessionDbName). + + +%%-------------------------------------------------------------------- %% Function: set_options(Options, ProfileName) -> ok %% %% Options = [Option] @@ -379,8 +440,7 @@ do_init(ProfileName, CookiesDir) -> %% Create handler db ?hcrt("create handler/request db", []), HandlerDbName = handler_db_name(ProfileName), - ets:new(HandlerDbName, - [protected, set, named_table, {keypos, #handler_info.id}]), + ets:new(HandlerDbName, [protected, set, named_table, {keypos, 1}]), %% Cookie DB ?hcrt("create cookie db", []), @@ -414,9 +474,10 @@ handle_call({request, Request}, _, State) -> {stop, Error, httpc_response:error(Request, Error), State} end; -handle_call({cancel_request, RequestId}, From, State) -> +handle_call({cancel_request, RequestId}, From, + #state{handler_db = HandlerDb} = State) -> ?hcri("cancel_request", [{request_id, RequestId}]), - case ets:lookup(State#state.handler_db, RequestId) of + case ets:lookup(HandlerDb, RequestId) of [] -> %% The request has allready compleated make sure %% it is deliverd to the client process queue so @@ -428,9 +489,9 @@ handle_call({cancel_request, RequestId}, From, State) -> {noreply, State}; [{_, Pid, _}] -> httpc_handler:cancel(RequestId, Pid, From), - {noreply, State#state{cancel = - [{RequestId, Pid, From} | - State#state.cancel]}} + {noreply, + State#state{cancel = + [{RequestId, Pid, From} | State#state.cancel]}} end; handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) -> @@ -457,8 +518,8 @@ handle_call({which_cookies, Url, Options}, _, handle_call({get_options, OptionItems}, _, #state{options = Options} = State) -> ?hcrv("get options", [{option_items, OptionItems}]), - Reply = [{OptionItem, get_option(OptionItem, Options)} || OptionItem <- - OptionItems], + Reply = [{OptionItem, get_option(OptionItem, Options)} || + OptionItem <- OptionItems], {reply, Reply, State}; handle_call(info, _, State) -> @@ -645,7 +706,7 @@ code_change(_, code_change(_, State, _) -> {ok, State}. -%% This function is to catch everything that calls through the cracks... +%% This function is used to catch everything that falls through the cracks... update_session_table(SessionDB, Transform) -> ets:safe_fixtable(SessionDB, true), update_session_table(SessionDB, ets:first(SessionDB), Transform), @@ -673,40 +734,56 @@ update_session_table(SessionDB, Key, Transform) -> %%-------------------------------------------------------------------- get_manager_info(#state{handler_db = HDB, - cookie_db = CDB} = _State) -> + session_db = SDB, + cookie_db = CDB, + options = Options} = _State) -> HandlerInfo = get_handler_info(HDB), + SessionInfo = which_sessions2(SDB), + OptionsInfo = + [{Item, get_option(Item, Options)} || + Item <- record_info(fields, options)], CookieInfo = httpc_cookie:which_cookies(CDB), - [{handlers, HandlerInfo}, {cookies, CookieInfo}]. + [{handlers, HandlerInfo}, + {sessions, SessionInfo}, + {options, OptionsInfo}, + {cookies, CookieInfo}]. + +sort_handlers(Unsorted) -> + sort_handlers2(lists:keysort(1, Unsorted)). + +sort_handlers2([]) -> + []; +sort_handlers2([{HandlerPid, RequestId}|L]) -> + {Handler, Rest} = sort_handlers2(HandlerPid, [RequestId], L), + [Handler | sort_handlers2(Rest)]. + +sort_handlers2(HandlerPid, Reqs, []) -> + {{HandlerPid, lists:sort(Reqs)}, []}; +sort_handlers2(HandlerPid, Reqs, [{HandlerPid, ReqId}|Rest]) -> + sort_handlers2(HandlerPid, [ReqId|Reqs], Rest); +sort_handlers2(HandlerPid1, Reqs, [{HandlerPid2, _}|_] = Rest) + when HandlerPid1 =/= HandlerPid2 -> + {{HandlerPid1, lists:sort(Reqs)}, Rest}. get_handler_info(Tab) -> - Pattern = #handler_info{handler = '$1', - state = '$2', - _ = '_'}, - Handlers1 = [{Pid, State} || [Pid, State] <- ets:match(Tab, Pattern)], - F = fun({Pid, State} = Elem, Acc) when State =/= canceled -> - case lists:keymember(Pid, 1, Acc) of - true -> - Acc; - false -> - [Elem | Acc] - end; - (_, Acc) -> - Acc - end, - Handlers2 = lists:foldl(F, [], Handlers1), - Handlers3 = [{Pid, State, - case (catch httpc_handler:info(Pid)) of - {'EXIT', _} -> + Pattern = {'$2', '$1', '_'}, + Handlers1 = [{Pid, Id} || [Pid, Id] <- ets:match(Tab, Pattern)], + Handlers2 = sort_handlers(Handlers1), + Handlers3 = [{Pid, Reqs, + try + begin + httpc_handler:info(Pid) + end + catch + _:_ -> %% Why would this crash? %% Only if the process has died, but we don't %% know about it? - []; - Else -> - Else - end} || - {Pid, State} <- Handlers2], + [] + end} || {Pid, Reqs} <- Handlers2], Handlers3. + handle_request(#request{settings = #http_options{version = "HTTP/0.9"}} = Request, State) -> @@ -758,19 +835,21 @@ handle_request(Request, State = #state{options = Options}) -> {reply, {ok, NewRequest#request.id}, State}. -start_handler(Request, State) -> +start_handler(#request{id = Id, + from = From} = Request, + #state{profile_name = ProfileName, + handler_db = HandlerDb, + options = Options}) -> {ok, Pid} = case is_inets_manager() of true -> httpc_handler_sup:start_child([whereis(httpc_handler_sup), - Request, State#state.options, - State#state.profile_name]); + Request, Options, ProfileName]); false -> - httpc_handler:start_link(self(), Request, State#state.options, - State#state.profile_name) + httpc_handler:start_link(self(), Request, Options, ProfileName) end, - ets:insert(State#state.handler_db, {Request#request.id, - Pid, Request#request.from}), + HandlerInfo = {Id, Pid, From}, + ets:insert(HandlerDb, HandlerInfo), erlang:monitor(process, Pid). @@ -827,12 +906,14 @@ select_session(Candidates, Max) -> {ok, HandlerPid} end. -pipeline_or_keep_alive(Request, HandlerPid, State) -> +pipeline_or_keep_alive(#request{id = Id, + from = From} = Request, + HandlerPid, + #state{handler_db = HandlerDb} = State) -> case (catch httpc_handler:send(Request, HandlerPid)) of ok -> - ets:insert(State#state.handler_db, {Request#request.id, - HandlerPid, - Request#request.from}); + HandlerInfo = {Id, HandlerPid, From}, + ets:insert(HandlerDb, HandlerInfo); _ -> % timeout pipelining failed start_handler(Request, State) end. diff --git a/lib/inets/src/http_lib/Makefile b/lib/inets/src/http_lib/Makefile index aaf3cfb995..3ec6ce7436 100644 --- a/lib/inets/src/http_lib/Makefile +++ b/lib/inets/src/http_lib/Makefile @@ -87,11 +87,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/http_lib - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_lib - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/http_lib" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/http_lib" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile index 60bb0d2527..67555d5f1c 100644 --- a/lib/inets/src/http_server/Makefile +++ b/lib/inets/src/http_server/Makefile @@ -123,11 +123,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/http_server - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_server - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/http_server" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/http_server" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl index 60ab326a20..a34435e0e8 100644 --- a/lib/inets/src/http_server/httpd_log.erl +++ b/lib/inets/src/http_server/httpd_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -36,8 +36,8 @@ AuthUser :: string(), Date :: string(), StatusCode :: pos_integer(), - Size :: pos_integer() | string()) -> - {Log :: atom() | pid(), Entry :: string()}. + Size :: 0 | pos_integer() | string()) -> + {Log :: atom() | pid(), Entry :: string()} | term() . access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, SizeStr) when is_list(SizeStr) -> @@ -69,7 +69,7 @@ access_entry(Log, NoLog, Info :: #mod{}, Date :: string(), Reason :: term()) -> - {Log :: atom() | pid(), Entry :: string()}. + {Log :: atom() | pid(), Entry :: string()} | term(). error_entry(Log, NoLog, #mod{config_db = ConfigDB, @@ -87,7 +87,7 @@ error_entry(Log, NoLog, ConfigDB :: term(), Date :: string(), ErrroStr :: string()) -> - {Log :: atom() | pid(), Entry :: string()}. + {Log :: atom() | pid(), Entry :: string()} | term(). error_report_entry(Log, NoLog, ConfigDb, Date, ErrorStr) -> MakeEntry = fun() -> io_lib:format("[~s], ~s~n", [Date, ErrorStr]) end, @@ -99,7 +99,7 @@ error_report_entry(Log, NoLog, ConfigDb, Date, ErrorStr) -> ConfigDB :: term(), Date :: string(), Reason :: term()) -> - {Log :: atom() | pid(), Entry :: string()}. + {Log :: atom() | pid(), Entry :: string()} | term(). security_entry(Log, NoLog, #mod{config_db = ConfigDB}, Date, Reason) -> MakeEntry = fun() -> io_lib:format("[~s] ~s~n", [Date, Reason]) end, diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile index 6da6a1d79f..b8d923edd8 100644 --- a/lib/inets/src/inets_app/Makefile +++ b/lib/inets/src/inets_app/Makefile @@ -111,13 +111,13 @@ $(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/inets_app - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/inets_app - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/inets_app" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/inets_app" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index c7029f7b31..2adb2a0fc8 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,14 +18,25 @@ {"%VSN%", [ + {"5.9", + [ + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_manager, soft, soft_purge, soft_purge, []} + ] + }, {"5.8.1", [ + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, http_uri, soft_purge, soft_purge, []}, {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, {load_module, httpc, soft_purge, soft_purge, [http_uri, httpc_manager]}, - {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, {update, inets_sup, soft, soft_purge, soft_purge, []}, @@ -35,36 +46,15 @@ {load_module, httpd_script_env, soft_purge, soft_purge, []}, {load_module, inets, soft_purge, soft_purge, [inets_trace]}, - {update, httpc_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, {update, httpd_sup, soft, soft_purge, soft_purge, []}, {add_module, inets_trace} ] }, - {"5.8", + {"5.8", [ - {load_module, http_uri, soft_purge, soft_purge, []}, - {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, - - {load_module, httpc, soft_purge, soft_purge, - [http_uri, httpc_manager]}, - - {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, - {update, inets_sup, soft, soft_purge, soft_purge, []}, - - {load_module, inets, soft_purge, soft_purge, [inets_trace]}, - - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_response, soft_purge, soft_purge, []}, - {load_module, httpd_script_env, soft_purge, soft_purge, []}, - - {load_module, ftp, soft_purge, soft_purge, []}, - {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, [http_uri, httpc_handler]}, - {update, httpd_sup, soft, soft_purge, soft_purge, []}, - - {add_module, inets_trace} + {restart_application, inets} ] }, {"5.7.2", @@ -74,14 +64,25 @@ } ], [ + {"5.9", + [ + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_manager, soft, soft_purge, soft_purge, []} + ] + }, {"5.8.1", [ + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, http_uri, soft_purge, soft_purge, []}, {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, {load_module, httpc, soft_purge, soft_purge, [http_uri, httpc_manager]}, - {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, {update, inets_sup, soft, soft_purge, soft_purge, []}, @@ -91,36 +92,15 @@ {load_module, httpd_script_env, soft_purge, soft_purge, []}, {load_module, inets, soft_purge, soft_purge, []}, - {update, httpc_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, {update, httpd_sup, soft, soft_purge, soft_purge, []}, {remove, {inets_trace, soft_purge, brutal_purge}} ] }, {"5.8", [ - {load_module, http_uri, soft_purge, soft_purge, []}, - {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, - - {load_module, httpc, soft_purge, soft_purge, - [http_uri, httpc_manager]}, - - {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, - {update, inets_sup, soft, soft_purge, soft_purge, []}, - - {load_module, inets, soft_purge, soft_purge, []}, - - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_response, soft_purge, soft_purge, []}, - {load_module, httpd_script_env, soft_purge, soft_purge, []}, - - {load_module, ftp, soft_purge, soft_purge, []}, - {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, [http_uri, httpc_handler]}, - {update, httpd_sup, soft, soft_purge, soft_purge, []}, - - {remove, {inets_trace, soft_purge, brutal_purge}} + {restart_application, inets} ] }, {"5.7.2", diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk index d24cc0aea3..adef32dc19 100644 --- a/lib/inets/src/inets_app/inets.mk +++ b/lib/inets/src/inets_app/inets.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2011. All Rights Reserved. +# Copyright Ericsson AB 2010-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 @@ -33,10 +33,6 @@ ifeq ($(WARN_UNUSED_WARS), true) ERL_COMPILE_FLAGS += +warn_unused_vars endif -ifeq ($(shell erl -noshell -eval 'io:format("~4s", [erlang:system_info(otp_release)])' -s init stop), R14B) -INETS_ERL_COMPILE_FLAGS += -D'OTP-R14B-COMPILER' -endif - INETS_APP_VSN_COMPILE_FLAGS = \ +'{parse_transform,sys_pre_attributes}' \ +'{attribute,insert,app_vsn,$(APP_VSN)}' diff --git a/lib/inets/src/inets_app/inets_service.erl b/lib/inets/src/inets_app/inets_service.erl index a057a51e2c..d17fdfe13e 100644 --- a/lib/inets/src/inets_app/inets_service.erl +++ b/lib/inets/src/inets_app/inets_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -20,20 +20,6 @@ -module(inets_service). --ifdef('OTP-R14B-COMPILER'). - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{start_standalone, 1}, - {start_service, 1}, - {stop_service, 1}, - {services, 0}, - {service_info, 1}]; -behaviour_info(_) -> - undefined. - --else. %% Starts service stand-alone %% start_standalone(Config) -> % {ok, Pid} | {error, Reason} @@ -83,4 +69,3 @@ behaviour_info(_) -> -callback service_info(Service :: term()) -> {ok, [{Property :: term(), Value :: term()}]} | {error, Reason :: term()}. --endif. diff --git a/lib/inets/src/tftp/Makefile b/lib/inets/src/tftp/Makefile index 759b70c8e4..a9df8d0319 100644 --- a/lib/inets/src/tftp/Makefile +++ b/lib/inets/src/tftp/Makefile @@ -39,8 +39,10 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- +BEHAVIOUR_MODULES= \ + tftp + MODULES = \ - tftp \ tftp_binary \ tftp_engine \ tftp_file \ @@ -50,10 +52,13 @@ MODULES = \ HRL_FILES = tftp.hrl -ERL_FILES = $(MODULES:%=%.erl) +ERL_FILES= \ + $(MODULES:%=%.erl) \ + $(BEHAVIOUR_MODULES:%=%.erl) TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR)) # ---------------------------------------------------- # FLAGS @@ -72,10 +77,12 @@ ERL_COMPILE_FLAGS += \ # Targets # ---------------------------------------------------- +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) + debug opt: $(TARGET_FILES) clean: - rm -f $(TARGET_FILES) + rm -f $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) rm -f core docs: @@ -86,11 +93,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/tftp - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/tftp - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/tftp" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/tftp" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/tftp/tftp.erl b/lib/inets/src/tftp/tftp.erl index 0d7ae1a89e..1621add246 100644 --- a/lib/inets/src/tftp/tftp.erl +++ b/lib/inets/src/tftp/tftp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -224,20 +224,6 @@ service_info/1 ]). --ifdef('OTP-R14B-COMPILER'). - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{prepare, 6}, - {open, 6}, - {read, 1}, - {write, 2}, - {abort, 3}]; -behaviour_info(_) -> - undefined. - --else. -type peer() :: {PeerType :: inet | inet6, PeerHost :: inet:ip_address(), @@ -280,8 +266,6 @@ behaviour_info(_) -> -callback abort(Code :: error_code(), string(), State :: term()) -> 'ok'. --endif. - -include("tftp.hrl"). diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index 87ca60e4b3..7befa82272 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -234,7 +234,7 @@ endif # Release directory specification # ---------------------------------------------------- -RELTESTSYSDIR = $(RELEASE_PATH)/inets_test +RELTESTSYSDIR = "$(RELEASE_PATH)/inets_test" RELTESTSYSALLDATADIR = $(RELTESTSYSDIR)/all_SUITE_data RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin @@ -284,9 +284,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/test - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/test - $(INSTALL_DATA) $(INETS_FILES) $(RELSYSDIR)/test + $(INSTALL_DIR) "$(RELSYSDIR)/test" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/test" + $(INSTALL_DATA) $(INETS_FILES) "$(RELSYSDIR)/test" @for d in $(DATADIRS); do \ echo "installing data dir $$d"; \ if test -f $$d/TAR.exclude; then \ @@ -299,9 +299,9 @@ release_spec: opt find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \ find $$d -name 'core' >> $$d/TAR.exclude2; \ find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \ - tar cfX - $$d/TAR.exclude2 $$d | (cd $(RELSYSDIR)/test; tar xf -); \ + tar cfX - $$d/TAR.exclude2 $$d | (cd "$(RELSYSDIR)/test"; tar xf -); \ else \ - tar cf - $$d | (cd $(RELSYSDIR)/test; tar xf -); \ + tar cf - $$d | (cd "$(RELSYSDIR)/test"; tar xf -); \ fi; \ done @@ -330,8 +330,8 @@ info: @echo "INETS_SPECS = $(INETS_SPECS)" @echo "INETS_FILES = $(INETS_FILES)" @echo "" - @echo "RELEASE_PATH = $(RELEASE_PATH)" - @echo "RELSYSDIR = $(RELSYSDIR)" + @echo "RELEASE_PATH = "$(RELEASE_PATH)"" + @echo "RELSYSDIR = "$(RELSYSDIR)"" @echo "RELTESTSYSDIR = $(RELTESTSYSDIR)" @echo "RELTESTSYSALLDATADIR = $(RELTESTSYSALLDATADIR)" @echo "RELTESTSYSBINDIR = $(RELTESTSYSBINDIR)" diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index a116edef77..1cdd96f0b0 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -761,116 +761,158 @@ http_inets_pipe(Config) when is_list(Config) -> test_pipeline(URL) -> - p("test_pipeline -> entry with" - "~n URL: ~p", [URL]), - - httpc:set_options([{pipeline_timeout, 50000}]), - - p("test_pipeline -> issue (async) request 1"), - {ok, RequestId1} = + p("test_pipeline -> entry with" + "~n URL: ~p", [URL]), + + httpc:set_options([{pipeline_timeout, 50000}]), + + p("test_pipeline -> issue (async) request 1" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdA1} = httpc:request(get, {URL, []}, [], [{sync, false}]), - test_server:format("RequestId1: ~p~n", [RequestId1]), - p("test_pipeline -> RequestId1: ~p", [RequestId1]), - - %% Make sure pipeline is initiated - p("test_pipeline -> sleep some", []), - test_server:sleep(4000), - - p("test_pipeline -> issue (async) request 2"), - {ok, RequestId2} = + tsp("RequestIdA1: ~p", [RequestIdA1]), + p("test_pipeline -> RequestIdA1: ~p" + "~n when profile info: ~p", [RequestIdA1, httpc:info()]), + + %% Make sure pipeline is initiated + p("test_pipeline -> sleep some", []), + test_server:sleep(4000), + + p("test_pipeline -> issue (async) request A2, A3 and A4" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdA2} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId2: ~p", [RequestId2]), - p("test_pipeline -> RequestId2: ~p", [RequestId2]), - - p("test_pipeline -> issue (sync) request 3"), - {ok, {{_,200,_}, [_ | _], [_ | _]}} = + {ok, RequestIdA3} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdA4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestIdAs => A2: ~p, A3: ~p and A4: ~p", + [RequestIdA2, RequestIdA3, RequestIdA4]), + p("test_pipeline -> RequestIds => A2: ~p, A3: ~p and A4: ~p" + "~n when profile info: ~p", + [RequestIdA2, RequestIdA3, RequestIdA4, httpc:info()]), + + p("test_pipeline -> issue (sync) request 3"), + {ok, {{_,200,_}, [_ | _], [_ | _]}} = httpc:request(get, {URL, []}, [], []), + + p("test_pipeline -> expect reply for (async) request A1, A2, A3 and A4" + "~n when profile info: ~p", [httpc:info()]), + pipeline_await_async_reply([{RequestIdA1, a1, 200}, + {RequestIdA2, a2, 200}, + {RequestIdA3, a3, 200}, + {RequestIdA4, a4, 200}], ?MINS(1)), - p("test_pipeline -> expect reply for (async) request 1 or 2"), - receive - {http, {RequestId1, {{_, 200, _}, _, _}}} -> - p("test_pipeline -> received reply for (async) request 1 - now wait for 2"), - receive - {http, {RequestId2, {{_, 200, _}, _, _}}} -> - p("test_pipeline -> received reply for (async) request 2"), - ok; - {http, Msg1} -> - tsf(Msg1) - end; - {http, {RequestId2, {{_, 200, _}, _, _}}} -> - io:format("test_pipeline -> received reply for (async) request 2 - now wait for 1"), - receive - {http, {RequestId1, {{_, 200, _}, _, _}}} -> - io:format("test_pipeline -> received reply for (async) request 1"), - ok; - {http, Msg2} -> - tsf(Msg2) - end; - {http, Msg3} -> - tsf(Msg3) - after 60000 -> - receive Any1 -> - tsp("received crap after timeout: ~n ~p", [Any1]), - tsf({error, {timeout, Any1}}) - end - end, - - p("test_pipeline -> sleep some"), - test_server:sleep(4000), - - p("test_pipeline -> issue (async) request 4"), - {ok, RequestId3} = + p("test_pipeline -> sleep some" + "~n when profile info: ~p", [httpc:info()]), + test_server:sleep(4000), + + p("test_pipeline -> issue (async) request B1, B2, B3 and B4" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdB1} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId3: ~p", [RequestId3]), - p("test_pipeline -> RequestId3: ~p", [RequestId3]), - - p("test_pipeline -> issue (async) request 5"), - {ok, RequestId4} = + {ok, RequestIdB2} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId4: ~p~n", [RequestId4]), - p("test_pipeline -> RequestId4: ~p", [RequestId4]), - - p("test_pipeline -> cancel (async) request 4"), - ok = httpc:cancel_request(RequestId3), - - p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), - receive - {http, {RequestId3, _}} -> - tsf(http_cancel_request_failed) - after 3000 -> - ok - end, - - p("test_pipeline -> expect reply for (async) request 4"), - Body = - receive - {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> - p("test_pipeline -> received reply for (async) request 5"), - tsp("Receive : ~p", [Res]), - BinBody4; - {http, Msg4} -> - tsf(Msg4) - after 60000 -> - receive Any2 -> - tsp("received crap after timeout: ~n ~p", [Any2]), - tsf({error, {timeout, Any2}}) - end - end, + {ok, RequestIdB3} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdB4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p", + [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4]), + p("test_pipeline -> RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p" + "~n when profile info: ~p", + [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4, httpc:info()]), + + p("test_pipeline -> cancel (async) request B2" + "~n when profile info: ~p", [httpc:info()]), + ok = httpc:cancel_request(RequestIdB2), + + p("test_pipeline -> " + "expect *no* reply for cancelled (async) request B2 (for 3 secs)" + "~n when profile info: ~p", [httpc:info()]), + receive + {http, {RequestIdB2, _}} -> + tsf(http_cancel_request_failed) + after 3000 -> + ok + end, + + p("test_pipeline -> expect reply for (async) request B1, B3 and B4" + "~n when profile info: ~p", [httpc:info()]), + Bodies = pipeline_await_async_reply([{RequestIdB1, b1, 200}, + {RequestIdB3, b3, 200}, + {RequestIdB4, b4, 200}], ?MINS(1)), + [{b1, Body}|_] = Bodies, - p("test_pipeline -> check reply for (async) request 5"), + p("test_pipeline -> check reply for (async) request B1" + "~n when profile info: ~p", [httpc:info()]), inets_test_lib:check_body(binary_to_list(Body)), - - p("test_pipeline -> ensure no unexpected incomming"), + + p("test_pipeline -> ensure no unexpected incomming" + "~n when profile info: ~p", [httpc:info()]), receive {http, Any} -> tsf({unexpected_message, Any}) after 500 -> ok end, - - p("test_pipeline -> done"), + + p("test_pipeline -> done" + "~n when profile info: ~p", [httpc:info()]), ok. +pipeline_await_async_reply(ReqIds, Timeout) -> + pipeline_await_async_reply(ReqIds, Timeout, []). + +pipeline_await_async_reply([], _, Acc) -> + lists:keysort(1, Acc); +pipeline_await_async_reply(ReqIds, Timeout, Acc) when Timeout > 0 -> + T1 = inets_test_lib:timestamp(), + p("pipeline_await_async_reply -> await replies" + "~n ReqIds: ~p" + "~n Timeout: ~p", [ReqIds, Timeout]), + receive + {http, {RequestId, {{_, Status, _}, _, Body}}} -> + p("pipeline_await_async_reply -> received reply for" + "~n RequestId: ~p" + "~n Status: ~p", [RequestId, Status]), + case lists:keysearch(RequestId, 1, ReqIds) of + {value, {RequestId, N, Status}} -> + p("pipeline_await_async_reply -> " + "found expected request ~w", [N]), + ReqIds2 = lists:keydelete(RequestId, 1, ReqIds), + NewTimeout = Timeout - (inets_test_lib:timestamp()-T1), + pipeline_await_async_reply(ReqIds2, NewTimeout, + [{N, Body} | Acc]); + {value, {RequestId, N, WrongStatus}} -> + p("pipeline_await_async_reply -> " + "found request ~w with wrong status", [N]), + tsf({reply_with_unexpected_status, + {RequestId, N, WrongStatus}}); + false -> + tsf({unexpected_reply, {RequestId, Status}}) + end; + {http, Msg} -> + tsf({unexpected_reply, Msg}) + after Timeout -> + receive + Any -> + tsp("pipeline_await_async_reply -> " + "received unknown data after timeout: " + "~n ~p", [Any]), + tsf({timeout, {unknown, Any}}) + end + end; +pipeline_await_async_reply(ReqIds, _, Acc) -> + tsp("pipeline_await_async_reply -> " + "timeout: " + "~n ~p" + "~nwhen" + "~n ~p", [ReqIds, Acc]), + tsf({timeout, ReqIds, Acc}). + + + %%------------------------------------------------------------------------- http_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index cb1214b7fb..387263ce58 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -82,19 +82,23 @@ actions(Type, Port, Host, Node) -> [{statuscode, 200}, {version, "HTTP/1.0"}]). + %%------------------------------------------------------------------------- security(ServerRoot, Type, Port, Host, Node) -> - %% io:format(user, "~w:security -> entry with" - %% "~n ServerRoot: ~p" - %% "~n Type: ~p" - %% "~n Port: ~p" - %% "~n Host: ~p" - %% "~n Node: ~p" - %% "~n", [?MODULE, ServerRoot, Type, Port, Host, Node]), - -%% io:format(user, "~w:security -> register~n", [?MODULE]), + tsp("security -> " + "entry with" + "~n ServerRoot: ~p" + "~n Type: ~p" + "~n Port: ~p" + "~n Host: ~p" + "~n Node: ~p", [ServerRoot, Type, Port, Host, Node]), + + tsp("security -> " + "register - receive security events"), global:register_name(mod_security_test, self()), % Receive events + tsp("security -> " + "sleep"), test_server:sleep(5000), OpenDir = filename:join([ServerRoot, "htdocs", "open"]), @@ -102,133 +106,240 @@ security(ServerRoot, Type, Port, Host, Node) -> %% Test blocking / unblocking of users. %% /open, require user one Aladdin -%% io:format(user, "~w:security -> remove user~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "remove all existing users"), remove_users(Node, ServerRoot, Host, Port, "open"), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for nonex user 'one' - expect 401"), auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "onePassword"}]}, Node, Port), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for nonex user 'two' - expect 401"), auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "two"}, {password, "twoPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for nonex user 'Alladin' - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "Aladdin", "AladdinPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "Aladdin"}, {password, "AladdinPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> add users~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "add user 'one'"), add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []), + + tsp("security -> " + "blocking and unblocking of users - " + "add user 'two'"), add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request 1 for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "WrongPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request 2 for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "WrongPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> await block security event~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "await block security event (two failed attempts)"), receive_security_event({event, user_block, Port, OpenDir, [{user, "one"}]}, Node, Port), -%% io:format(user, "~w:security -> unregister~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "unregister - no more security events"), global:unregister_name(mod_security_test), % No more events. -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for user 'one' with correct password - expect 403"), auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", [{statuscode, 403}]), %% User "one" should be blocked now.. - %% [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "list blocked users - 'one' should be the only one"), case list_blocked_users(Node, Port) of [{"one",_, Port, OpenDir,_}] -> ok; Blocked -> - %% io:format(user, "~w:security -> Blocked: ~p" - %% "~n", [?MODULE, Blocked]), + tsp(" *** unexpected blocked users ***" + "~n Blocked: ~p", [Blocked]), exit({unexpected_blocked, Blocked}) end, - -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), - [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port,OpenDir), -%% io:format(user, "~w:security -> unblock user~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "list users blocked for dir '~p' - " + "user 'one' should be the only one", [OpenDir]), + [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir), + + tsp("security -> " + "blocking and unblocking of users - " + "unblock user 'one' for dir '~p'", [OpenDir]), true = unblock_user(Node, "one", Port, OpenDir), - %% User "one" should not be blocked any more.. -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + %% User "one" should not be blocked any more. + + tsp("security -> " + "blocking and unblocking of users - " + "ensure user 'one' is no longer blocked"), [] = list_blocked_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), - [] = list_blocked_users(Node, Port, OpenDir), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + + tsp("security -> " + "blocking and unblocking of users - " + "auth request for user 'one' with correct password - expect 200"), auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", [{statuscode, 200}]), + + %% Test list_auth_users & auth_timeout -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), - ["one"] = list_auth_users(Node, Port, OpenDir), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "auth request for user 'two' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users for dir '~p' - expect user 'one'", [OpenDir]), ["one"] = list_auth_users(Node, Port, OpenDir), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "auth request for user 'two' with correct password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users for dir '~p' - expect user 'one'", [OpenDir]), ["one"] = list_auth_users(Node, Port, OpenDir), + %% Wait for successful auth to timeout. + tsp("security -> " + "list-auth-users and auth-timeout - " + "wait for successful auth to timeout"), test_server:sleep(?AUTH_TIMEOUT*1001), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect none"), [] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users for dir '~p'~n - expect none", [OpenDir]), [] = list_auth_users(Node, Port, OpenDir), + %% "two" is blocked. -%% io:format(user, "~w:security -> unblock user~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "unblock user 'two' for dir '~p'", [OpenDir]), true = unblock_user(Node, "two", Port, OpenDir), + + %% Test explicit blocking. Block user 'two'. -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "explicit blocking - list blocked users - should be none"), [] = list_blocked_users(Node,Port,OpenDir), -%% io:format(user, "~w:security -> block user~n", [?MODULE]), + + tsp("security -> " + "explicit blocking - " + "block user 'two' for dir '~p'", [OpenDir]), true = block_user(Node, "two", Port, OpenDir, 10), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + tsp("security -> " + "explicit blocking - " + "auth request for user 'two' with correct password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", - [{statuscode, 401}]). + [{statuscode, 401}]), + tsp("security -> " + "done"). + %%------------------------------------------------------------------------- auth(Type, Port, Host, Node) -> + tsp("auth -> " + "entry with" + "~n Type: ~p" + "~n Port: ~p" + "~n Host: ~p" + "~n Node: ~p", [Type, Port, Host, Node]), + %% Authentication required! ok = httpd_test_lib:verify_request(Type,Host,Port,Node, "GET /open/ HTTP/1.0\r\n\r\n", @@ -913,13 +1024,11 @@ list_users(Node, Root, _Host, Port, Dir) -> receive_security_event(Event, Node, Port) -> - %% io:format(user, "~w:receive_security_event -> entry with" - %% "~n Event: ~p" - %% "~n Node: ~p" - %% "~n Port: ~p" - %% "~n", [?MODULE, Event, Node, Port]), + tsp("receive_security_event -> await ~w event", [element(2, Event)]), receive Event -> + tsp("receive_security_event -> " + "received expected ~w event", [element(2, Event)]), ok; {'EXIT', _, _} -> receive_security_event(Event, Node, Port) @@ -1027,8 +1136,14 @@ check_lists_members1(L1,L2) -> {error,{lists_not_equal,L1,L2}}. -%% tsp(F) -> -%% inets_test_lib:tsp(F). +%% p(F) -> +%% p(F, []). + +%% p(F, A) -> +%% io:format(user, "~w:" ++ F ++ "~n", [?MODULE|A]). + +tsp(F) -> + inets_test_lib:tsp(F). tsp(F, A) -> inets_test_lib:tsp(F, A). diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index c94be796cd..0f8671b682 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -31,6 +31,7 @@ send/3, close/2]). -export([copy_file/3, copy_files/2, copy_dirs/2, del_dirs/1]). -export([info/4, log/4, debug/4, print/4]). +-export([timestamp/0, formated_timestamp/0]). -export([tsp/1, tsp/2, tsf/1, tss/1]). -export([check_body/1]). -export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). @@ -530,34 +531,27 @@ connect(ip_comm, Host, Port, Opts, Type) -> "~n Opts: ~p" "~n Type: ~p", [Host, Port, Opts, Type]), - case gen_tcp:connect(Host, Port, Opts) of + case gen_tcp:connect(Host, Port, Opts, timer:seconds(10)) of {ok, Socket} -> tsp("connect success"), {ok, Socket}; - {error, nxdomain} when Type =:= inet6 -> - tsp("connect error nxdomain when" - "~n Opts: ~p", [Opts]), - connect(ip_comm, Host, Port, Opts -- [inet6], inet); - {error, eafnosupport} when Type =:= inet6 -> - tsp("connect error eafnosupport when" - "~n Opts: ~p", [Opts]), - connect(ip_comm, Host, Port, Opts -- [inet6], inet); - {error, econnreset} when Type =:= inet6 -> - tsp("connect error econnreset when" - "~n Opts: ~p", [Opts]), - connect(ip_comm, Host, Port, Opts -- [inet6], inet); - {error, enetunreach} when Type =:= inet6 -> - tsp("connect error eafnosupport when" - "~n Opts: ~p", [Opts]), - connect(ip_comm, Host, Port, Opts -- [inet6], inet); - {error, econnrefused} when Type =:= inet6 -> - tsp("connect error econnrefused when" - "~n Opts: ~p", [Opts]), + {error, Reason} when ((Type =:= inet6) andalso + ((Reason =:= timeout) orelse + (Reason =:= nxdomain) orelse + (Reason =:= eafnosupport) orelse + (Reason =:= econnreset) orelse + (Reason =:= enetunreach) orelse + (Reason =:= econnrefused) orelse + (Reason =:= ehostunreach))) -> + tsp("connect(ip_comm) -> Connect error: " + "~n Reason: ~p" + "~n Type: ~p" + "~n Opts: ~p", [Reason, Type, Opts]), connect(ip_comm, Host, Port, Opts -- [inet6], inet); Error -> - tsp("connect(ip_conn) -> Fatal connect error: " + tsp("connect(ip_comm) -> Fatal connect error: " "~n Error: ~p" "~nwhen" "~n Host: ~p" @@ -642,6 +636,9 @@ tsf(Reason) -> tss(Time) -> test_server:sleep(Time). +timestamp() -> + http_util:timestamp(). + formated_timestamp() -> format_timestamp( os:timestamp() ). diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 488947c3a1..949eceea7f 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.9 +INETS_VSN = 5.9.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/inviso/doc/src/Makefile b/lib/inviso/doc/src/Makefile index 1b184ed78b..f00b10f29c 100644 --- a/lib/inviso/doc/src/Makefile +++ b/lib/inviso/doc/src/Makefile @@ -106,14 +106,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/inviso/src/Makefile b/lib/inviso/src/Makefile index 1f2f8b1aff..292a2bec99 100644 --- a/lib/inviso/src/Makefile +++ b/lib/inviso/src/Makefile @@ -87,12 +87,12 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src -# $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" +# $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inviso/test/Makefile b/lib/inviso/test/Makefile index c1df29d631..2650faa392 100644 --- a/lib/inviso/test/Makefile +++ b/lib/inviso/test/Makefile @@ -51,10 +51,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) inviso.spec inviso.cover $(ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) inviso.spec inviso.cover $(ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile index acd5307dee..858c97f187 100644 --- a/lib/jinterface/doc/src/Makefile +++ b/lib/jinterface/doc/src/Makefile @@ -160,17 +160,17 @@ debug opt: 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/html/java/$(JAVA_PKG_PATH) - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - (/bin/cp -rf ../html $(RELSYSDIR)/doc) + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html/java/$(JAVA_PKG_PATH)" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + (/bin/cp -rf ../html "$(RELSYSDIR)/doc") # $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \ -# $(RELSYSDIR)/doc/html -# $(INSTALL_DATA) $(JAVA_EXTRA_FILES) $(RELSYSDIR)/doc/html/java -# $(INSTALL_DATA) $(TOP_HTML_FILES) $(RELSYSDIR)/doc +# "$(RELSYSDIR)/doc/html" +# $(INSTALL_DATA) $(JAVA_EXTRA_FILES) "$(RELSYSDIR)/doc/html/java" +# $(INSTALL_DATA) $(TOP_HTML_FILES) "$(RELSYSDIR)/doc" release_spec: diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile index 365798e68a..ec817aa39b 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile @@ -66,7 +66,7 @@ JARFLAGS=-cvf JAVA_OPTIONS = ifeq ($(TESTROOT),) -RELEASE_PATH=$(ERL_TOP)/release/$(TARGET) +RELEASE_PATH="$(ERL_TOP)/release/$(TARGET)" else RELEASE_PATH=$(TESTROOT) endif @@ -96,13 +96,13 @@ docs: # include $(ERL_TOP)/make/otp_release_targets.mk release release_docs release_tests release_html: - $(MAKE) $(MFLAGS) RELEASE_PATH=$(RELEASE_PATH) $(TARGET_MAKEFILE) $@_spec + $(MAKE) $(MFLAGS) RELEASE_PATH="$(RELEASE_PATH)" $(TARGET_MAKEFILE) $@_spec release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/java_src/com/ericsson/otp/erlang - $(INSTALL_DATA) $(JAVA_SRC) $(RELSYSDIR)/java_src/com/ericsson/otp/erlang - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/java_src/com/ericsson/otp/erlang" + $(INSTALL_DATA) $(JAVA_SRC) "$(RELSYSDIR)/java_src/com/ericsson/otp/erlang" + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/jinterface/test/Makefile b/lib/jinterface/test/Makefile index a85d0e7411..b70521c08d 100644 --- a/lib/jinterface/test/Makefile +++ b/lib/jinterface/test/Makefile @@ -80,6 +80,6 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(TEST_SPEC_FILE) $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(TEST_SPEC_FILE) $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index 214e994889..78e5f7bc26 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -155,18 +155,18 @@ $(SPECDIR)/specs_zlib_stub.xml: 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 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man4 - $(INSTALL_DATA) $(MAN4_FILES) $(RELEASE_PATH)/man/man4 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4" + $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index ee687511a3..214e61cc00 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -91,7 +91,7 @@ of the additional library directories will override modules with the same name in OTP, except for modules in Kernel and STDLIB.</p> - <p>The environment variable <c>ERL_LIBS</c> (if defined) shold contain + <p>The environment variable <c>ERL_LIBS</c> (if defined) should contain a colon-separated (for Unix-like systems) or semicolon-separated (for Windows) list of additional libraries.</p> <p>Example: On an Unix-like system, <c>ERL_LIBS</c> could be set to diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index 579b7f1f74..e327a4f907 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -123,7 +123,7 @@ <p>Completely closes the socket and all associations on it. The unsent data is flushed as in <c>eof/2</c>. The <c>close/1</c> call is blocking or otherwise depending of the value of - the <seealso marker="#option-linger">linger</seealso> socket + the <seealso marker="inet#option-linger">linger</seealso> socket <seealso marker="#options">option</seealso>. If <c>close</c> does not linger or linger timeout expires, the call returns and the data is flushed in the background.</p> @@ -309,8 +309,8 @@ <seealso marker="#option-active">passive</seealso> mode, with <anno>SockType</anno> <c>seqpacket</c>, and with reasonably large - <seealso marker="#option-sndbuf">kernel</seealso> and driver - <seealso marker="#option-buffer">buffers.</seealso></p> + <seealso marker="inet#option-sndbuf">kernel</seealso> and driver + <seealso marker="inet#option-buffer">buffers.</seealso></p> </desc> </func> <func> @@ -530,19 +530,8 @@ SCTP data interleaved with other inter-process messages.</p> </item> </list> - <marker id="option-buffer"></marker> </item> - <tag><c>{buffer, integer()}</c></tag> - <item> - <p>Determines the size of the user-level software buffer used by - the SCTP driver. Not to be confused with <c>sndbuf</c> - and <c>recbuf</c> options which correspond to - the kernel socket buffers. It is recommended - to have <c>val(buffer) >= max(val(sndbuf),val(recbuf))</c>. - In fact, the <c>val(buffer)</c> is automatically set to - the above maximum when <c>sndbuf</c> or <c>recbuf</c> values are set.</p> - </item> - <tag><c>{tos, integer()}</c></tag> + <tag><c>{tos, integer()}</c></tag> <item> <p>Sets the Type-Of-Service field on the IP datagrams being sent, to the given value, which effectively determines a prioritization @@ -567,19 +556,8 @@ <c>{IP,Port}</c> of the socket can be re-used immediately: no waiting in the CLOSE_WAIT state is performed (may be required for high-throughput servers).</p> - <marker id="option-linger"></marker> - </item> - <tag><c>{linger, {true|false, integer()}</c></tag> - <item> - <p>Determines the timeout in seconds for flushing unsent data in the - <c>gen_sctp:close/1</c> socket call. If the 1st component of the value - tuple is <c>false</c>, the 2nd one is ignored, which means that - <c>gen_sctp:close/1</c> returns immediately not waiting - for data to be flushed. Otherwise, the 2nd component is - the flushing time-out in seconds.</p> - <marker id="option-sndbuf"></marker> </item> - <tag><c>{sndbuf, integer()}</c></tag> + <tag><c>{sndbuf, integer()}</c></tag> <item> <p>The size, in bytes, of the *kernel* send buffer for this socket. Sending errors would occur for datagrams larger than @@ -593,6 +571,15 @@ <c>val(sndbuf)</c>. Setting this option also adjusts the size of the driver buffer (see <c>buffer</c> above).</p> </item> + + <tag><c>{sctp_module, module()}</c></tag> + <item> <p> + Override which callback module is used. Defaults to + <c>inet_sctp</c> for IPv4 and <c>inet6_sctp</c> for IPv6. + </p> + </item> + + <tag><c>{sctp_rtoinfo, #sctp_rtoinfo{}}</c></tag> <item> <pre> #sctp_rtoinfo{ diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index cf97607af1..11a0843c10 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -96,37 +96,47 @@ do_recv(Sock, Bs) -> can be either a hostname, or an IP address.</p> <p>The available options are:</p> <taglist> - <tag><c>list</c></tag> - <item> - <p>Received <c>Packet</c> is delivered as a list.</p> - </item> - <tag><c>binary</c></tag> - <item> - <p>Received <c>Packet</c> is delivered as a binary.</p> - </item> - <tag><c>{ip, ip_address()}</c></tag> + <tag><c>{ip, ip_address()}</c></tag> <item> <p>If the host has several network interfaces, this option specifies which one to use.</p> </item> - <tag><c>{port, Port}</c></tag> + + <tag><c>{ifaddr, ip_address()}</c></tag> <item> - <p>Specify which local port number to use.</p> - </item> + <p>Same as <c>{ip, ip_address()}</c>. If the host has several network interfaces, this option + specifies which one to use.</p> + </item> + <tag><c>{fd, integer() >= 0}</c></tag> <item> <p>If a socket has somehow been connected without using <c>gen_tcp</c>, use this option to pass the file descriptor for it.</p> </item> - <tag><c>inet6</c></tag> + + <tag><c>inet</c></tag> <item> + <p>Set up the socket for IPv4.</p> + </item> + + <tag><c>inet6</c></tag> + <item> <p>Set up the socket for IPv6.</p> </item> - <tag><c>inet</c></tag> + + <tag><c>{port, Port}</c></tag> <item> - <p>Set up the socket for IPv4.</p> + <p>Specify which local port number to use.</p> </item> + + <tag><c>{tcp_module, module()}</c></tag> + <item> <p> + Override which callback module is used. Defaults to + <c>inet_tcp</c> for IPv4 and <c>inet6_tcp</c> for IPv6. + </p> + </item> + <tag><c>Opt</c></tag> <item> <p>See @@ -197,6 +207,13 @@ do_recv(Sock, Bs) -> <c>gen_tcp</c>, use this option to pass the file descriptor for it.</p> </item> + + <tag><c>{ifaddr, ip_address()}</c></tag> + <item> + <p>Same as <c>{ip, ip_address()}</c>. If the host has several network interfaces, this option + specifies which one to use.</p> + </item> + <tag><c>inet6</c></tag> <item> <p>Set up the socket for IPv6.</p> @@ -205,6 +222,14 @@ do_recv(Sock, Bs) -> <item> <p>Set up the socket for IPv4.</p> </item> + + <tag><c>{tcp_module, module()}</c></tag> + <item> <p> + Override which callback module is used. Defaults to + <c>inet_tcp</c> for IPv4 and <c>inet6_tcp</c> for IPv6. + </p> + </item> + <tag><c>Opt</c></tag> <item> <p>See @@ -299,7 +324,7 @@ do_recv(Sock, Bs) -> <c><anno>Socket</anno></c>. The controlling process is the process which receives messages from the socket. If called by any other process than the current controlling process, - <c>{error, eperm}</c> is returned.</p> + <c>{error, not_owner}</c> is returned.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index daa9b7d887..726dc30546 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -72,6 +72,14 @@ <p>If the host has several network interfaces, this option specifies which one to use.</p> </item> + + <tag><c>{ifaddr, ip_address()}</c></tag> + <item> + <p>Same as <c>{ip, ip_address()}</c>. If the host has several network interfaces, this option + specifies which one to use.</p> + </item> + + <tag><c>{fd, integer() >= 0}</c></tag> <item> <p>If a socket has somehow been opened without using @@ -86,6 +94,51 @@ <item> <p>Set up the socket for IPv4.</p> </item> + + <tag><c>{udp_module, module()}</c></tag> + <item> <p> + Override which callback module is used. Defaults to + <c>inet_udp</c> for IPv4 and <c>inet6_udp</c> for IPv6. + </p> + </item> + + <tag><c>{multicast_if, Address}</c></tag> + <item> + <p>Set the local device for a multicast socket.</p> + </item> + + <tag><c>{multicast_loop, true | false}</c></tag> + <item> + <p> + When <c>true</c> sent multicast packets will be looped back to the local + sockets. + </p> + </item> + + <tag><c>{multicast_ttl, Integer}</c></tag> + <item> + <p> + The <c>multicast_ttl</c> option changes the time-to-live (TTL) for + outgoing multicast datagrams in order to control the scope of the + multicasts. + </p> + <p> + Datagrams with a TTL of 1 are not forwarded beyond the local + network. + <br />Default: 1 + </p> + </item> + + <tag><c>{add_membership, {MultiAddress, InterfaceAddress}}</c></tag> + <item> + <p>Join a multicast group. </p> + </item> + + <tag><c>{drop_membership, {MultiAddress, InterfaceAddress}}</c></tag> + <item> + <p>Leave multicast group.</p> + </item> + <tag><c>Opt</c></tag> <item> <p>See @@ -136,7 +189,9 @@ <desc> <p>Assigns a new controlling process <c><anno>Pid</anno></c> to <c><anno>Socket</anno></c>. The controlling process is the process which - receives messages from the socket.</p> + receives messages from the socket. If called by any other + process than the current controlling process, + <c>{error, not_owner}</c> is returned.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index bf6c4cfb1a..b727960d96 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -445,10 +445,34 @@ fe80::204:acff:fe17:bf38 flow control; the other side will not be able send faster than the receiver can read.</p> </item> + + <tag><c>{bit8, clear | set | on | off}</c></tag> + <item> + <p> + Scans every byte in received data-packets and checks if the 8 bit + is set in any of them. Information is retrieved with + <c>inet:getopts/2</c>. + </p> + <p>Note that the <c>bit8</c> option is deprecated and will be removed in Erlang/OTP R16.</p> + </item> + <tag><c>{broadcast, Boolean}</c>(UDP sockets)</tag> <item> <p>Enable/disable permission to send broadcasts.</p> + <marker id="option-buffer"></marker> </item> + + <tag><c>{buffer, Size}</c></tag> + <item> + <p>Determines the size of the user-level software buffer used by + the driver. Not to be confused with <c>sndbuf</c> + and <c>recbuf</c> options which correspond to + the kernel socket buffers. It is recommended + to have <c>val(buffer) >= max(val(sndbuf),val(recbuf))</c>. + In fact, the <c>val(buffer)</c> is automatically set to + the above maximum when <c>sndbuf</c> or <c>recbuf</c> values are set.</p> + </item> + <tag><c>{delay_send, Boolean}</c></tag> <item> <p>Normally, when an Erlang process sends to a socket, @@ -463,10 +487,19 @@ fe80::204:acff:fe17:bf38 real property of the socket. Needless to say it is an implementation specific option. Default is <c>false</c>.</p> </item> + + <tag><c>{deliver, port | term}</c></tag> + <item> <p> When <c>{active, true}</c> delivers data on the forms + <c>port</c> : <c>{S, {data, [H1,..Hsz | Data]}}</c> or + <c>term</c> : <c>{tcp, S, [H1..Hsz | Data]}</c>. + </p> + </item> + <tag><c>{dontroute, Boolean}</c></tag> <item> <p>Enable/disable routing bypass for outgoing messages.</p> </item> + <tag><c>{exit_on_close, Boolean}</c></tag> <item> <p>By default this option is set to <c>true</c>.</p> @@ -476,6 +509,7 @@ fe80::204:acff:fe17:bf38 <seealso marker="gen_tcp#shutdown/2">gen_tcp:shutdown/2</seealso> to shutdown the write side.</p> </item> + <tag><c>{header, Size}</c></tag> <item> <p>This option is only meaningful if the <c>binary</c> @@ -487,6 +521,15 @@ fe80::204:acff:fe17:bf38 example <c>Size == 2</c>, the data received will match <c>[Byte1,Byte2|Binary]</c>.</p> </item> + + <tag><c>{high_watermark, Size}</c></tag> + <item> <p> + Sender is forced busy if sent and enqueued data + reaches the highwater mark. + <br /> Default: 8192 kB. + </p> + </item> + <tag><c>{keepalive, Boolean}</c>(TCP/IP sockets)</tag> <item> <p>Enables/disables periodic transmission on a connected @@ -494,7 +537,43 @@ fe80::204:acff:fe17:bf38 the other end does not respond, the connection is considered broken and an error message will be sent to the controlling process. Default disabled.</p> + <marker id="option-linger"></marker> </item> + + <tag><c>{linger, {true|false, Seconds}}</c></tag> + <item> + <p>Determines the timeout in seconds for flushing unsent data in the + <c>close/1</c> socket call. If the 1st component of the value + tuple is <c>false</c>, the 2nd one is ignored, which means that + <c>close/1</c> returns immediately not waiting + for data to be flushed. Otherwise, the 2nd component is + the flushing time-out in seconds.</p> + </item> + + <tag><c>{low_watermark, Size}</c></tag> + <item> <p> + If the port has reached its <c>high_watermark</c> it will + force busy onto senders. When the port data queue reaches the + <c>low_watermark</c> callers are no longer forced busy. + <br /> Default: 4096 kB. + </p> + </item> + + <tag><c>{mode, Mode :: binary | list}</c></tag> + <item> + <p>Received <c>Packet</c> is delivered as defined by Mode.</p> + </item> + + <tag><c>list</c></tag> + <item> + <p>Received <c>Packet</c> is delivered as a list.</p> + </item> + + <tag><c>binary</c></tag> + <item> + <p>Received <c>Packet</c> is delivered as a binary.</p> + </item> + <tag><c>{nodelay, Boolean}</c>(TCP/IP sockets)</tag> <item> <p>If <c>Boolean == true</c>, the <c>TCP_NODELAY</c> option @@ -578,6 +657,16 @@ fe80::204:acff:fe17:bf38 indicated length are accepted and not considered invalid due to internal buffer limitations.</p> </item> + + <tag><c>{priority, Priority}</c></tag> + <item> <p>Set the protocol-defined priority for all packets to be sent + on this socket.</p> + </item> + + <tag><c>{raw, Protocol, OptionNum, ValueBin}</c></tag> + <item> <p>See below.</p> + </item> + <tag><c>{read_packets, Integer}</c>(UDP sockets)</tag> <item> <p>Sets the max number of UDP packets to read without @@ -589,7 +678,7 @@ fe80::204:acff:fe17:bf38 high the system can become unresponsive due to UDP packet flooding.</p> </item> - <tag><c>{recbuf, Integer}</c></tag> + <tag><c>{recbuf, Size}</c></tag> <item> <p>Gives the size of the receive buffer to use for the socket.</p> @@ -618,9 +707,10 @@ fe80::204:acff:fe17:bf38 returns <c>{error,timeout}</c>. The recommended setting is <c>true</c> which will automatically close the socket. Default is <c>false</c> due to backward compatibility.</p> + <marker id="option-sndbuf"></marker> </item> - <tag><c>{sndbuf, Integer}</c></tag> + <tag><c>{sndbuf, Size}</c></tag> <item> <p>Gives the size of the send buffer to use for the socket.</p> </item> @@ -639,6 +729,7 @@ fe80::204:acff:fe17:bf38 not implemented. Use with caution.</p> </item> </taglist> + <p>In addition to the options mentioned above, <em>raw</em> option specifications can be used. The raw options are specified as a tuple of arity four, beginning with the tag diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index e94119845a..09c525b376 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -80,6 +80,10 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> Each environment variable is given as a single string on the format <c>"VarName=Value"</c>, where <c>VarName</c> is the name of the variable and <c>Value</c> its value.</p> + <p>If Unicode file name encoding is in effect (see the <seealso + marker="erts:erl#file_name_encoding">erl manual + page</seealso>), the strings may contain characters with + codepoints > 255.</p> </desc> </func> <func> @@ -93,6 +97,10 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> <p>Returns the <c>Value</c> of the environment variable <c>VarName</c>, or <c>false</c> if the environment variable is undefined.</p> + <p>If Unicode file name encoding is in effect (see the <seealso + marker="erts:erl#file_name_encoding">erl manual + page</seealso>), the strings (both <c>VarName</c> and + <c>Value</c>) may contain characters with codepoints > 255.</p> </desc> </func> <func> @@ -123,6 +131,13 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> <desc> <p>Sets a new <c>Value</c> for the environment variable <c>VarName</c>.</p> + <p>If Unicode filename encoding is in effect (see the <seealso + marker="erts:erl#file_name_encoding">erl manual + page</seealso>), the strings (both <c>VarName</c> and + <c>Value</c>) may contain characters with codepoints > 255.</p> + <p>On Unix platforms, the environment will be set using UTF-8 encoding + if Unicode file name translation is in effect. On Windows the + environment is set using wide character interfaces.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/packages.xml b/lib/kernel/doc/src/packages.xml index 80de2e05fc..81b8693baa 100644 --- a/lib/kernel/doc/src/packages.xml +++ b/lib/kernel/doc/src/packages.xml @@ -204,11 +204,5 @@ ok Explicitly declaring each use of a module makes for safe code.</p> </description> - <funcs> - <func> - <name>no functions exported</name> - <fsummary>x</fsummary> - </func> - </funcs> </erlref> diff --git a/lib/kernel/examples/Makefile b/lib/kernel/examples/Makefile index fb27f8d438..21dfd0ec0e 100644 --- a/lib/kernel/examples/Makefile +++ b/lib/kernel/examples/Makefile @@ -47,8 +47,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/kernel-$(KERNEL_VSN)/examples EXAMPLES = uds_dist release_spec: - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" tar cf - $(EXAMPLES) | \ - (cd $(RELSYSDIR); tar xf - ; chmod -R ug+w $(EXAMPLES) ) + (cd "$(RELSYSDIR)"; tar xf - ; chmod -R ug+w $(EXAMPLES) ) release_docs_spec: diff --git a/lib/kernel/examples/uds_dist/c_src/uds_drv.c b/lib/kernel/examples/uds_dist/c_src/uds_drv.c index 9327ab19dc..9ad6b85a0f 100644 --- a/lib/kernel/examples/uds_dist/c_src/uds_drv.c +++ b/lib/kernel/examples/uds_dist/c_src/uds_drv.c @@ -967,7 +967,7 @@ static void *my_malloc(size_t size) void *ptr; if ((ptr = driver_alloc(size)) == NULL) { - erl_exit(1,"Could not allocate %d bytes of memory",(int) size); + erl_exit(1,"Could not allocate %lu bytes of memory",(unsigned long) size); } return ptr; } @@ -977,7 +977,7 @@ static void *my_realloc(void *ptr, size_t size) void erl_exit(int, char *, ...); void *nptr; if ((nptr = driver_realloc(ptr, size)) == NULL) { - erl_exit(1,"Could not reallocate %d bytes of memory",(int) size); + erl_exit(1,"Could not reallocate %lu bytes of memory",(unsigned long) size); } return nptr; } diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 54f21eb2b8..a39864d6b7 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -198,13 +198,13 @@ $(EBIN)/erl_epmd.beam: $(ESRC)/erl_epmd.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index b7fda69ce0..363072951e 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -63,7 +63,7 @@ which/1, where_is_file/1, where_is_file/2, - set_primary_archive/3, + set_primary_archive/4, clash/0]). -export_type([load_error_rsn/0, load_ret/0]). @@ -107,7 +107,7 @@ %% unstick_mod(Module) -> true %% is_sticky(Module) -> boolean() %% which(Module) -> Filename | loaded_ret_atoms() | non_existing -%% set_primary_archive((FileName, Bin, FileInfo) -> ok | {error, Reason} +%% set_primary_archive((FileName, ArchiveBin, FileInfo, ParserFun) -> ok | {error, Reason} %% clash() -> ok prints out number of clashes %%---------------------------------------------------------------------------- @@ -481,13 +481,16 @@ where_is_file(Path, File) when is_list(Path), is_list(File) -> -spec set_primary_archive(ArchiveFile :: file:filename(), ArchiveBin :: binary(), - FileInfo :: file:file_info()) + FileInfo :: file:file_info(), + ParserFun :: fun()) -> 'ok' | {'error', atom()}. -set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo) +set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo, + ParserFun) when is_list(ArchiveFile0), is_binary(ArchiveBin) -> ArchiveFile = filename:absname(ArchiveFile0), - case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo}) of + case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo, + ParserFun}) of {ok, []} -> ok; {ok, _Mode, Ebins} -> diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index a2db7c9790..00ad923466 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -394,8 +394,8 @@ handle_call(stop,{_From,_Tag}, S) -> handle_call({is_cached,_File}, {_From,_Tag}, S=#state{cache=no_cache}) -> {reply, no, S}; -handle_call({set_primary_archive, File, ArchiveBin, FileInfo}, {_From,_Tag}, S=#state{mode=Mode}) -> - case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo) of +handle_call({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}, {_From,_Tag}, S=#state{mode=Mode}) -> + case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) of {ok, Files} -> {reply, {ok, Mode, Files}, S}; {error, _Reason} = Error -> diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index f5f972c112..5b1efcd395 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -282,7 +282,8 @@ change_notify(Log, Pid, NewNotify) -> -spec change_header(Log, Header) -> 'ok' | {'error', Reason} when Log :: log(), - Header :: {head, dlog_head_opt()} | {head_func, mfa()}, + Header :: {head, dlog_head_opt()} + | {head_func, MFA :: {atom(), atom(), list()}}, Reason :: no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {badarg, head}. change_header(Log, NewHead) -> @@ -336,7 +337,9 @@ format_error(Error) -> ok | {blocked, QueueLogRecords :: boolean()}} | {node, Node :: node()} | {distributed, Dist :: local | [node()]} - | {head, Head :: none | {head, term()} | mfa()} + | {head, Head :: none + | {head, term()} + | (MFA :: {atom(), atom(), list()})} | {no_written_items, NoWrittenItems ::non_neg_integer()} | {full, Full :: boolean} | {no_current_bytes, non_neg_integer()} diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl index 259967650f..242a25a7a6 100644 --- a/lib/kernel/src/disk_log.hrl +++ b/lib/kernel/src/disk_log.hrl @@ -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 @@ -74,7 +74,7 @@ | {distributed, Nodes :: [node()]} | {notify, boolean()} | {head, Head :: dlog_head_opt()} - | {head_func, mfa()} + | {head_func, MFA :: {atom(), atom(), list()}} | {mode, Mode :: dlog_mode()}. -type dlog_options() :: [dlog_option()]. -type dlog_repair() :: 'truncate' | boolean(). diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index d8033ee192..cdb984c333 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -1258,7 +1258,7 @@ sendfile_fallback_int(File, Sock, Bytes, ChunkSize, BytesSent) when Bytes > BytesSent; Bytes == 0 -> Size = if Bytes == 0 -> ChunkSize; - (Bytes - BytesSent + ChunkSize) > 0 -> + (Bytes - BytesSent) < ChunkSize -> Bytes - BytesSent; true -> ChunkSize diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index d8954f0cf7..8fa963ec78 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -425,9 +425,10 @@ error_string(X) -> erlang:error(badarg, [X]). --spec controlling_process(Socket, Pid) -> ok when +-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: sctp_socket(), - Pid :: pid(). + Pid :: pid(), + Reason :: closed | not_owner | inet:posix(). controlling_process(S, Pid) when is_port(S), is_pid(Pid) -> inet:udp_controlling_process(S, Pid); diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index ef6bfdf7f4..e6dfdadb03 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -31,7 +31,6 @@ -type option() :: {active, true | false | once} | - {bit8, clear | set | on | off} | {buffer, non_neg_integer()} | {delay_send, boolean()} | {deliver, port | term} | @@ -61,7 +60,6 @@ {tos, non_neg_integer()}. -type option_name() :: active | - bit8 | buffer | delay_send | deliver | diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index 8688799ae9..914854c65c 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -185,9 +185,10 @@ connect(S, Address, Port) when is_port(S) -> Error end. --spec controlling_process(Socket, Pid) -> ok when +-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: socket(), - Pid :: pid(). + Pid :: pid(), + Reason :: closed | not_owner | inet:posix(). controlling_process(S, NewOwner) -> inet:udp_controlling_process(S, NewOwner). diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl index 255ae4e51b..218be964a0 100644 --- a/lib/kernel/src/heart.erl +++ b/lib/kernel/src/heart.erl @@ -18,6 +18,10 @@ %% -module(heart). +-compile(no_native). +% 'no_native' as part of a crude fix to make init:restart/0 work by clearing +% all hipe inter-module information (hipe_mfa_info's in hipe_bif0.c). + %%%-------------------------------------------------------------------- %%% This is a rewrite of pre_heart from BS.3. %%% diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index 8b3aa0286d..514c002d87 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -34,6 +34,13 @@ -module(hipe_unified_loader). +-compile(no_native). +% 'no_native' is a workaround to avoid "The code server called unloaded module" +% caused by Mod:module_info(exports) in patch_to_emu_step1() called by post_beam_load. +% Reproducable with hipelibs and asn1_SUITE. +% I think the real solution would be to let BIF erlang:load_module/2 redirect all +% hipe calls to the module and thereby remove post_beam_load. + -export([chunk_name/1, %% Only the code and code_server modules may call the entries below! load_native_code/2, diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index abaf4486dc..1a03424f88 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -763,8 +763,12 @@ sctp_opt([Opt|Opts], Mod, R, As) -> {Name,Val} -> sctp_opt (Opts, Mod, R, As, Name, Val); _ -> {error,badarg} end; -sctp_opt([], _Mod, R, _SockOpts) -> - {ok, R}. +sctp_opt([], _Mod, #sctp_opts{ifaddr=IfAddr}=R, _SockOpts) -> + if is_list(IfAddr) -> + {ok, R#sctp_opts{ifaddr=lists:reverse(IfAddr)}}; + true -> + {ok, R} + end. sctp_opt(Opts, Mod, R, As, Name, Val) -> case add_opt(Name, Val, R#sctp_opts.opts, As) of @@ -1015,11 +1019,7 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 -> case prim_inet:setopts(S, Opts) of ok -> case if is_list(Addr) -> - prim_inet:bind(S, add, - [case A of - {_,_} -> A; - _ -> {A,Port} - end || A <- Addr]); + bindx(S, Addr, Port); true -> prim_inet:bind(S, Addr, Port) end of @@ -1040,6 +1040,34 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 -> open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module) -> fdopen(Fd, Opts, Protocol, Family, Type, Module). +bindx(S, [Addr], Port0) -> + {IP, Port} = set_bindx_port(Addr, Port0), + prim_inet:bind(S, IP, Port); +bindx(S, Addrs, Port0) -> + [{IP, Port} | Rest] = [set_bindx_port(Addr, Port0) || Addr <- Addrs], + case prim_inet:bind(S, IP, Port) of + {ok, AssignedPort} when Port =:= 0 -> + %% On newer Linux kernels, Solaris and FreeBSD, calling + %% bindx with port 0 is ok, but on SuSE 10, it results in einval + Rest2 = [change_bindx_0_port(Addr, AssignedPort) || Addr <- Rest], + prim_inet:bind(S, add, Rest2); + {ok, _} -> + prim_inet:bind(S, add, Rest); + Error -> + Error + end. + +set_bindx_port({_IP, _Port}=Addr, _OtherPort) -> + Addr; +set_bindx_port(IP, Port) -> + {IP, Port}. + +change_bindx_0_port({IP, 0}, AssignedPort) -> + {IP, AssignedPort}; +change_bindx_0_port({_IP, _Port}=Addr, _AssignedPort) -> + Addr. + + -spec fdopen(Fd :: non_neg_integer(), Opts :: [socket_setopt()], Protocol :: socket_protocol(), @@ -1246,6 +1274,8 @@ udp_close(S) when is_port(S) -> %% Set controlling process for TCP socket. tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) -> case erlang:port_info(S, connected) of + {connected, NewOwner} -> + ok; {connected, Pid} when Pid =/= self() -> {error, not_owner}; undefined -> @@ -1297,6 +1327,8 @@ tcp_sync_input(S, Owner, Flag) -> %% Set controlling process for UDP or SCTP socket. udp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) -> case erlang:port_info(S, connected) of + {connected, NewOwner} -> + ok; {connected, Pid} when Pid =/= self() -> {error, not_owner}; _ -> diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index e214ffa404..a3fc57a124 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -286,7 +286,7 @@ call(N,M,F,A) -> Reason :: term(), Timeout :: timeout(). -call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call +call(N,M,F,A,infinity) when node() =:= N -> %% Optimize local call local_call(M,F,A); call(N,M,F,A,infinity) -> do_call(N, {call,M,F,A,group_leader()}, infinity); diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 5dcaad3f5e..7bb3e3a365 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -140,12 +140,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(APP_FILES) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) kernel.spec $(EMAKEFILE)\ - $(COVERFILE) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(COVERFILE) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/kernel/test/bif_SUITE.erl b/lib/kernel/test/bif_SUITE.erl index 6276270d20..a2826f34df 100644 --- a/lib/kernel/test/bif_SUITE.erl +++ b/lib/kernel/test/bif_SUITE.erl @@ -260,23 +260,15 @@ spawn_opt2(Config) when is_list(Config) -> ?line P1 = spawn_opt(fun() -> Parent ! {self(), fetch_proc_vals(self())} end, - case heap_type() of - separate -> - [{fullsweep_after, 0},{min_heap_size, 1000}]; - shared -> - [] - end - ++ [link, {priority, max}]), + [{fullsweep_after, 0},{min_heap_size, 1000}, + link, {priority, max}]), ?line receive {P1, PV1} -> ?line Node = node(P1), ?line check_proc_vals(true, max, 0, 1000, PV1) end, ?line P2 = spawn_opt(fun() -> Parent ! {self(), fetch_proc_vals(self())} end, - case heap_type() of - separate -> [{min_heap_size, 10}]; - shared -> [] - end), + [{min_heap_size, 10}]), ?line receive {P2, PV2} -> ?line Node = node(P2), @@ -295,13 +287,8 @@ spawn_opt3(Config) when is_list(Config) -> fun() -> Parent ! {self(), fetch_proc_vals(self())} end, - case heap_type() of - separate -> - [{fullsweep_after,0}, {min_heap_size,1000}]; - shared -> - [] - end - ++ [link, {priority, max}]), + [{fullsweep_after,0}, {min_heap_size,1000}, + link, {priority, max}]), ?line receive {P1, PV1} -> ?line Node = node(P1), @@ -309,10 +296,7 @@ spawn_opt3(Config) when is_list(Config) -> end, ?line P2 = spawn_opt(Node, fun() -> Parent ! {self(), fetch_proc_vals(self())} end, - case heap_type() of - separate -> [{min_heap_size, 10}]; - shared -> [] - end), + [{min_heap_size, 10}]), ?line receive {P2, PV2} -> ?line Node = node(P2), @@ -333,13 +317,8 @@ spawn_opt4(Config) when is_list(Config) -> [fun() -> Parent ! {self(), fetch_proc_vals(self())} end], - case heap_type() of - separate -> - [{fullsweep_after,0}, {min_heap_size,1000}]; - shared -> - [] - end - ++ [link, {priority, max}]), + [{fullsweep_after,0}, {min_heap_size,1000}, + link, {priority, max}]), ?line receive {P1, PV1} -> ?line Node = node(P1), @@ -350,10 +329,7 @@ spawn_opt4(Config) when is_list(Config) -> [fun() -> Parent ! {self(), fetch_proc_vals(self())} end], - case heap_type() of - separate -> [{min_heap_size, 10}]; - shared -> [] - end), + [{min_heap_size, 10}]), ?line receive {P2, PV2} -> ?line Node = node(P2), @@ -374,13 +350,8 @@ spawn_opt5(Config) when is_list(Config) -> [fun() -> Parent ! {self(), fetch_proc_vals(self())} end], - case heap_type() of - separate -> - [{fullsweep_after,0}, {min_heap_size,1000}]; - shared -> - [] - end - ++ [link, {priority, max}]), + [{fullsweep_after,0}, {min_heap_size,1000}, + link, {priority, max}]), ?line receive {P1, PV1} -> ?line Node = node(P1), @@ -392,10 +363,7 @@ spawn_opt5(Config) when is_list(Config) -> [fun() -> Parent ! {self(), fetch_proc_vals(self())} end], - case heap_type() of - separate -> [{min_heap_size, 10}]; - shared -> [] - end), + [{min_heap_size, 10}]), ?line receive {P2, PV2} -> ?line Node = node(P2), @@ -532,34 +500,19 @@ spawn_failures(Config) when is_list(Config) -> check_proc_vals(Link, Priority, FullsweepAfter, MinHeapSize, {Ls, P, FA, HS}) -> ?line Link = lists:member(self(), Ls), ?line Priority = P, - ?line case heap_type() of - separate -> - ?line FullsweepAfter = FA, - ?line true = (HS >= MinHeapSize); - shared -> - ?line ok - end, + FullsweepAfter = FA, + true = (HS >= MinHeapSize), ?line ok. fetch_proc_vals(Pid) -> ?line PI = process_info(Pid), ?line {value,{links, Ls}} = lists:keysearch(links, 1, PI), ?line {value,{priority,P}} = lists:keysearch(priority, 1, PI), - ?line {FA, HS} - = case heap_type() of - separate -> - ?line {value, - {garbage_collection, - Gs}} = lists:keysearch(garbage_collection, 1, PI), - ?line {value, - {fullsweep_after, - Fa}} = lists:keysearch(fullsweep_after, 1, Gs), - ?line {value, - {heap_size,Hs}} = lists:keysearch(heap_size, 1, PI), - ?line {Fa, Hs}; - shared -> - {undefined, undefined} - end, + {value,{garbage_collection,Gs}} = + lists:keysearch(garbage_collection, 1, PI), + {value,{fullsweep_after,FA}} = + lists:keysearch(fullsweep_after, 1, Gs), + {value,{heap_size,HS}} = lists:keysearch(heap_size, 1, PI), ?line {Ls, P, FA, HS}. % This testcase should probably be moved somewhere else @@ -650,12 +603,3 @@ stop_node(Node) -> run_fun(Fun) -> Fun(). - -heap_type() -> - case catch erlang:system_info(heap_type) of - shared -> shared; - unified -> shared; - _ -> separate - end. - - diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 2c59351600..827208b048 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -501,7 +501,7 @@ sticky_dir(doc) -> ["Test that a module with the same name as a module in ", "a sticky directory cannot be loaded."]; sticky_dir(Config) when is_list(Config) -> MyDir=filename:dirname(code:which(?MODULE)), - ?line {ok, Node}=?t:start_node(sticky_dir, slave,[{args, "-pa "++MyDir}]), + ?line {ok, Node}=?t:start_node(sticky_dir, slave,[{args, "-pa \""++MyDir++"\""}]), File=filename:join([?config(data_dir, Config), "calendar"]), ?line Ret=rpc:call(Node, ?MODULE, sticky_compiler, [File]), case Ret of @@ -822,7 +822,7 @@ load_cached(Config) when is_list(Config) -> ?line WD = filename:dirname(code:which(?MODULE)), ?line {ok,Node} = ?t:start_node(code_cache_node, peer, [{args, - "-pa " ++ WD}, + "-pa \"" ++ WD ++ "\""}, {erl, [this]}]), CCTabCreated = fun(Tab) -> case ets:info(Tab, name) of @@ -907,7 +907,7 @@ add_and_rehash(Config) when is_list(Config) -> ?line WD = filename:dirname(code:which(?MODULE)), ?line {ok,Node} = ?t:start_node(code_cache_node, peer, [{args, - "-pa " ++ WD}, + "-pa \"" ++ WD ++ "\""}, {erl, [this]}]), CCTabCreated = fun(Tab) -> case ets:info(Tab, name) of @@ -1550,7 +1550,8 @@ native_early_modules_1(Architecture) -> true -> ?line true = lists:all(fun code:is_module_native/1, [ets,file,filename,gb_sets,gb_trees, - hipe_unified_loader,lists,os,packages]), + %%hipe_unified_loader, no_native as workaround + lists,os,packages]), ok end. diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index ad987fe7a7..0c3f5c3514 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -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 @@ -90,7 +90,7 @@ evil/1, - otp_6278/1]). + otp_6278/1, otp_10131/1]). -export([head_fun/1, hf/0, lserv/1, measure/0, init_m/1, xx/0, head_exit/0, slow_header/1]). @@ -124,7 +124,7 @@ [halt_int, wrap_int, halt_ext, wrap_ext, read_mode, head, notif, new_idx_vsn, reopen, block, unblock, open, close, error, chunk, truncate, many_users, info, change_size, - change_attribute, distribution, evil, otp_6278]). + change_attribute, distribution, evil, otp_6278, otp_10131]). %% The following two lists should be mutually exclusive. To skip a case %% on VxWorks altogether, use the kernel.spec.vxworks file instead. @@ -153,7 +153,7 @@ all() -> {group, open}, {group, close}, {group, error}, chunk, truncate, many_users, {group, info}, {group, change_size}, change_attribute, - {group, distribution}, evil, otp_6278]. + {group, distribution}, evil, otp_6278, otp_10131]. groups() -> [{halt_int, [], [halt_int_inf, {group, halt_int_sz}]}, @@ -4915,6 +4915,22 @@ otp_6278(Conf) when is_list(Conf) -> end, ?line error_logger:delete_report_handler(?MODULE). +otp_10131(suite) -> []; +otp_10131(doc) -> ["OTP-10131. head_func type."]; +otp_10131(Conf) when is_list(Conf) -> + Dir = ?privdir(Conf), + Log = otp_10131, + File = filename:join(Dir, lists:concat([Log, ".LOG"])), + HeadFunc = {?MODULE, head_fun, [{ok,"head"}]}, + {ok, Log} = disk_log:open([{name,Log},{file,File}, + {head_func, HeadFunc}]), + HeadFunc = info(Log, head, undef), + HeadFunc2 = {?MODULE, head_fun, [{ok,"head2"}]}, + ok = disk_log:change_header(Log, {head_func, HeadFunc2}), + HeadFunc2 = info(Log, head, undef), + ok = disk_log:close(Log), + ok. + mark(FileName, What) -> {ok,Fd} = file:open(FileName, [raw, binary, read, write]), {ok,_} = file:position(Fd, 4), diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl index 6f4f27d594..72239641e9 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE.erl @@ -426,7 +426,9 @@ primary_archive(Config) when is_list(Config) -> ExpectedEbins = [Archive, DictDir ++ "/ebin", DummyDir ++ "/ebin"], io:format("ExpectedEbins: ~p\n", [ExpectedEbins]), ?line {ok, FileInfo} = prim_file:read_file_info(Archive), - ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, [Archive, ArchiveBin, FileInfo]), + ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, + [Archive, ArchiveBin, FileInfo, + fun escript:parse_file/1]), ?line ExpectedEbins = lists:sort(Ebins), % assert ?line {ok, TopFiles2} = rpc:call(Node, erl_prim_loader, list_dir, [Archive]), @@ -435,7 +437,9 @@ primary_archive(Config) when is_list(Config) -> ?line ok = test_archive(Node, Archive, DictDir, BeamName), %% Cleanup - ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, [undefined, undefined, undefined]), + ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, + [undefined, undefined, undefined, + fun escript:parse_file/1]), ?line stop_node(Node), ?line ok = file:delete(Archive), ok. diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index 53bcb1162d..be33ec2c06 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -74,7 +74,7 @@ init_per_suite/1,end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). --export([normal/1,icky/1,very_icky/1,normalize/1]). +-export([normal/1,icky/1,very_icky/1,normalize/1,home_dir/1]). init_per_testcase(_Func, Config) -> @@ -88,7 +88,7 @@ end_per_testcase(_Func, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [normal, icky, very_icky, normalize]. + [normal, icky, very_icky, normalize, home_dir]. groups() -> []. @@ -105,6 +105,54 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +home_dir(suite) -> + []; +home_dir(doc) -> + ["Check that Erlang can be started with unicode named home directory"]; +home_dir(Config) when is_list(Config) -> + try + Name=[960,945,964,961,953,954], + Priv = ?config(priv_dir, Config), + UniMode = file:native_name_encoding() =/= latin1, + if + not UniMode -> + throw(need_unicode_mode); + true -> + ok + end, + NewHome=filename:join(Priv,Name), + file:make_dir(NewHome), + {SaveOldName,SaveOldValue} = case os:type() of + {win32,nt} -> + HomePath=re:replace(filename:nativename(NewHome),"^[a-zA-Z]:","",[{return,list},unicode]), + Save = os:getenv("HOMEPATH"), + os:putenv("HOMEPATH",HomePath), + {"HOMEPATH",Save}; + {unix,_} -> + Save = os:getenv("HOME"), + os:putenv("HOME",NewHome), + {"HOME",Save}; + _ -> + rm_rf(prim_file,NewHome), + throw(unsupported_os) + end, + try + {ok,Node} = test_server:start_node(test_unicode_homedir,slave,[{args,"-setcookie "++atom_to_list(erlang:get_cookie())}]), + test_server:stop_node(Node), + ok + after + os:putenv(SaveOldName,SaveOldValue), + rm_rf(prim_file,NewHome) + end + catch + throw:need_unicode_mode -> + io:format("Sorry, can only run in unicode mode.~n"), + {skipped,"VM needs to be started in Unicode filename mode"}; + throw:unsupported_os -> + io:format("Sorry, can only run on Unix/Windows.~n"), + {skipped,"Runs only on Unix/Windows"} + end. + normalize(suite) -> []; normalize(doc) -> diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index 8f490b6643..f4bf6e719e 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -31,14 +31,22 @@ [basic/1, api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1, xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1, - basic_stream/1, xfer_stream_min/1, peeloff/1, buffers/1]). + basic_stream/1, xfer_stream_min/1, peeloff/1, buffers/1, + open_multihoming_ipv4_socket/1, + open_unihoming_ipv6_socket/1, + open_multihoming_ipv6_socket/1, + open_multihoming_ipv4_and_ipv6_socket/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, api_open_close, api_listen, api_connect_init, api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6, - basic_stream, xfer_stream_min, peeloff, buffers]. + basic_stream, xfer_stream_min, peeloff, buffers, + open_multihoming_ipv4_socket, + open_unihoming_ipv6_socket, + open_multihoming_ipv6_socket, + open_multihoming_ipv4_and_ipv6_socket]. groups() -> []. @@ -1105,6 +1113,192 @@ mk_data(N, Bytes, Bin) when N < Bytes -> mk_data(_, _, Bin) -> Bin. + + +open_multihoming_ipv4_socket(doc) -> + "Test opening a multihoming ipv4 socket"; +open_multihoming_ipv4_socket(suite) -> + []; +open_multihoming_ipv4_socket(Config) when is_list(Config) -> + ?line case get_addrs_by_family(inet, 2) of + {ok, [Addr1, Addr2]} -> + ?line do_open_and_connect([Addr1, Addr2], Addr1); + {error, Reason} -> + {skip, Reason} + end. + +open_unihoming_ipv6_socket(doc) -> + %% This test is mostly aimed to indicate + %% whether host has a non-working ipv6 setup + "Test opening a unihoming (non-multihoming) ipv6 socket"; +open_unihoming_ipv6_socket(suite) -> + []; +open_unihoming_ipv6_socket(Config) when is_list(Config) -> + ?line case get_addrs_by_family(inet6, 1) of + {ok, [Addr]} -> + ?line do_open_and_connect([Addr], Addr); + {error, Reason} -> + {skip, Reason} + end. + + +open_multihoming_ipv6_socket(doc) -> + "Test opening a multihoming ipv6 socket"; +open_multihoming_ipv6_socket(suite) -> + []; +open_multihoming_ipv6_socket(Config) when is_list(Config) -> + ?line case get_addrs_by_family(inet6, 2) of + {ok, [Addr1, Addr2]} -> + ?line do_open_and_connect([Addr1, Addr2], Addr1); + {error, Reason} -> + {skip, Reason} + end. + +open_multihoming_ipv4_and_ipv6_socket(doc) -> + "Test opening a multihoming ipv6 socket with ipv4 and ipv6 addresses"; +open_multihoming_ipv4_and_ipv6_socket(suite) -> + []; +open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) -> + ?line case get_addrs_by_family(inet_and_inet6, 2) of + {ok, [[InetAddr1, InetAddr2], [Inet6Addr1, Inet6Addr2]]} -> + %% Connect to the first address to test bind + ?line do_open_and_connect([InetAddr1, Inet6Addr1, InetAddr2], + InetAddr1), + ?line do_open_and_connect([Inet6Addr1, InetAddr1], + Inet6Addr1), + + %% Connect an address, not the first, + %% to test sctp_bindx + ?line do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1], + Inet6Addr2), + ?line do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1], + InetAddr1); + {error, Reason} -> + {skip, Reason} + end. + + +get_addrs_by_family(Family, NumAddrs) -> + case os:type() of + {unix,linux} -> + get_addrs_by_family_aux(Family, NumAddrs); + {unix,freebsd} -> + get_addrs_by_family_aux(Family, NumAddrs); + {unix,sunos} -> + case get_addrs_by_family_aux(Family, NumAddrs) of + {ok, [InetAddrs, Inet6Addrs]} when Family =:= inet_and_inet6 -> + %% Man page for sctp_bindx on Solaris says: "If sock is an + %% Internet Protocol Version 6 (IPv6) socket, addrs should + %% be an array of sockaddr_in6 structures containing IPv6 + %% or IPv4-mapped IPv6 addresses." + {ok, [ipv4_map_addrs(InetAddrs), Inet6Addrs]}; + {ok, Addrs} -> + {ok, Addrs}; + {error, Reason} -> + {error, Reason} + end; + Os -> + Reason = if Family =:= inet_and_inet6 -> + f("Mixing ipv4 and ipv6 addresses for multihoming " + " has not been verified on ~p", [Os]); + true -> + f("Multihoming for ~p has not been verified on ~p", + [Family, Os]) + end, + {error, Reason} + end. + +get_addrs_by_family_aux(Family, NumAddrs) when Family =:= inet; + Family =:= inet6 -> + ?line + case inet:getaddr(localhost, Family) of + {error,eafnosupport} -> + {skip, f("No support for ~p", Family)}; + {ok, _} -> + ?line IfAddrs = ok(inet:getifaddrs()), + ?line case filter_addrs_by_family(IfAddrs, Family) of + Addrs when length(Addrs) >= NumAddrs -> + {ok, lists:sublist(Addrs, NumAddrs)}; + [] -> + {error, f("Need ~p ~p address(es) found none~n", + [NumAddrs, Family])}; + Addrs -> + {error, + f("Need ~p ~p address(es) found only ~p: ~p~n", + [NumAddrs, Family, length(Addrs), Addrs])} + end + end; +get_addrs_by_family_aux(inet_and_inet6, NumAddrs) -> + ?line catch {ok, [case get_addrs_by_family_aux(Family, NumAddrs) of + {ok, Addrs} -> Addrs; + {error, Reason} -> throw({error, Reason}) + end || Family <- [inet, inet6]]}. + +filter_addrs_by_family(IfAddrs, Family) -> + lists:flatten([[Addr || {addr, Addr} <- Info, + is_good_addr(Addr, Family)] + || {_IfName, Info} <- IfAddrs]). + +is_good_addr(Addr, inet) when tuple_size(Addr) =:= 4 -> + true; +is_good_addr({0,0,0,0,0,16#ffff,_,_}, inet6) -> + false; %% ipv4 mapped +is_good_addr({16#fe80,_,_,_,_,_,_,_}, inet6) -> + false; %% link-local +is_good_addr(Addr, inet6) when tuple_size(Addr) =:= 8 -> + true; +is_good_addr(_Addr, _Family) -> + false. + +ipv4_map_addrs(InetAddrs) -> + [begin + <<AB:16>> = <<A,B>>, + <<CD:16>> = <<C,D>>, + {0, 0, 0, 0, 0, 16#ffff, AB, CD} + end || {A,B,C,D} <- InetAddrs]. + +f(F, A) -> + lists:flatten(io_lib:format(F, A)). + +do_open_and_connect(ServerAddresses, AddressToConnectTo) -> + ?line ServerFamily = get_family_by_addrs(ServerAddresses), + ?line io:format("Serving ~p addresses: ~p~n", + [ServerFamily, ServerAddresses]), + ?line S1 = ok(gen_sctp:open(0, [{ip,Addr} || Addr <- ServerAddresses] ++ + [ServerFamily])), + ?line ok = gen_sctp:listen(S1, true), + ?line P1 = ok(inet:port(S1)), + ?line ClientFamily = get_family_by_addr(AddressToConnectTo), + ?line io:format("Connecting to ~p ~p~n", + [ClientFamily, AddressToConnectTo]), + ?line S2 = ok(gen_sctp:open(0, [ClientFamily])), + %% Verify client can connect + ?line #sctp_assoc_change{state=comm_up} = + ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])), + %% verify server side also receives comm_up from client + ?line recv_comm_up_eventually(S1), + ?line ok = gen_sctp:close(S2), + ?line ok = gen_sctp:close(S1). + +%% If at least one of the addresses is an ipv6 address, return inet6, else inet. +get_family_by_addrs(Addresses) -> + ?line case lists:usort([get_family_by_addr(Addr) || Addr <- Addresses]) of + [inet, inet6] -> inet6; + [inet] -> inet; + [inet6] -> inet6 + end. + +get_family_by_addr(Addr) when tuple_size(Addr) =:= 4 -> inet; +get_family_by_addr(Addr) when tuple_size(Addr) =:= 8 -> inet6. + +recv_comm_up_eventually(S) -> + ?line case ok(gen_sctp:recv(S)) of + {_Addr, _Port, _Info, #sctp_assoc_change{state=comm_up}} -> + ok; + {_Addr, _Port, _Info, _OtherSctpMsg} -> + ?line recv_comm_up_eventually(S) + end. + %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% socket gen_server ultra light diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index c74a258af9..1592399996 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -24,7 +24,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - controlling_process/1, no_accept/1, close_with_pending_output/1, + controlling_process/1, controlling_process_self/1, + no_accept/1, close_with_pending_output/1, data_before_close/1, iter_max_socks/1, get_status/1, passive_sockets/1, accept_closed_by_other_process/1, init_per_testcase/2, end_per_testcase/2, @@ -58,7 +59,7 @@ end_per_testcase(_Func, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [controlling_process, no_accept, + [controlling_process, controlling_process_self, no_accept, close_with_pending_output, data_before_close, iter_max_socks, passive_sockets, accept_closed_by_other_process, otp_3924, closed_socket, @@ -307,6 +308,32 @@ not_owner(S) -> ok end. +controlling_process_self(doc) -> + ["Open a listen port and assign the controlling process to " + "it self, then exit and make sure the port is closed properly."]; +controlling_process_self(Config) when is_list(Config) -> + S = self(), + process_flag(trap_exit,true), + spawn_link(fun() -> + {ok,Sock} = gen_tcp:listen(0,[]), + S ! {socket, Sock}, + ok = gen_tcp:controlling_process(Sock,self()), + S ! done + end), + receive + done -> + receive + {socket,Sock} -> + process_flag(trap_exit,false), + %% Make sure the port is invalid after process crash + {error,einval} = inet:port(Sock) + end; + Msg when element(1,Msg) /= socket -> + process_flag(trap_exit,false), + exit({unknown_msg,Msg}) + end. + + no_accept(doc) -> ["Open a listen port and connect to it, then close the listen port ", "without doing any accept. The connected socket should receive ", @@ -2044,7 +2071,7 @@ send_timeout_active(Config) when is_list(Config) -> ?line {error,timeout} = Loop(fun() -> receive - {tcp, Sock, _Data} -> + {tcp, _Sock, _Data} -> inet:setopts(A, [{active, once}]), Res = gen_tcp:send(A,lists:duplicate(1000, $a)), %erlang:display(Res), @@ -2536,7 +2563,7 @@ otp_8102_do(LSocket, PortNum, {Bin,PType}) -> otp_9389(doc) -> ["Verify packet_size handles long HTTP header lines"]; otp_9389(suite) -> []; otp_9389(Config) when is_list(Config) -> - ?line {ok, LS} = gen_tcp:listen(0, []), + ?line {ok, LS} = gen_tcp:listen(0, [{active,false}]), ?line {ok, {_, PortNum}} = inet:sockname(LS), io:format("Listening on ~w with port number ~p\n", [LS, PortNum]), OrigLinkHdr = "/" ++ string:chars($S, 8192), diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index b39fadd65f..e3fa4642b7 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -608,7 +608,7 @@ boot2(Config) when is_list(Config) -> %% Absolute boot file name Boot = filename:join([code:root_dir(), "bin", "start_sasl"]), - Args = args() ++ " -boot " ++ Boot, + Args = args() ++ " -boot \"" ++ Boot++"\"", ?line {ok, Node} = start_node(init_test, Args), ?line stop_node(Node), @@ -618,7 +618,7 @@ boot2(Config) when is_list(Config) -> %% converted to backslashes. Win_boot = lists:map(fun($/) -> $\\; (C) -> C end, Boot), - Args2 = args() ++ " -boot " ++ Win_boot, + Args2 = args() ++ " -boot \"" ++ Win_boot ++ "\"", ?line {ok, Node2} = start_node(init_test, Args2), ?line stop_node(Node2); _ -> diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl index b2308dd321..96e45cc23f 100644 --- a/lib/kernel/test/interactive_shell_SUITE.erl +++ b/lib/kernel/test/interactive_shell_SUITE.erl @@ -251,7 +251,7 @@ rtnode(Commands,Nodename,ErlPrefix) -> ?line {skip, Reason2}; Tempdir -> ?line SPid = - start_runerl_node(RunErl,ErlPrefix++Erl, + start_runerl_node(RunErl,ErlPrefix++"\\\""++Erl++"\\\"", Tempdir,Nodename), ?line CPid = start_toerl_server(ToErl,Tempdir), ?line erase(getline_skipped), @@ -487,7 +487,7 @@ start_runerl_node(RunErl,Erl,Tempdir,Nodename) -> " -setcookie "++atom_to_list(erlang:get_cookie()) end, spawn(fun() -> - os:cmd(RunErl++" "++Tempdir++"/ "++Tempdir++" \""++ + os:cmd("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++" \""++ Erl++XArg++"\"") end). @@ -518,7 +518,7 @@ try_to_erl(Command, N) -> end. toerl_server(Parent,ToErl,Tempdir) -> - Port = try_to_erl(ToErl++" "++Tempdir++"/ 2>/dev/null",8), + Port = try_to_erl("\""++ToErl++"\" "++Tempdir++"/ 2>/dev/null",8), case Port of P when is_port(P) -> Parent ! {self(),started}; diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 76d3003ff4..c494f8a864 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 2.15.1 +KERNEL_VSN = 2.15.2 diff --git a/lib/megaco/Makefile b/lib/megaco/Makefile index 9dc84c122c..fc8f408b0a 100644 --- a/lib/megaco/Makefile +++ b/lib/megaco/Makefile @@ -49,11 +49,14 @@ VSN=$(MEGACO_VSN) DIR_NAME = megaco_src-$(VSN)$(PRE_VSN) +nullstring := +space := $(nullstring) # a space at the end + ifndef APP_RELEASE_DIR ifndef TESTROOT APP_RELEASE_DIR = /tmp else - APP_RELEASE_DIR = $(TESTROOT) + APP_RELEASE_DIR = $(subst $(space),\ ,$(TESTROOT)) endif endif @@ -151,7 +154,7 @@ version: # Application install (of a app built from source) targets # ---------------------------------------------------- app_install: - $(MAKE) TESTROOT=$(APP_INSTALL_DIR) release + $(MAKE) TESTROOT="$(APP_INSTALL_DIR)" release # ---------------------------------------------------- @@ -185,20 +188,20 @@ TAR.exclude2: Makefile TAR.exclude (cd ..; find megaco -name '.cmake.state' >> megaco/TAR.exclude2) $(APP_DIR): tar_exclude - mkdir -p $(APP_DIR); \ + mkdir -p "$(subst $(space),\ ,$@)"; \ (cd ..; tar cfX - megaco/TAR.exclude2 megaco) | \ - (cd $(APP_DIR); tar xf -); \ - mv $(APP_DIR)/megaco/* $(APP_DIR)/; \ - mkdir $(APP_DIR)/autoconf; \ - cp autoconf/config.guess $(APP_DIR)/autoconf/; \ - cp autoconf/config.sub $(APP_DIR)/autoconf/; \ - cp autoconf/install-sh $(APP_DIR)/autoconf/; \ - rmdir $(APP_DIR)/megaco + (cd "$(subst $(space),\ ,$@)"; tar xf -); \ + mv "$(subst $(space),\ ,$@)"/megaco/* "$(subst $(space),\ ,$@)"/; \ + mkdir $(subst $(space),\ ,$@)/autoconf; \ + cp autoconf/config.guess "$(subst $(space),\ ,$@)"/autoconf/; \ + cp autoconf/config.sub "$(subst $(space),\ ,$@)"/autoconf/; \ + cp autoconf/install-sh "$(subst $(space),\ ,$@)"/autoconf/; \ + rmdir "$(subst $(space),\ ,$@)"/megaco tar: $(APP_TAR_FILE) $(APP_TAR_FILE): $(APP_DIR) - (cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME)) + (cd "$(APP_RELEASE_DIR)"; gtar zcf "$(subst $(space),\ ,$@)" $(DIR_NAME)) dialyzer_plt: $(DIA_PLT) diff --git a/lib/megaco/doc/src/Makefile b/lib/megaco/doc/src/Makefile index 137f0315d8..493304523e 100644 --- a/lib/megaco/doc/src/Makefile +++ b/lib/megaco/doc/src/Makefile @@ -164,16 +164,16 @@ info: 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 - $(INSTALL_DIR) $(RELSYSDIR)/doc/standard - $(INSTALL_DATA) $(STANDARDS) $(RELSYSDIR)/doc/standard + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/standard" + $(INSTALL_DATA) $(STANDARDS) "$(RELSYSDIR)/doc/standard" release_spec: diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml index 393064fbb5..928e3bea82 100644 --- a/lib/megaco/doc/src/notes.xml +++ b/lib/megaco/doc/src/notes.xml @@ -36,6 +36,78 @@ section is the version number of Megaco.</p> + <section><title>Megaco 3.16.0.2</title> + + <p>Version 3.16.0.2 supports code replacement in runtime from/to + version 3.16.0.1, 3.16, 3.15.1.1, 3.15.1 and 3.15.</p> + + <section> + <title>Improvements and new features</title> + +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>Allow whitespaces in installation path. </p> + <p>It is now possible to give configure and make an + installation/release path with whitespaces in it. </p> + <p>Own Id: OTP-10107</p> + </item> + + <item> + <p>Fix parallel make for behaviours. </p> + </item> + + <item> + <p>Removed use of deprecated system flag, + <c>global_haeps_size</c>, in the measurement tool + <c>mstone1</c>. </p> + </item> + + </list> + + </section> + + <section> + <title>Fixed bugs and malfunctions</title> + + <p>-</p> + + <!-- + <list type="bulleted"> + <item> + <p>Fixing miscellaneous things detected by dialyzer. </p> + <p>Own Id: OTP-9075</p> + </item> + + </list> + --> + + </section> + + <section> + <title>Incompatibilities</title> + <p>-</p> + +<!-- + <list type="bulleted"> + <item> + <p>Due to the change in the flex driver API, + we may no longer be able to build and/or use + the flex driver without reentrant support. </p> + <p>Own Id: OTP-9795</p> + </item> + + </list> +--> + + </section> + + </section> <!-- 3.16.0.2 --> + + <section><title>Megaco 3.16.0.1</title> <p>Version 3.16.0.1 supports code replacement in runtime from/to diff --git a/lib/megaco/examples/meas/Makefile.in b/lib/megaco/examples/meas/Makefile.in index 6af7ef6c65..607d81d440 100644 --- a/lib/megaco/examples/meas/Makefile.in +++ b/lib/megaco/examples/meas/Makefile.in @@ -131,12 +131,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(EXAMPLE_RELSYSDIR) - $(INSTALL_DIR) $(MEAS_RELSYSDIR) - $(INSTALL_DATA) $(MESSAGE_PACKAGES) $(MEAS_RELSYSDIR) - $(INSTALL_DATA) $(SCRIPT_SKELETONS) $(MEAS_RELSYSDIR) - $(INSTALL_DATA) $(TARGET_FILES) $(MEAS_RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(MEAS_RELSYSDIR) + $(INSTALL_DIR) "$(EXAMPLE_RELSYSDIR)" + $(INSTALL_DIR) "$(MEAS_RELSYSDIR)" + $(INSTALL_DATA) $(MESSAGE_PACKAGES) "$(MEAS_RELSYSDIR)" + $(INSTALL_DATA) $(SCRIPT_SKELETONS) "$(MEAS_RELSYSDIR)" + $(INSTALL_DATA) $(TARGET_FILES) "$(MEAS_RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) "$(MEAS_RELSYSDIR)" release_docs_spec: diff --git a/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl b/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl index ca8016d65f..9af88d9f50 100644 --- a/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl +++ b/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl @@ -100,7 +100,6 @@ display_system_info() -> OtpRel = otp_release(), SysVer = system_version(), SysHT = heap_type(), - SysGHSz = global_heaps_size(), SysSMP = smp_support(), SysNumSched = schedulers(), SysProcLimit = process_limit(), @@ -113,7 +112,6 @@ display_system_info() -> io:format("OTP release: ~s~n", [OtpRel]), io:format("System version: ~s~n", [SysVer]), io:format("Heap type: ~s~n", [SysHT]), - io:format("Global heap size: ~s~n", [SysGHSz]), io:format("Thread support: ~s~n", [SysThreads]), io:format("Thread pool size: ~s~n", [SysTPSz]), io:format("Process limit: ~s~n", [SysProcLimit]), @@ -137,9 +135,6 @@ system_version() -> heap_type() -> system_info(heap_type, any). -global_heaps_size() -> - system_info(global_heaps_size, any). - smp_support() -> system_info(smp_support, any). diff --git a/lib/megaco/examples/simple/Makefile b/lib/megaco/examples/simple/Makefile index f91d1d886f..261acbb479 100644 --- a/lib/megaco/examples/simple/Makefile +++ b/lib/megaco/examples/simple/Makefile @@ -139,9 +139,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DIR) $(RELSYSDIR)/examples/simple - $(INSTALL_DATA) $(ERL_FILES) $(TARGET_FILES) $(RELSYSDIR)/examples/simple + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/simple" + $(INSTALL_DATA) $(ERL_FILES) $(TARGET_FILES) "$(RELSYSDIR)/examples/simple" release_docs_spec: diff --git a/lib/megaco/src/app/Makefile b/lib/megaco/src/app/Makefile index 01dfb9b860..a7b458845d 100644 --- a/lib/megaco/src/app/Makefile +++ b/lib/megaco/src/app/Makefile @@ -108,14 +108,14 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/app - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/app - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/app" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/app" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src index 7f89fa8bc2..a7b38eb107 100644 --- a/lib/megaco/src/app/megaco.appup.src +++ b/lib/megaco/src/app/megaco.appup.src @@ -145,10 +145,17 @@ %% | %% v %% 3.16.0.1 +%% | +%% v +%% 3.16.0.2 %% %% {"%VSN%", [ + {"3.16.0.1", + [ + ] + }, {"3.16", [ ] @@ -170,6 +177,10 @@ } ], [ + {"3.16.0.1", + [ + ] + }, {"3.16", [ ] diff --git a/lib/megaco/src/binary/Makefile b/lib/megaco/src/binary/Makefile index d594f34f43..77b06c81d3 100644 --- a/lib/megaco/src/binary/Makefile +++ b/lib/megaco/src/binary/Makefile @@ -194,11 +194,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/binary - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(ASN1_FILES) $(RELSYSDIR)/src/binary + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/binary" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(ASN1_FILES) "$(RELSYSDIR)/src/binary" release_docs_spec: diff --git a/lib/megaco/src/engine/Makefile b/lib/megaco/src/engine/Makefile index 3943f4b957..ae2fadb3fc 100644 --- a/lib/megaco/src/engine/Makefile +++ b/lib/megaco/src/engine/Makefile @@ -42,9 +42,14 @@ RELSYSDIR = $(RELEASE_PATH)/lib/megaco-$(VSN) include modules.mk -ERL_FILES = $(MODULES:%=%.erl) +ERL_FILES = \ + $(MODULES:%=%.erl) \ + $(BEHAVIOUR_MODULES:%=%.erl) -TARGET_FILES = \ +BEHAVIOUR_TARGET_FILES= \ + $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR)) + +TARGET_FILES = \ $(MODULES:%=$(EBIN)/%.$(EMULATOR)) @@ -65,19 +70,23 @@ ERL_COMPILE_FLAGS += \ # ---------------------------------------------------- # Targets # ---------------------------------------------------- + +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) + debug: @${MAKE} TYPE=debug opt opt: $(TARGET_FILES) clean: - rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) + rm -f $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) rm -f errs core *~ docs: info: @echo "MODULES = $(MODULES)" + @echo "BEHAVIOUR_MODULES = $(BEHAVIOUR_MODULES)" @echo "" @@ -88,12 +97,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/engine - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/engine - $(INSTALL_DIR) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/engine" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/engine" + $(INSTALL_DIR) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/megaco/src/engine/modules.mk b/lib/megaco/src/engine/modules.mk index 4bc57cd63e..6e2f9246f0 100644 --- a/lib/megaco/src/engine/modules.mk +++ b/lib/megaco/src/engine/modules.mk @@ -23,7 +23,6 @@ BEHAVIOUR_MODULES = \ megaco_transport MODULES = \ - $(BEHAVIOUR_MODULES) \ megaco_config_misc \ megaco_config \ megaco_digit_map \ diff --git a/lib/megaco/src/flex/Makefile.in b/lib/megaco/src/flex/Makefile.in index 7d82644246..976ecaa4f7 100644 --- a/lib/megaco/src/flex/Makefile.in +++ b/lib/megaco/src/flex/Makefile.in @@ -271,16 +271,16 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/flex - $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/flex - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/flex" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/flex" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" ifeq ($(ENABLE_MEGACO_FLEX_SCANNER),true) - $(INSTALL_DATA) $(FLEX_FILES) $(C_TARGETS) $(RELSYSDIR)/src/flex - $(INSTALL_PROGRAM) $(SOLIBS) $(RELSYSDIR)/priv/lib + $(INSTALL_DATA) $(FLEX_FILES) $(C_TARGETS) "$(RELSYSDIR)/src/flex" + $(INSTALL_PROGRAM) $(SOLIBS) "$(RELSYSDIR)/priv/lib" endif diff --git a/lib/megaco/src/tcp/Makefile b/lib/megaco/src/tcp/Makefile index 0bd4b7c4ee..efc76bb2c5 100644 --- a/lib/megaco/src/tcp/Makefile +++ b/lib/megaco/src/tcp/Makefile @@ -89,11 +89,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/tcp - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/tcp + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/tcp" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/tcp" release_docs_spec: diff --git a/lib/megaco/src/text/Makefile b/lib/megaco/src/text/Makefile index b2e8e762dd..2c7703cd33 100644 --- a/lib/megaco/src/text/Makefile +++ b/lib/megaco/src/text/Makefile @@ -130,11 +130,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(BEAM_TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/text - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_YRL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/text + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(BEAM_TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/text" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_YRL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/text" release_docs_spec: diff --git a/lib/megaco/src/udp/Makefile b/lib/megaco/src/udp/Makefile index 64b6478c2c..db9bb72d82 100644 --- a/lib/megaco/src/udp/Makefile +++ b/lib/megaco/src/udp/Makefile @@ -89,11 +89,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/udp - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/udp + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/udp" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/udp" release_docs_spec: diff --git a/lib/megaco/test/Makefile b/lib/megaco/test/Makefile index 88f6f06e73..b1bca10ca8 100644 --- a/lib/megaco/test/Makefile +++ b/lib/megaco/test/Makefile @@ -748,11 +748,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(RELTEST_FILES) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(RELTEST_FILES) "$(RELSYSDIR)" # $(INSTALL_DATA) $(TEST_SPEC_FILE) $(COVER_SPEC_FILE) \ # $(HRL_FILES) $(ERL_FILES) \ -# $(RELSYSDIR) +# "$(RELSYSDIR)" # - chmod -R u+w $(RELSYSDIR) + chmod -R u+w "$(RELSYSDIR)" diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk index 11a951a23e..5e72ade769 100644 --- a/lib/megaco/vsn.mk +++ b/lib/megaco/vsn.mk @@ -18,6 +18,6 @@ # %CopyrightEnd% APPLICATION = megaco -MEGACO_VSN = 3.16.0.1 +MEGACO_VSN = 3.16.0.2 PRE_VSN = APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)" diff --git a/lib/mnesia/doc/src/Makefile b/lib/mnesia/doc/src/Makefile index 1ac5760510..173e43ac1f 100644 --- a/lib/mnesia/doc/src/Makefile +++ b/lib/mnesia/doc/src/Makefile @@ -142,13 +142,13 @@ debug opt: 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) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/mnesia/examples/Makefile b/lib/mnesia/examples/Makefile index ff00ee76a5..5379761c64 100644 --- a/lib/mnesia/examples/Makefile +++ b/lib/mnesia/examples/Makefile @@ -78,9 +78,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) $(RELSYSDIR)/examples - $(INSTALL_DIR) $(RELSYSDIR)/examples/bench + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/bench" (cd bench; $(INSTALL_DATA) \ Makefile \ README \ @@ -96,8 +96,8 @@ release_spec: opt bench.config5 \ bench.config6 \ bench.config7 \ - $(RELSYSDIR)/examples/bench) - (cd bench; $(INSTALL_SCRIPT) bench.sh $(RELSYSDIR)/examples/bench) + "$(RELSYSDIR)/examples/bench") + (cd bench; $(INSTALL_SCRIPT) bench.sh "$(RELSYSDIR)/examples/bench") release_docs_spec: diff --git a/lib/mnesia/include/Makefile b/lib/mnesia/include/Makefile index f9b7d72abe..20f62b5460 100644 --- a/lib/mnesia/include/Makefile +++ b/lib/mnesia/include/Makefile @@ -54,8 +54,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(INCLUDE_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(INCLUDE_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/mnesia/src/Makefile b/lib/mnesia/src/Makefile index 1c8ec54605..1432eabc37 100644 --- a/lib/mnesia/src/Makefile +++ b/lib/mnesia/src/Makefile @@ -132,10 +132,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/mnesia/src/mnesia.appup.src b/lib/mnesia/src/mnesia.appup.src index 304a15242f..355aafb215 100644 --- a/lib/mnesia/src/mnesia.appup.src +++ b/lib/mnesia/src/mnesia.appup.src @@ -1,6 +1,9 @@ %% -*- erlang -*- {"%VSN%", [ + {"4.7.1", [{restart_application, mnesia}]}, + {"4.7", [{restart_application, mnesia}]}, + {"4.6", [{restart_application, mnesia}]}, {"4.5.1", [{restart_application, mnesia}]}, {"4.5", [{restart_application, mnesia}]}, {"4.4.19", [{restart_application, mnesia}]}, @@ -9,6 +12,9 @@ {"4.4.16", [{restart_application, mnesia}]} ], [ + {"4.7.1", [{restart_application, mnesia}]}, + {"4.7", [{restart_application, mnesia}]}, + {"4.6", [{restart_application, mnesia}]}, {"4.5.1", [{restart_application, mnesia}]}, {"4.5", [{restart_application, mnesia}]}, {"4.4.19", [{restart_application, mnesia}]}, diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl index 61210d7e55..37989a1958 100644 --- a/lib/mnesia/src/mnesia_index.erl +++ b/lib/mnesia/src/mnesia_index.erl @@ -120,9 +120,9 @@ del_object_bag(Tab, Key, Obj, Pos, Ixt, undefined) -> IxKey = element(Pos, Obj), Old = [X || X <- mnesia_lib:db_get(Tab, Key), element(Pos, X) =:= IxKey], del_object_bag(Tab, Key, Obj, Pos, Ixt, Old); -%% If Tab type is bag we need remove index identifier if Tab -%% contains less than 2 elements. -del_object_bag(_Tab, Key, Obj, Pos, Ixt, Old) when length(Old) < 2 -> +%% If Tab type is bag we need remove index identifier if the object being +%% deleted was the last one +del_object_bag(_Tab, Key, Obj, Pos, Ixt, Old) when Old =:= [Obj] -> del_ixes(Ixt, [Obj], Pos, Key); del_object_bag(_Tab, _Key, _Obj, _Pos, _Ixt, _Old) -> ok. diff --git a/lib/mnesia/test/Makefile b/lib/mnesia/test/Makefile index ae4c9626c7..0451da697d 100644 --- a/lib/mnesia/test/Makefile +++ b/lib/mnesia/test/Makefile @@ -52,24 +52,29 @@ MODULES= \ mnesia_cost \ mnesia_dbn_meters -MnesiaExamplesDir := ../examples +DocExamplesDir := ../doc/src/ -ExampleModules = \ +DocExampleModules = \ company \ company_o \ - bup \ - mnesia_meter \ - mnesia_tpcb -ExamplesHrl = \ + bup + +DocExamplesHrl = \ company.hrl \ company_o.hrl -ERL_FILES= $(MODULES:%=%.erl) $(ExampleModules:%=$(MnesiaExamplesDir)/%.erl) +ExamplesDir := ../examples/ + +ExampleModules = \ + mnesia_meter \ + mnesia_tpcb -HRL_FILES= mnesia_test_lib.hrl $(ExamplesHrl:%=$(MnesiaExamplesDir)/%) +ERL_FILES= $(MODULES:%=%.erl) $(DocExampleModules:%=$(DocExamplesDir)/%.erl) $(ExampleModules:%=$(ExamplesDir)/%.erl) + +HRL_FILES= mnesia_test_lib.hrl $(DocExamplesHrl:%=$(DocExamplesDir)/%) TARGET_FILES= \ - $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(ExampleModules:%=$(EBIN)/%.$(EMULATOR)) + $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(DocExampleModules:%=$(EBIN)/%.$(EMULATOR)) $(ExampleModules:%=$(EBIN)/%.$(EMULATOR)) INSTALL_PROGS= $(TARGET_FILES) @@ -91,7 +96,10 @@ EBIN = . tests debug opt: $(TARGET_FILES) -$(EBIN)/%.beam: $(MnesiaExamplesDir)/%.erl +$(EBIN)/%.beam: $(DocExamplesDir)/%.erl + $(ERLC) -bbeam $(ERL_COMPILE_FLAGS) -o$(EBIN) $< + +$(EBIN)/%.beam: $(ExamplesDir)/%.erl $(ERLC) -bbeam $(ERL_COMPILE_FLAGS) -o$(EBIN) $< clean: @@ -108,11 +116,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) mnesia.spec mnesia.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_SCRIPT) mt $(INSTALL_PROGS) $(RELSYSDIR) -# chmod -R u+w $(RELSYSDIR) -# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) mnesia.spec mnesia.cover $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_SCRIPT) mt $(INSTALL_PROGS) "$(RELSYSDIR)" +# chmod -R u+w "$(RELSYSDIR)" +# @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/mnesia/test/mnesia_consistency_test.erl b/lib/mnesia/test/mnesia_consistency_test.erl index f38e13f3a2..bb21723e27 100644 --- a/lib/mnesia/test/mnesia_consistency_test.erl +++ b/lib/mnesia/test/mnesia_consistency_test.erl @@ -100,7 +100,7 @@ groups() -> {group, updates_during_checkpoint_iteration}, {group, load_table_with_activated_checkpoint}, {group, - add_table_copy_to_table_with_activated_checkpoint}]}, + add_table_copy_to_table_checkpoint}]}, {updates_during_checkpoint_activation, [], [updates_during_checkpoint_activation_2_ram, updates_during_checkpoint_activation_2_disc, @@ -116,10 +116,10 @@ groups() -> [load_table_with_activated_checkpoint_ram, load_table_with_activated_checkpoint_disc, load_table_with_activated_checkpoint_disc_only]}, - {add_table_copy_to_table_with_activated_checkpoint, [], - [add_table_copy_to_table_with_activated_checkpoint_ram, - add_table_copy_to_table_with_activated_checkpoint_disc, - add_table_copy_to_table_with_activated_checkpoint_disc_only]}, + {add_table_copy_to_table_checkpoint, [], + [add_table_copy_to_table_checkpoint_ram, + add_table_copy_to_table_checkpoint_disc, + add_table_copy_to_table_checkpoint_disc_only]}, {backup_consistency, [], [{group, interupted_install_fallback}, {group, interupted_uninstall_fallback}, @@ -952,16 +952,16 @@ view(Source, Mod) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -add_table_copy_to_table_with_activated_checkpoint_ram(suite) -> []; -add_table_copy_to_table_with_activated_checkpoint_ram(Config) when is_list(Config) -> +add_table_copy_to_table_checkpoint_ram(suite) -> []; +add_table_copy_to_table_checkpoint_ram(Config) when is_list(Config) -> add_table_copy_to_table_with_activated_checkpoint(ram_copies, Config). -add_table_copy_to_table_with_activated_checkpoint_disc(suite) -> []; -add_table_copy_to_table_with_activated_checkpoint_disc(Config) when is_list(Config) -> +add_table_copy_to_table_checkpoint_disc(suite) -> []; +add_table_copy_to_table_checkpoint_disc(Config) when is_list(Config) -> add_table_copy_to_table_with_activated_checkpoint(disc_copies, Config). -add_table_copy_to_table_with_activated_checkpoint_disc_only(suite) -> []; -add_table_copy_to_table_with_activated_checkpoint_disc_only(Config) when is_list(Config) -> +add_table_copy_to_table_checkpoint_disc_only(suite) -> []; +add_table_copy_to_table_checkpoint_disc_only(Config) when is_list(Config) -> add_table_copy_to_table_with_activated_checkpoint(disc_only_copies, Config). add_table_copy_to_table_with_activated_checkpoint(Type,Config) -> diff --git a/lib/mnesia/test/mnesia_dirty_access_test.erl b/lib/mnesia/test/mnesia_dirty_access_test.erl index abbdab48c0..a57adefbac 100644 --- a/lib/mnesia/test/mnesia_dirty_access_test.erl +++ b/lib/mnesia/test/mnesia_dirty_access_test.erl @@ -527,6 +527,9 @@ dirty_index_update_bag(Config, Storage) -> ?match(ok, mnesia:dirty_write(Rec1)), ?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)), + ?match(ok, mnesia:dirty_delete_object(Rec5)), + ?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)), + ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec2) end)), R1 = mnesia:dirty_index_read(Tab, 2, ValPos), ?match([Rec1, Rec2], lists:sort(R1)), diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl index c040d0ca3f..73c3fe0362 100644 --- a/lib/mnesia/test/mnesia_trans_access_test.erl +++ b/lib/mnesia/test/mnesia_trans_access_test.erl @@ -896,6 +896,10 @@ index_update_bag(Config)when is_list(Config) -> ?match({atomic, [Rec1]}, mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)), + ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec5) end)), + ?match({atomic, [Rec1]}, + mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)), + ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec2) end)), {atomic, R1} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end), diff --git a/lib/observer/doc/src/Makefile b/lib/observer/doc/src/Makefile index cd9f9466ca..0f564d3299 100644 --- a/lib/observer/doc/src/Makefile +++ b/lib/observer/doc/src/Makefile @@ -133,16 +133,16 @@ 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 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile index 91a4c656ad..7135a6abd5 100644 --- a/lib/observer/src/Makefile +++ b/lib/observer/src/Makefile @@ -129,20 +129,20 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(EXECUTABLES) $(RELSYSDIR)/priv/bin - $(INSTALL_DIR) $(RELSYSDIR)/priv/crashdump_viewer - $(INSTALL_DATA) $(WEBTOOLFILES) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(GIF_FILES) $(RELSYSDIR)/priv/crashdump_viewer + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLE_FILES) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_SCRIPT) $(EXECUTABLES) "$(RELSYSDIR)/priv/bin" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/crashdump_viewer" + $(INSTALL_DATA) $(WEBTOOLFILES) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(GIF_FILES) "$(RELSYSDIR)/priv/crashdump_viewer" release_docs_spec: diff --git a/lib/observer/src/crashdump_viewer_html.erl b/lib/observer/src/crashdump_viewer_html.erl index 24a80b1916..3151b83bfb 100644 --- a/lib/observer/src/crashdump_viewer_html.erl +++ b/lib/observer/src/crashdump_viewer_html.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-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 @@ -1394,7 +1394,7 @@ timers_table(Timer) -> td("ALIGN=right",Time)]). loaded_mods_table(#loaded_mod{mod=Mod,current_size=CS,old_size=OS}) -> - tr([td(href(["loaded_mod_details?mod=",Mod],Mod)), + tr([td(href(["loaded_mod_details?mod=",http_uri:encode(Mod)],Mod)), td("ALIGN=right",CS), td("ALIGN=right",OS)]). diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src index 5c65ea5c8f..d3aaf351dd 100644 --- a/lib/observer/src/observer.app.src +++ b/lib/observer/src/observer.app.src @@ -25,6 +25,18 @@ etop_gui, etop_tr, etop_txt, + observer, + observer_app_wx, + observer_lib, + observer_perf_wx, + observer_pro_wx, + observer_procinfo, + observer_sys_wx, + observer_trace_wx, + observer_traceoptions_wx, + observer_tv_table, + observer_tv_wx, + observer_wx, ttb, ttb_et]}, {registered, []}, diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index 7eac2b8fab..380532e90c 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -147,11 +147,11 @@ setup_scrollbar({CW, CH}, AppWin, #app{dim={W0,H0}}) -> H = max(H0,CH), PPC = 20, if W0 =< CW, H0 =< CH -> - wxScrolledWindow:setScrollbars(AppWin, W, H, 1, 1); + wxScrolledWindow:setScrollbars(AppWin, W, H, 0, 0); H0 =< CH -> - wxScrolledWindow:setScrollbars(AppWin, PPC, H, W div PPC+1, 1); + wxScrolledWindow:setScrollbars(AppWin, PPC, H, W div PPC+1, 0); W0 =< CW -> - wxScrolledWindow:setScrollbars(AppWin, W, PPC, 1, H div PPC+1); + wxScrolledWindow:setScrollbars(AppWin, W, PPC, 0, H div PPC+1); true -> wxScrolledWindow:setScrollbars(AppWin, PPC, PPC, W div PPC+1, H div PPC+1) end; @@ -204,7 +204,7 @@ handle_event(#wx{id=?ID_PROC_MSG, event=#wxCommand{type=command_menu_selected}}, handle_event(#wx{id=?ID_PROC_KILL, event=#wxCommand{type=command_menu_selected}}, State = #state{panel=Panel, sel={#box{s1=#str{pid=Pid}},_}}) -> - case observer_lib:user_term(Panel, "Enter Exit Reason", "") of + case observer_lib:user_term(Panel, "Enter Exit Reason", "kill") of cancel -> ok; {ok, Term} -> exit(Pid, Term); {error, Error} -> observer_lib:display_info_dialog(Error) @@ -269,22 +269,21 @@ handle_cast(Event, _State) -> %%%%%%%%%% handle_info({active, Node}, State = #state{parent=Parent, current=Curr, appmon=Appmon}) -> create_menus(Parent, []), - {ok, Pid} = appmon_info:start_link(Node, self(), []), - case Appmon of - undefined -> ok; - Pid -> ok; - _ -> %% Deregister me as client (and stop appmon if last) - exit(Appmon, normal) - end, + Pid = try + Node = node(Appmon), + Appmon + catch _:_ -> + {ok, P} = appmon_info:start_link(Node, self(), []), + P + end, appmon_info:app_ctrl(Pid, Node, true, []), (Curr =/= undefined) andalso appmon_info:app(Pid, Curr, true, []), {noreply, State#state{appmon=Pid}}; - -handle_info(not_active, State = #state{appmon=AppMon, current=Prev}) -> +handle_info(not_active, State = #state{appmon=AppMon}) -> appmon_info:app_ctrl(AppMon, node(AppMon), false, []), - (Prev =/= undefined) andalso appmon_info:app(AppMon, Prev, false, []), - {noreply, State}; - + lists:member(node(AppMon), nodes()) andalso exit(AppMon, normal), + observer_wx:set_status(""), + {noreply, State#state{appmon=undefined}}; handle_info({delivery, Pid, app_ctrl, _, Apps0}, State = #state{appmon=Pid, apps_w=LBox, current=Curr0}) -> Apps = [atom_to_list(App) || {_, App, {_, _, _}} <- Apps0], @@ -341,6 +340,7 @@ handle_mouse_click(Node = {#box{s1=#str{pid=Pid}},_}, Type, right_down -> popup_menu(Panel); _ -> ok end, + observer_wx:set_status(io_lib:format("Pid: ~p", [Pid])), wxWindow:refresh(AppWin), State#state{sel=Node}; handle_mouse_click(_, _, State = #state{sel=undefined}) -> @@ -349,6 +349,7 @@ handle_mouse_click(_, right_down, State=#state{panel=Panel}) -> popup_menu(Panel), State; handle_mouse_click(_, _, State=#state{app_w=AppWin}) -> + observer_wx:set_status(""), wxWindow:refresh(AppWin), State#state{sel=undefined}. @@ -376,10 +377,11 @@ popup_menu(Panel) -> wxMenu:append(Menu, ?ID_TRACE_NAME, "Trace named process"), wxMenu:append(Menu, ?ID_TRACE_TREE_PIDS, "Trace process tree"), wxMenu:append(Menu, ?ID_TRACE_TREE_NAMES, "Trace named process tree"), + wxMenu:append(Menu, ?ID_PROC_MSG, "Send Msg"), + wxMenu:append(Menu, ?ID_PROC_KILL, "Kill process"), wxWindow:popupMenu(Panel, Menu), wxMenu:destroy(Menu). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% locate_node(X, _Y, [{Box=#box{x=BX}, _Chs}|_Rest]) when X < BX -> diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl index 3b924d46cf..4077f8371a 100644 --- a/lib/observer/src/observer_lib.erl +++ b/lib/observer/src/observer_lib.erl @@ -19,7 +19,7 @@ -module(observer_lib). -export([get_wx_parent/1, - display_info_dialog/1, user_term/3, + display_info_dialog/1, user_term/3, user_term_multiline/3, interval_dialog/4, start_timer/1, stop_timer/1, display_info/2, fill_info/2, update_info/2, to_str/1, create_menus/3, create_menu_item/3, @@ -347,6 +347,58 @@ user_term(Parent, Title, Default) -> cancel end. +user_term_multiline(Parent, Title, Default) -> + Dialog = wxDialog:new(Parent, ?wxID_ANY, Title, + [{style, ?wxDEFAULT_DIALOG_STYLE bor + ?wxRESIZE_BORDER}]), + Panel = wxPanel:new(Dialog), + + TextCtrl = wxTextCtrl:new(Panel, ?wxID_ANY, + [{value, Default}, + {style, ?wxDEFAULT bor ?wxTE_MULTILINE}]), + Line = wxStaticLine:new(Panel, [{style, ?wxLI_HORIZONTAL}]), + + Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL), + + InnerSizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(InnerSizer, TextCtrl, + [{flag, ?wxEXPAND bor ?wxALL},{proportion, 1},{border, 5}]), + wxSizer:add(InnerSizer, Line, + [{flag, ?wxEXPAND},{proportion, 0},{border, 5}]), + wxPanel:setSizer(Panel, InnerSizer), + + TopSizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(TopSizer, Panel, + [{flag, ?wxEXPAND bor ?wxALL},{proportion, 1},{border, 5}]), + wxSizer:add(TopSizer, Buttons, + [{flag, ?wxEXPAND bor ?wxBOTTOM bor ?wxRIGHT},{border, 10}]), + + % calculate the size of TopSizer when the whole user_term + % fits in the TextCtrl + DC = wxClientDC:new(Panel), + W = wxDC:getCharWidth(DC), + H = wxDC:getCharHeight(DC), + {EW, EH} = wxDC:getMultiLineTextExtent(DC, Default), + wxSizer:setItemMinSize(InnerSizer, 0, EW+2*W, EH+H), + TopSize = wxSizer:getMinSize(TopSizer), + % reset min size of TextCtrl to 40 chararacters * 4 lines + wxSizer:setItemMinSize(InnerSizer, 0, 40*W, 4*H), + + wxWindow:setSizerAndFit(Dialog, TopSizer), + wxSizer:setSizeHints(TopSizer, Dialog), + + wxWindow:setClientSize(Dialog, TopSize), + + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + Str = wxTextCtrl:getValue(TextCtrl), + wxDialog:destroy(Dialog), + parse_string(ensure_last_is_dot(Str)); + ?wxID_CANCEL -> + wxDialog:destroy(Dialog), + cancel + end. + parse_string(Str) -> try Tokens = case erl_scan:string(Str) of diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index fa867e12f6..abf90ac612 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -123,7 +123,7 @@ init([Notebook, Parent]) -> }} catch _:Err -> io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]), - {error, Err} + {stop, Err} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index 7578215ff9..ee67664539 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-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 @@ -191,13 +191,20 @@ dump_to_file(Parent, FileName, Holder) -> start_procinfo(undefined, _Frame, Opened) -> Opened; start_procinfo(Pid, Frame, Opened) -> - case lists:member(Pid, Opened) of - true -> - Opened; - false -> - observer_procinfo:start(Pid, Frame, self()), - [Pid | Opened] + %% This code doesn't work until we collect which windows have been + %% closed maybe it should moved to observer_wx.erl + %% and add a global menu which remembers windows. + %% case lists:keyfind(Pid, 1, Opened) of + %% false -> + case observer_procinfo:start(Pid, Frame, self()) of + {error, _} -> Opened; + PI -> [{Pid, PI} | Opened] end. + %%; + %% {_, PI} -> + %% wxFrame:raise(PI), + %% Opened + %% end. call(Holder, What) -> Ref = erlang:monitor(process, Holder), @@ -251,8 +258,7 @@ terminate(_Reason, #state{holder=Holder}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. - + {ok, State}. handle_call(Msg, _From, State) -> io:format("~p:~p: Unhandled call ~p~n",[?MODULE, ?LINE, Msg]), diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl index ec08d3aff1..45218c177b 100644 --- a/lib/observer/src/observer_procinfo.erl +++ b/lib/observer/src/observer_procinfo.erl @@ -49,7 +49,8 @@ init([Pid, ParentFrame, Parent]) -> try Title=case observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, registered_name]) of [] -> io_lib:format("~p",[Pid]); - {registered_name, Registered} -> atom_to_list(Registered) + {registered_name, Registered} -> io_lib:format("~p (~p)",[Registered, Pid]); + undefined -> throw(process_undefined) end, Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [atom_to_list(node(Pid)), $:, Title], [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {850,600}}]), @@ -75,7 +76,10 @@ init([Pid, ParentFrame, Parent]) -> }} catch error:{badrpc, _} -> observer_wx:return_to_localnode(ParentFrame, node(Pid)), - {stop, badrpc, #state{parent=Parent, pid=Pid}} + {stop, badrpc}; + process_undefined -> + observer_lib:display_info_dialog("No such alive process"), + {stop, normal} end. init_panel(Notebook, Str, Pid, Fun) -> @@ -94,8 +98,11 @@ handle_event(#wx{event=#wxClose{type=close_window}}, State) -> handle_event(#wx{id=?wxID_CLOSE, event=#wxCommand{type=command_menu_selected}}, State) -> {stop, normal, State}; -handle_event(#wx{id=?REFRESH}, #state{pages=Pages}=State) -> - [(W#worker.callback)() || W <- Pages], +handle_event(#wx{id=?REFRESH}, #state{frame=Frame, pid=Pid, pages=Pages}=State) -> + try [(W#worker.callback)() || W <- Pages] + catch process_undefined -> + wxFrame:setTitle(Frame, io_lib:format("*DEAD* ~p",[Pid])) + end, {noreply, State}; handle_event(Event, _State) -> @@ -120,17 +127,15 @@ terminate(_Reason, #state{parent=Parent,pid=Pid,frame=Frame}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_process_page(Panel, Pid) -> Fields0 = process_info_fields(Pid), {FPanel, _, UpFields} = observer_lib:display_info(Panel, Fields0), - {FPanel, fun() -> case process_info_fields(Pid) of - Fields when is_list(Fields) -> - observer_lib:update_info(UpFields, Fields); - _ -> ok - end + {FPanel, fun() -> + Fields = process_info_fields(Pid), + observer_lib:update_info(UpFields, Fields) end}. init_text_page(Parent) -> @@ -162,7 +167,8 @@ init_message_page(Parent, Pid) -> false -> wxTextCtrl:writeText(Text, Messages) end; - _ -> ok + _ -> + throw(process_undefined) end end, Update(), @@ -178,7 +184,8 @@ init_dict_page(Parent, Pid) -> Last = wxTextCtrl:getLastPosition(Text), wxTextCtrl:remove(Text, 0, Last), wxTextCtrl:writeText(Text, Dict); - _ -> ok + _ -> + throw(process_undefined) end end, Update(), @@ -216,7 +223,8 @@ init_stack_page(Parent, Pid) -> wxListCtrl:setItem(LCtrl, Row, 1, FileLine), Row+1 end, 0, RawBt); - _ -> ok + _ -> + throw(process_undefined) end end, Resize = fun(#wx{event=#wxSize{size={W,_}}},Ev) -> @@ -266,7 +274,7 @@ process_info_fields(Pid) -> RawInfo when is_list(RawInfo) -> observer_lib:fill_info(Struct, RawInfo); _ -> - ok + throw(process_undefined) end. item_list() -> diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl index 09602bbd9e..f00a666a35 100644 --- a/lib/observer/src/observer_sys_wx.erl +++ b/lib/observer/src/observer_sys_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-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 @@ -147,7 +147,7 @@ terminate(_Reason, _State) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. handle_call(Msg, _From, State) -> io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index d0b6a1e063..f2a1084f85 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-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 @@ -489,7 +489,7 @@ terminate(_Reason, #state{nodes=_Nodes}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_view=Fview}) -> diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index 6a634e06f0..e27f565abc 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-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 @@ -167,8 +167,10 @@ module_selector(Parent, Node) -> function_selector(Parent, Node, Module) -> Functions = observer_wx:try_rpc(Node, Module, module_info, [functions]), - Choices = lists:sort([{Name, Arity} || {Name, Arity} <- Functions, - not(erl_internal:guard_bif(Name, Arity))]), + Externals = observer_wx:try_rpc(Node, Module, module_info, [exports]), + + Choices = lists:usort([{Name, Arity} || {Name, Arity} <- Externals ++ Functions, + not(erl_internal:guard_bif(Name, Arity))]), ParsedChoices = parse_function_names(Choices), case check_selector(Parent, ParsedChoices) of [] -> [{Module, '_', '_'}]; diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl index f432173f57..c41f0f006a 100644 --- a/lib/observer/src/observer_tv_table.erl +++ b/lib/observer/src/observer_tv_table.erl @@ -24,6 +24,8 @@ -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_sync_event/3, handle_cast/2]). +-export([format/1]). + -include("observer_defs.hrl"). -import(observer_lib, [to_str/1]). @@ -218,8 +220,8 @@ search_area(Parent) -> search=TC1,goto=TC2,radio={Nbtn,Pbtn,Cbtn}}. edit(Index, #state{pid=Pid, frame=Frame}) -> - Str = get_row(Pid, Index, all), - case observer_lib:user_term(Frame, "Edit object:", Str) of + Str = get_row(Pid, Index, all_multiline), + case observer_lib:user_term_multiline(Frame, "Edit object:", Str) of cancel -> ok; {ok, Term} -> Pid ! {edit, Index, Term}; Err = {error, _} -> self() ! Err @@ -265,7 +267,8 @@ handle_event(#wx{id=?ID_DELETE}, wxStatusBar:setStatusText(StatusBar, io_lib:format("Deleted object: ~s",[Str])), {noreply, State}; -handle_event(#wx{id=?wxID_CLOSE}, State) -> +handle_event(#wx{id=?wxID_CLOSE}, State = #state{frame=Frame}) -> + wxFrame:destroy(Frame), {stop, normal, State}; handle_event(Help = #wx{id=?wxID_HELP}, State) -> @@ -321,7 +324,7 @@ handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{type=command_text_enter,cmdS wxStatusBar:setStatusText(SB, "Not found"), Pid ! {mark_search_hit, Find#find.start}, wxListCtrl:refreshItem(Grid, Find#find.start), - {noreply, State#state{search=Search#search{find=#find{found=false}}}}; + {noreply, State#state{search=Search#search{find=Find#find{found=false}}}}; Row -> wxListCtrl:ensureVisible(Grid, Row), wxListCtrl:refreshItem(Grid, Row), @@ -453,7 +456,7 @@ get_attr(Table, Item) -> Ref = erlang:monitor(process, Table), Table ! {get_attr, self(), Item}, receive - {'DOWN', Ref, _, _, _} -> ""; + {'DOWN', Ref, _, _, _} -> wx:null(); {Table, Res} -> erlang:demonitor(Ref), Res @@ -594,7 +597,7 @@ keysort(Col, Table) -> lists:sort(Sort, Table). search([Str, Row, Dir0, CaseSens], - S=#holder{parent=Parent, table=Table}) -> + S=#holder{parent=Parent, table=Table0}) -> Opt = case CaseSens of true -> []; false -> [caseless] @@ -605,29 +608,35 @@ search([Str, Row, Dir0, CaseSens], end, Res = case re:compile(Str, Opt) of {ok, Re} -> + Table = + case Dir0 of + true -> + lists:nthtail(Row, Table0); + false -> + lists:reverse(lists:sublist(Table0, Row+1)) + end, search(Row, Dir, Re, Table); {error, _} -> false end, Parent ! {self(), Res}, S#holder{search=Res}. -search(Row, Dir, Re, Table) -> - Res = try lists:nth(Row+1, Table) of - Term -> - Str = format(Term), - re:run(Str, Re) - catch _:_ -> no_more - end, +search(Row, Dir, Re, [ [Term|_] |Table]) -> + Str = format(Term), + Res = re:run(Str, Re), case Res of nomatch -> search(Row+Dir, Dir, Re, Table); - no_more -> false; {match,_} -> Row - end. + end; +search(_, _, _, []) -> + false. get_row(From, Row, Col, Table) -> case lists:nth(Row+1, Table) of [Object|_] when Col =:= all -> From ! {self(), format(Object)}; + [Object|_] when Col =:= all_multiline -> + From ! {self(), io_lib:format("~p", [Object])}; [Object|_] when tuple_size(Object) >= Col -> From ! {self(), format(element(Col, Object))}; _ -> @@ -747,6 +756,13 @@ format(List) when is_list(List) -> format_list(List); format(Bin) when is_binary(Bin), byte_size(Bin) > 100 -> io_lib:format("<<#Bin:~w>>", [byte_size(Bin)]); +format(Bin) when is_binary(Bin) -> + try + true = printable_list(unicode:characters_to_list(Bin)), + io_lib:format("<<\"~ts\">>", [Bin]) + catch _:_ -> + io_lib:format("~w", [Bin]) + end; format(Float) when is_float(Float) -> io_lib:format("~.3g", [Float]); format(Term) -> @@ -762,7 +778,7 @@ format_tuple(_Tuple, 1, 0) -> format_list([]) -> "[]"; format_list(List) -> case printable_list(List) of - true -> io_lib:format("\"~ts\"", [List]); + true -> io_lib:format("\"~ts\"", [map_printable_list(List)]); false -> [$[ | make_list(List)] end. @@ -771,6 +787,24 @@ make_list([Last]) -> make_list([Head|Tail]) -> [format(Head), $,|make_list(Tail)]. +map_printable_list([$\n|Cs]) -> + [$\\, $n|map_printable_list(Cs)]; +map_printable_list([$\r|Cs]) -> + [$\\, $r|map_printable_list(Cs)]; +map_printable_list([$\t|Cs]) -> + [$\\, $t|map_printable_list(Cs)]; +map_printable_list([$\v|Cs]) -> + [$\\, $v|map_printable_list(Cs)]; +map_printable_list([$\b|Cs]) -> + [$\\, $b|map_printable_list(Cs)]; +map_printable_list([$\f|Cs]) -> + [$\\, $f|map_printable_list(Cs)]; +map_printable_list([$\e|Cs]) -> + [$\\, $e|map_printable_list(Cs)]; +map_printable_list([]) -> []; +map_printable_list([C|Cs]) -> + [C|map_printable_list(Cs)]. + %% printable_list([Char]) -> bool() %% Return true if CharList is a list of printable characters, else %% false. diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index e2b256d768..e433bea8c2 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -20,7 +20,7 @@ -behaviour(wx_object). -export([start/0]). --export([create_menus/2, get_attrib/1, get_tracer/0, +-export([create_menus/2, get_attrib/1, get_tracer/0, set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). -export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, @@ -58,7 +58,8 @@ perf_panel, active_tab, node, - nodes + nodes, + prev_node="" }). start() -> @@ -73,6 +74,9 @@ create_menus(Object, Menus) when is_list(Menus) -> get_attrib(What) -> wx_object:call(observer, {get_attrib, What}). +set_status(What) -> + wx_object:cast(observer, {status_bar, What}). + get_tracer() -> wx_object:call(observer, get_tracer). @@ -191,10 +195,13 @@ setup(#state{frame = Frame} = State) -> %%Callbacks handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changing}}, #state{active_tab=Previous, node=Node} = State) -> - Pid = get_active_pid(State), - Previous ! not_active, - Pid ! {active, Node}, - {noreply, State#state{active_tab=Pid}}; + case get_active_pid(State) of + Previous -> {noreply, State}; + Pid -> + Previous ! not_active, + Pid ! {active, Node}, + {noreply, State#state{active_tab=Pid}} + end; handle_event(#wx{event = #wxClose{}}, State) -> {stop, normal, State}; @@ -258,20 +265,21 @@ handle_event(#wx{id = ?ID_CONNECT, event = #wxCommand{type = command_menu_select handle_event(#wx{id = ?ID_PING, event = #wxCommand{type = command_menu_selected}}, #state{frame = Frame} = State) -> UpdState = case create_connect_dialog(ping, State) of - cancel -> State; + cancel -> State; {value, Value} when is_list(Value) -> try Node = list_to_atom(Value), case net_adm:ping(Node) of pang -> create_txt_dialog(Frame, "Connect failed", "Pang", ?wxICON_EXCLAMATION), - State; + State#state{prev_node=Value}; pong -> - change_node_view(Node, State) + State1 = change_node_view(Node, State), + State1#state{prev_node=Value} end catch _:_ -> create_txt_dialog(Frame, "Connect failed", "Pang", ?wxICON_EXCLAMATION), - State + State#state{prev_node=Value} end end, {noreply, UpdState}; @@ -288,6 +296,10 @@ handle_event(Event, State) -> Pid ! Event, {noreply, State}. +handle_cast({status_bar, Msg}, State=#state{status_bar=SB}) -> + wxStatusBar:setStatusText(SB, Msg), + {noreply, State}; + handle_cast(_Cast, State) -> {noreply, State}. @@ -341,7 +353,7 @@ terminate(_Reason, #state{frame = Frame}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -401,7 +413,9 @@ connect2(NodeName, Opts, Cookie) -> end. change_node_view(Node, State) -> - get_active_pid(State) ! {active, Node}, + Tab = get_active_pid(State), + Tab ! not_active, + Tab ! {active, Node}, StatusText = ["Observer - " | atom_to_list(Node)], wxFrame:setTitle(State#state.frame, StatusText), wxStatusBar:setStatusText(State#state.status_bar, StatusText), @@ -439,8 +453,8 @@ pid2panel(Pid, #state{pro_panel=Pro, sys_panel=Sys, end. -create_connect_dialog(ping, #state{frame = Frame}) -> - Dialog = wxTextEntryDialog:new(Frame, "Connect to node"), +create_connect_dialog(ping, #state{frame = Frame, prev_node=Prev}) -> + Dialog = wxTextEntryDialog:new(Frame, "Connect to node", [{value, Prev}]), case wxDialog:showModal(Dialog) of ?wxID_OK -> Value = wxTextEntryDialog:getValue(Dialog), @@ -560,7 +574,16 @@ remove_menu_items([], _MB) -> ok. get_nodes() -> - Nodes = [node()| nodes()], + Nodes0 = case erlang:is_alive() of + false -> []; + true -> + case net_adm:names() of + {error, _} -> nodes(); + {ok, Names} -> + epmd_nodes(Names) ++ nodes() + end + end, + Nodes = lists:usort(Nodes0), {_, Menues} = lists:foldl(fun(Node, {Id, Acc}) when Id < ?LAST_NODES_MENU_ID -> {Id + 1, [#create_menu{id=Id + ?FIRST_NODES_MENU_ID, @@ -568,6 +591,10 @@ get_nodes() -> end, {1, []}, Nodes), {Nodes, lists:reverse(Menues)}. +epmd_nodes(Names) -> + [_, Host] = string:tokens(atom_to_list(node()),"@"), + [list_to_atom(Name ++ [$@|Host]) || {Name, _} <- Names]. + update_node_list(State = #state{menubar=MenuBar}) -> {Nodes, NodesMenuItems} = get_nodes(), NodeMenuId = wxMenuBar:findMenu(MenuBar, "Nodes"), diff --git a/lib/observer/test/Makefile b/lib/observer/test/Makefile index bf99f07081..9df0591da5 100644 --- a/lib/observer/test/Makefile +++ b/lib/observer/test/Makefile @@ -82,11 +82,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) observer.spec $(EMAKEFILE) \ $(COVERFILE) $(ERL_FILES) \ - $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl index 5bbce9d076..6f882d0be9 100644 --- a/lib/observer/test/crashdump_viewer_SUITE.erl +++ b/lib/observer/test/crashdump_viewer_SUITE.erl @@ -414,6 +414,10 @@ special(Port,File) -> _ -> ok end; + ".strangemodname" -> + AllMods = contents(Port,"loaded_modules"), + open_all_modules(Port,AllMods), + ok; %%! No longer needed - all atoms are shown on one page!! %% ".250atoms" -> %% Html1 = contents(Port,"atoms"), @@ -496,6 +500,26 @@ expand_binary_link(Html) -> expand_binary_link(T) end. +open_all_modules(Port,Modules) -> + case get_first_module(Modules) of + {Module,Rest} -> + ModuleDetails = contents(Port,"loaded_mod_details?mod=" ++ Module), + ModTitle = http_uri:decode(Module), + ModTitle = title(ModuleDetails), + open_all_modules(Port,Rest); + false -> + ok + end. + +get_first_module([]) -> + false; +get_first_module(Html) -> + case Html of + "<TD><A HREF=\"loaded_mod_details?mod=" ++ Rest -> + {string:sub_word(Rest,1,$"),Rest}; + [_H|T] -> + get_first_module(T) + end. %% next_link(Html) -> %% case Html of @@ -565,7 +589,7 @@ create_dumps(DataDir,[Rel|Rels],Acc) -> Fun = fun() -> do_create_dumps(DataDir,Rel) end, Pa = filename:dirname(code:which(?MODULE)), {SlAllocDumps,Dumps,DosDump} = - ?t:run_on_shielded_node(Fun, compat_rel(Rel) ++ "-pa " ++ Pa), + ?t:run_on_shielded_node(Fun, compat_rel(Rel) ++ "-pa \"" ++ Pa ++ "\""), create_dumps(DataDir,Rels,SlAllocDumps ++ Dumps ++ Acc ++ DosDump); create_dumps(_DataDir,[],Acc) -> Acc. @@ -590,7 +614,8 @@ do_create_dumps(DataDir,Rel) -> case Rel of current -> CD3 = dump_with_args(DataDir,Rel,"instr","+Mim true"), - {SlAllocDumps, [CD1,CD2,CD3], DosDump}; + CD4 = dump_with_strange_module_name(DataDir,Rel,"strangemodname"), + {SlAllocDumps, [CD1,CD2,CD3,CD4], DosDump}; _ -> {SlAllocDumps, [CD1,CD2], DosDump} end. @@ -600,7 +625,7 @@ do_create_dumps(DataDir,Rel) -> %% not connected node, and with monitors and links between nodes. full_dist_dump(DataDir,Rel) -> Opt = rel_opt(Rel), - Pz = "-pz " ++ filename:dirname(code:which(?MODULE)), + Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"", PzOpt = [{args,Pz}], {ok,N1} = ?t:start_node(n1,peer,Opt ++ PzOpt), {ok,N2} = ?t:start_node(n2,peer,Opt ++ PzOpt), @@ -648,7 +673,22 @@ dump_with_args(DataDir,Rel,DumpName,Args) -> ?t:stop_node(n1), CD. +%% This dump is added to test OTP-10090 - regarding URL encoding of +%% module names in the module detail link. +dump_with_strange_module_name(DataDir,Rel,DumpName) -> + Opt = rel_opt(Rel), + {ok,N1} = ?t:start_node(n1,peer,Opt), + Mod = '<mod ule#with?strange%name>', + File = atom_to_list(Mod) ++ ".erl", + Forms = [{attribute,1,file,{File,1}}, + {attribute,1,module,Mod}, + {eof,4}], + {ok,Mod,Bin} = rpc:call(N1,compile,forms,[Forms,[binary]]), + {module,Mod} = rpc:call(N1,code,load_binary,[Mod,File,Bin]), + CD = dump(N1,DataDir,Rel,DumpName), + ?t:stop_node(n1), + CD. dump(Node,DataDir,Rel,DumpName) -> rpc:call(Node,erlang,halt,[DumpName]), diff --git a/lib/odbc/aclocal.m4 b/lib/odbc/aclocal.m4 index 339a15a2bb..a76594d86f 100644 --- a/lib/odbc/aclocal.m4 +++ b/lib/odbc/aclocal.m4 @@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us dnl Cross compilation variables AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)]) +AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)]) @@ -606,6 +607,103 @@ ifelse([$5], , , [$5 fi ]) +dnl ---------------------------------------------------------------------- +dnl +dnl AC_DOUBLE_MIDDLE_ENDIAN +dnl +dnl Checks whether doubles are represented in "middle-endian" format. +dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly, +dnl as well as DOUBLE_MIDDLE_ENDIAN. +dnl +dnl + +AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN], +[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian, +[# It does not; compile a test program. +AC_RUN_IFELSE( +[AC_LANG_SOURCE([[#include <stdlib.h> + +int +main(void) +{ + int i = 0; + int zero = 0; + int bigendian; + int zero_index = 0; + + union + { + long int l; + char c[sizeof (long int)]; + } u; + + /* we'll use the one with 32-bit words */ + union + { + double d; + unsigned int c[2]; + } vint; + + union + { + double d; + unsigned long c[2]; + } vlong; + + union + { + double d; + unsigned short c[2]; + } vshort; + + + /* Are we little or big endian? From Harbison&Steele. */ + u.l = 1; + bigendian = (u.c[sizeof (long int) - 1] == 1); + + zero_index = bigendian ? 1 : 0; + + vint.d = 1.0; + vlong.d = 1.0; + vshort.d = 1.0; + + if (sizeof(unsigned int) == 4) + { + if (vint.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned long) == 4) + { + if (vlong.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned short) == 4) + { + if (vshort.c[zero_index] != 0) + zero = 1; + } + + exit (zero); +} +]])], + [ac_cv_c_double_middle_endian=no], + [ac_cv_c_double_middle_endian=yes], + [ac_cv_c_double_middle=unknown])]) +case $ac_cv_c_double_middle_endian in + yes) + m4_default([$1], + [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1, + [Define to 1 if your processor stores the words in a double in + middle-endian format (like some ARMs).])]) ;; + no) + $2 ;; + *) + m4_default([$3], + [AC_MSG_WARN([unknown double endianness +presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;; +esac +])# AC_C_DOUBLE_MIDDLE_ENDIAN + dnl ---------------------------------------------------------------------- dnl @@ -1337,6 +1435,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian]) fi +case X$erl_xcomp_double_middle_endian in + X) ;; + Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;; + *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);; +esac + +AC_C_DOUBLE_MIDDLE_ENDIAN + AC_ARG_ENABLE(native-ethr-impls, AS_HELP_STRING([--disable-native-ethr-impls], [disable native ethread implementations]), diff --git a/lib/odbc/c_src/Makefile.in b/lib/odbc/c_src/Makefile.in index 3a96a53ef8..5b37b352fb 100644 --- a/lib/odbc/c_src/Makefile.in +++ b/lib/odbc/c_src/Makefile.in @@ -128,12 +128,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt ifdef EXE_TARGET - $(INSTALL_DIR) $(RELSYSDIR)/c_src - $(INSTALL_DATA) $(C_FILES) $(H_FILES) $(RELSYSDIR)/c_src - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_DIR) $(RELSYSDIR)/priv/obj - $(INSTALL_PROGRAM) $(EXE_TARGET) $(RELSYSDIR)/priv/bin + $(INSTALL_DIR) "$(RELSYSDIR)/c_src" + $(INSTALL_DATA) $(C_FILES) $(H_FILES) "$(RELSYSDIR)/c_src" + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/obj" + $(INSTALL_PROGRAM) $(EXE_TARGET) "$(RELSYSDIR)/priv/bin" endif release_docs_spec: diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index ab2d7fe210..6d4460014f 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -176,7 +176,7 @@ static void encode_column_dyn(db_column column, int column_nr, static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size, SQLSMALLINT decimal_digits, db_state *state); static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params, - int i, int j); + int i, int j, int num_param_values); /*------------- Erlang port communication functions ----------------------*/ @@ -212,6 +212,7 @@ static db_column * alloc_column_buffer(int n); static void free_column_buffer(db_column **columns, int n); static void free_params(param_array **params, int cols); static void clean_state(db_state *state); +static SQLLEN* alloc_strlen_indptr(int n, int val); /* ------------- Init/map/bind/retrive functions -------------------------*/ @@ -1157,7 +1158,7 @@ static db_result_msg encode_out_params(db_state *state, break; case SQL_C_BIT: ei_x_encode_atom(&dynamic_buffer(state), - ((Boolean*)values)[j]==TRUE?"true":"false"); + ((byte*)values)[j]==TRUE?"true":"false"); break; default: ei_x_encode_atom(&dynamic_buffer(state), "error"); @@ -1579,37 +1580,48 @@ static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size, } static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params, - int i, int j) + int i, int j, int num_param_values) { int erl_type, size; long bin_size, l64; long val; param_array* param; TIMESTAMP_STRUCT* ts; + char atomarray[MAXATOMLEN+1]; ei_get_type(buffer, index, &erl_type, &size); param = &(*params)[i]; + if(erl_type == ERL_ATOM_EXT) { + ei_decode_atom(buffer, index, atomarray); + if(strncmp(atomarray, "null", 4) == 0 ) { + param->offset += param->type.len; + + if(!param->type.strlen_or_indptr_array) + param->type.strlen_or_indptr_array = alloc_strlen_indptr(num_param_values, param->type.len); + + param->type.strlen_or_indptr_array[j] = SQL_NULL_DATA; + return TRUE; + } + } + switch (param->type.c) { case SQL_C_CHAR: if (binary_strings(state)) { ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size); param->offset += param->type.len; - param->type.strlen_or_indptr_array[j] = SQL_NTS; } else { if(erl_type != ERL_STRING_EXT) { return FALSE; } ei_decode_string(buffer, index, &(param->values.string[param->offset])); param->offset += param->type.len; - param->type.strlen_or_indptr_array[j] = SQL_NTS; } break; case SQL_C_WCHAR: ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size); param->offset += param->type.len; - param->type.strlen_or_indptr_array[j] = SQL_NTS; break; case SQL_C_TYPE_TIMESTAMP: ts = (TIMESTAMP_STRUCT*) param->values.string; @@ -1661,9 +1673,13 @@ static Boolean decode_params(db_state *state, byte *buffer, int *index, param_ar if((erl_type != ERL_ATOM_EXT)) { return FALSE; } - ei_decode_boolean(buffer, index, &(param->values.bool[j])); + if (strncmp((char*)atomarray,"true",4) == 0) + param->values.bool[j] = TRUE; + else if (strncmp((char*)atomarray,"false",5) == 0) + param->values.bool[j] = FALSE; + else + return -1; break; - default: return FALSE; } @@ -2014,6 +2030,18 @@ static void clean_state(db_state *state) nr_of_columns(state) = 0; } +/* Allocates and fill with default value StrLen_or_IndPtr array */ +static SQLLEN* alloc_strlen_indptr(int n, int val) +{ + int i; + SQLLEN* arr = (SQLLEN*)safe_malloc(n * sizeof(SQLLEN)); + + for( i=0; i < n; ++i ) + arr[i] = val; + + return arr; +} + /* ------------- Init/map/bind/retrive functions ------------------------*/ /* Prepare the state for a connection */ @@ -2118,7 +2146,7 @@ static void init_param_column(param_array *params, byte *buffer, int *index, (double *)safe_malloc(num_param_values * params->type.len); } else if(params->type.c == SQL_C_CHAR) { params->type.strlen_or_indptr_array - = (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER)); + = alloc_strlen_indptr(num_param_values, SQL_NTS); params->values.string = (byte *)safe_malloc(num_param_values * sizeof(byte)* params->type.len); @@ -2136,8 +2164,8 @@ static void init_param_column(param_array *params, byte *buffer, int *index, params->type.len = length+1; params->type.c = SQL_C_CHAR; params->type.col_size = (SQLUINTEGER)length; - params->type.strlen_or_indptr_array = - (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER)); + params->type.strlen_or_indptr_array + = alloc_strlen_indptr(num_param_values, SQL_NTS); params->values.string = (byte *)safe_malloc(num_param_values * sizeof(byte)* params->type.len); @@ -2159,8 +2187,8 @@ static void init_param_column(param_array *params, byte *buffer, int *index, params->type.len = (length+1)*sizeof(SQLWCHAR); params->type.c = SQL_C_WCHAR; params->type.col_size = (SQLUINTEGER)length; - params->type.strlen_or_indptr_array = - (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER)); + params->type.strlen_or_indptr_array + = alloc_strlen_indptr(num_param_values, SQL_NTS); params->values.string = (byte *)safe_malloc(num_param_values * sizeof(byte) * params->type.len); @@ -2201,10 +2229,10 @@ static void init_param_column(param_array *params, byte *buffer, int *index, case USER_BOOLEAN: params->type.sql = SQL_BIT; params->type.c = SQL_C_BIT; - params->type.len = sizeof(Boolean); + params->type.len = sizeof(byte); params->type.col_size = params->type.len; params->values.bool = - (Boolean *)safe_malloc(num_param_values * params->type.len); + (byte *)safe_malloc(num_param_values * params->type.len); break; } params->offset = 0; @@ -2411,7 +2439,7 @@ static param_array * bind_parameter_arrays(byte *buffer, int *index, } for (j = 0; j < num_param_values; j++) { - if(!decode_params(state, buffer, index, ¶ms, i, j)) { + if(!decode_params(state, buffer, index, ¶ms, i, j, num_param_values)) { /* An input parameter was not of the expected type */ free_params(¶ms, i); return params; diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h index 56b6148777..a76cedf1af 100644 --- a/lib/odbc/c_src/odbcserver.h +++ b/lib/odbc/c_src/odbcserver.h @@ -156,7 +156,7 @@ typedef struct { byte *string; SQLINTEGER *integer; double *floating; - Boolean *bool; + byte *bool; }values; } param_array; diff --git a/lib/odbc/doc/src/Makefile b/lib/odbc/doc/src/Makefile index 0d456085f3..cd9bca0a55 100644 --- a/lib/odbc/doc/src/Makefile +++ b/lib/odbc/doc/src/Makefile @@ -125,13 +125,13 @@ debug opt: 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/odbc/src/Makefile b/lib/odbc/src/Makefile index b48dd768c8..7ca82d784b 100644 --- a/lib/odbc/src/Makefile +++ b/lib/odbc/src/Makefile @@ -108,13 +108,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXT_HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \ - $(RELSYSDIR)/ebin + "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src index 853323da09..c7c83ea079 100644 --- a/lib/odbc/src/odbc.appup.src +++ b/lib/odbc/src/odbc.appup.src @@ -1,12 +1,8 @@ %% -*- erlang -*- {"%VSN%", [ - {"2.10.11", [{restart_application, odbc}]}, - {"2.10.10", [{restart_application, odbc}]}, - {"2.10.9", [{restart_application, odbc}]} + {<<"2\\.*">>, [{restart_application, odbc}]} ], [ - {"2.10.11", [{restart_application, odbc}]}, - {"2.10.10", [{restart_application, odbc}]}, - {"2.10.9", [{restart_application, odbc}]} + {<<"2\\.*">>, [{restart_application, odbc}]} ]}. diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl index 36afd1abcf..9633b85cb2 100644 --- a/lib/odbc/src/odbc.erl +++ b/lib/odbc/src/odbc.erl @@ -451,7 +451,7 @@ init(Args) -> %% Start the port program (a c program) that utilizes the odbc driver case os:find_executable(?SERVERPROG, ?SERVERDIR) of FileName when is_list(FileName)-> - Port = open_port({spawn, FileName}, + Port = open_port({spawn, "\""++FileName++"\""}, [{packet, ?LENGTH_INDICATOR_SIZE}, binary, exit_status]), State = #state{listen_sockets = @@ -755,7 +755,10 @@ handle_info({'DOWN', _Ref, _Type, _Process, shutdown}, State) -> handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) -> {stop, {stopped, {'EXIT', Process, Reason}}, State#state{reply_to = undefined}}; - + +handle_info({tcp_closed, Socket}, State = #state{odbc_socket=Socket, + state = disconnecting}) -> + {stop, normal, State}; %--------------------------------------------------------------------------- %% Catch all - throws away unknown messages (This could happen by "accident" %% so we do not want to crash, but we make a log entry as it is an @@ -942,9 +945,11 @@ fix_params({sql_bit, InOut, Values}) -> fix_params({'sql_timestamp', InOut, Values}) -> NewValues = case (catch - lists:map(fun({{Year,Month,Day},{Hour,Minute,Second}}) -> - {Year,Month,Day,Hour,Minute,Second} - end, Values)) of + lists:map( + fun({{Year,Month,Day},{Hour,Minute,Second}}) -> + {Year,Month,Day,Hour,Minute,Second}; + (null) -> null + end, Values)) of Result -> Result end, @@ -960,15 +965,15 @@ fix_inout(out) -> fix_inout(inout) -> ?INOUT. -string_terminate([Value| _ ] = Values) when is_list(Value)-> - case (catch - lists:map(fun(Str) -> Str ++ [?STR_TERMINATOR] end, Values)) of - Result -> - Result - end; -string_terminate([Value| _ ] = Values) when is_binary(Value)-> - case (catch - lists:map(fun(B) -> <<B/binary,0:16>> end, Values)) of +string_terminate(Values) -> + case (catch lists:map(fun string_terminate_value/1, Values)) of Result -> Result end. + +string_terminate_value(String) when is_list(String) -> + String ++ [?STR_TERMINATOR]; +string_terminate_value(Binary) when is_binary(Binary) -> + <<Binary/binary,0:16>>; +string_terminate_value(null) -> + null. diff --git a/lib/odbc/test/Makefile b/lib/odbc/test/Makefile index bc6449242e..84551f0eb1 100644 --- a/lib/odbc/test/Makefile +++ b/lib/odbc/test/Makefile @@ -101,8 +101,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl index a8439d5fb6..e814cd2aca 100644 --- a/lib/odbc/test/odbc_test_lib.erl +++ b/lib/odbc/test/odbc_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-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 @@ -29,6 +29,7 @@ unique_table_name() -> lists:reverse(lists:foldl(fun($@, Acc) -> [$t, $A |Acc] ; + ($-,Acc) -> Acc; (X, Acc) -> [X |Acc] end, [], atom_to_list(node()))). diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk index fb6e208a52..3bb2fe5bce 100644 --- a/lib/odbc/vsn.mk +++ b/lib/odbc/vsn.mk @@ -1 +1 @@ -ODBC_VSN = 2.10.12 +ODBC_VSN = 2.10.13 diff --git a/lib/orber/COSS/CosNaming/Makefile b/lib/orber/COSS/CosNaming/Makefile index 064447f148..a61fcd08a6 100644 --- a/lib/orber/COSS/CosNaming/Makefile +++ b/lib/orber/COSS/CosNaming/Makefile @@ -141,11 +141,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/COSS/CosNaming - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(IDL_FILE) $(RELSYSDIR)/COSS/CosNaming - $(INSTALL_DATA) $(GEN_FILES) $(RELSYSDIR)/COSS/CosNaming + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/COSS/CosNaming" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(IDL_FILE) "$(RELSYSDIR)/COSS/CosNaming" + $(INSTALL_DATA) $(GEN_FILES) "$(RELSYSDIR)/COSS/CosNaming" release_docs_spec: diff --git a/lib/orber/c_src/Makefile.in b/lib/orber/c_src/Makefile.in index 56f0d57545..5953d41ff3 100644 --- a/lib/orber/c_src/Makefile.in +++ b/lib/orber/c_src/Makefile.in @@ -89,23 +89,23 @@ include $(ERL_TOP)/make/otp_release_targets.mk ifeq ($(findstring win32,$(TARGET)),win32) release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/src - $(INSTALL_DIR) $(RELSYSDIR)/priv/include - $(INSTALL_PROGRAM) $(CC_FILES) $(RELSYSDIR)/priv/src - $(INSTALL_PROGRAM) $(HH_FILES) $(RELSYSDIR)/priv/include + $(INSTALL_DIR) "$(RELSYSDIR)/priv/src" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/include" + $(INSTALL_PROGRAM) $(CC_FILES) "$(RELSYSDIR)/priv/src" + $(INSTALL_PROGRAM) $(HH_FILES) "$(RELSYSDIR)/priv/include" else ifeq ($(findstring vxworks,$(TARGET)),vxworks) release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/priv/src - $(INSTALL_DIR) $(RELSYSDIR)/priv/include - $(INSTALL_PROGRAM) $(CC_FILES) $(RELSYSDIR)/priv/src - $(INSTALL_PROGRAM) $(HH_FILES) $(RELSYSDIR)/priv/include + $(INSTALL_DIR) "$(RELSYSDIR)/priv/src" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/include" + $(INSTALL_PROGRAM) $(CC_FILES) "$(RELSYSDIR)/priv/src" + $(INSTALL_PROGRAM) $(HH_FILES) "$(RELSYSDIR)/priv/include" else release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/src - $(INSTALL_DIR) $(RELSYSDIR)/priv/include - $(INSTALL_DATA) $(CC_FILES) $(RELSYSDIR)/priv/src - $(INSTALL_DATA) $(HH_FILES) $(RELSYSDIR)/priv/include + $(INSTALL_DIR) "$(RELSYSDIR)/priv/src" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/include" + $(INSTALL_DATA) $(CC_FILES) "$(RELSYSDIR)/priv/src" + $(INSTALL_DATA) $(HH_FILES) "$(RELSYSDIR)/priv/include" endif endif diff --git a/lib/orber/doc/src/Makefile b/lib/orber/doc/src/Makefile index 68fbe3dce0..c429c1d80b 100644 --- a/lib/orber/doc/src/Makefile +++ b/lib/orber/doc/src/Makefile @@ -164,13 +164,13 @@ debug opt: 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/orber/examples/Stack/Makefile b/lib/orber/examples/Stack/Makefile index b985f348fa..b03d7c3b86 100644 --- a/lib/orber/examples/Stack/Makefile +++ b/lib/orber/examples/Stack/Makefile @@ -118,8 +118,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/Stack - $(INSTALL_DATA) $(ERL_FILES) $(JAVA_FILES) $(CPP_FILES) $(IDL_FILES) $(RELSYSDIR)/examples/Stack + $(INSTALL_DIR) "$(RELSYSDIR)/examples/Stack" + $(INSTALL_DATA) $(ERL_FILES) $(JAVA_FILES) $(CPP_FILES) $(IDL_FILES) "$(RELSYSDIR)/examples/Stack" release_docs_spec: diff --git a/lib/orber/java_src/Orber/Makefile b/lib/orber/java_src/Orber/Makefile index 49da975a8b..4e52ed3635 100644 --- a/lib/orber/java_src/Orber/Makefile +++ b/lib/orber/java_src/Orber/Makefile @@ -63,8 +63,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/java_src/Orber - $(INSTALL_DATA) $(JAVA_FILES) $(RELSYSDIR)/java_src/Orber + $(INSTALL_DIR) "$(RELSYSDIR)/java_src/Orber" + $(INSTALL_DATA) $(JAVA_FILES) "$(RELSYSDIR)/java_src/Orber" release_docs_spec: diff --git a/lib/orber/priv/Makefile b/lib/orber/priv/Makefile index af82177466..7957f7bbf0 100644 --- a/lib/orber/priv/Makefile +++ b/lib/orber/priv/Makefile @@ -58,8 +58,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(HELP_FILES) $(HTML_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(HELP_FILES) $(HTML_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/orber/src/Makefile b/lib/orber/src/Makefile index d2e98686da..a57d6d948b 100644 --- a/lib/orber/src/Makefile +++ b/lib/orber/src/Makefile @@ -254,12 +254,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(GEN_HRL_FILES_LOC) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(GEN_HRL_FILES_EXT) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(GEN_HRL_FILES_LOC) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(GEN_HRL_FILES_EXT) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/orber/test/Makefile b/lib/orber/test/Makefile index d4be009af3..8eef1934a4 100644 --- a/lib/orber/test/Makefile +++ b/lib/orber/test/Makefile @@ -209,12 +209,12 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) $(COVER_FILE) \ - $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ - $(RELSYSDIR)/$(IDLOUTDIR) + "$(RELSYSDIR)/$(IDLOUTDIR)" diff --git a/lib/orber/test/orber_test_lib.erl b/lib/orber/test/orber_test_lib.erl index 0ddde49cd6..2bfa1ef871 100644 --- a/lib/orber/test/orber_test_lib.erl +++ b/lib/orber/test/orber_test_lib.erl @@ -282,6 +282,7 @@ starter(Host, Name, Args) -> vxworks -> test_server:start_node(Name, slave, [{args,Args}]); _ -> + io:format("slave:start_link(~p,~p,~p).~n",[Host,Name,Args]), slave:start_link(Host, Name, Args) end. @@ -403,8 +404,8 @@ create_paths() -> filename:join(Path, "idl_output") ++ " -pa " ++ filename:join(Path, "all_SUITE_data") ++ - " -pa " ++ - filename:dirname(code:which(orber)). + " -pa \"" ++ + filename:dirname(code:which(orber))++"\"". %%------------------------------------------------------------ %% function : destroy_node diff --git a/lib/os_mon/c_src/Makefile.in b/lib/os_mon/c_src/Makefile.in index bac0413ece..45dc01e993 100644 --- a/lib/os_mon/c_src/Makefile.in +++ b/lib/os_mon/c_src/Makefile.in @@ -131,10 +131,10 @@ ifeq ($(findstring vxworks_simso,$(TARGET)),vxworks_simso) release_spec: else release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(C_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_PROGRAM) $(TARGET_FILES) $(RELSYSDIR)/priv/bin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(C_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_PROGRAM) $(TARGET_FILES) "$(RELSYSDIR)/priv/bin" endif release_docs_spec: diff --git a/lib/os_mon/c_src/cpu_sup.c b/lib/os_mon/c_src/cpu_sup.c index 9c5f9a6aa5..a0432b3093 100644 --- a/lib/os_mon/c_src/cpu_sup.c +++ b/lib/os_mon/c_src/cpu_sup.c @@ -458,8 +458,18 @@ static void error(char* err_msg) { * if we get error here we have trouble, * silence unnecessary warnings */ - if(write(FD_ERR, err_msg, strlen(err_msg))); - if(write(FD_ERR, "\n", 1)); + char buffer[256] = "[os_mon] cpu supervisor port (cpu_sup): "; + int i = strlen(buffer), j = 0; + int n = strlen(err_msg); + + while(i < 253 && j < n) { + buffer[i++] = err_msg[j++]; + } + buffer[i++] = '\r'; + buffer[i++] = '\n'; + + /* try to use one write only */ + if(write(FD_ERR, buffer, i)); exit(-1); } diff --git a/lib/os_mon/c_src/memsup.c b/lib/os_mon/c_src/memsup.c index 078f20ff98..593a066f98 100644 --- a/lib/os_mon/c_src/memsup.c +++ b/lib/os_mon/c_src/memsup.c @@ -493,7 +493,7 @@ get_basic_mem(unsigned long *tot, unsigned long *used, unsigned long *pagesize){ #elif defined(__linux__) && !defined(_SC_AVPHYS_PAGES) memory_ext me; if (get_mem_procfs(&me) < 0) { - print_error("ProcFS read error."); + print_error("ProcFS read error"); exit(1); } *tot = me.total; @@ -582,7 +582,7 @@ message_loop(int erlin_fd) * Wait for command from Erlang */ if ((res = read(erlin_fd, &cmdLen, 1)) < 0) { - print_error("Error reading from Erlang."); + print_error("Error reading from Erlang"); return; } @@ -603,19 +603,19 @@ message_loop(int erlin_fd) break; case 0: - print_error("Erlang has closed."); + print_error("Erlang has closed"); return; default: - print_error("Error reading from Erlang."); + print_error("Error reading from Erlang"); return; } /* switch() */ } else { /* cmdLen != 1 */ - print_error("Invalid command length (%d) received.", cmdLen); + print_error("Invalid command length (%d) received", cmdLen); return; } } else { /* Erlang end closed */ - print_error("Erlang has closed."); + print_error("Erlang has closed"); return; } } @@ -641,15 +641,12 @@ static void print_error(const char *format,...) { va_list args; + char buffer[256]; va_start(args, format); - fprintf(stderr, "%s: ", program_name); - vfprintf(stderr, format, args); + vsnprintf(buffer, 256, format, args); va_end(args); - fprintf(stderr, " \n"); + /* try to use one write only */ + fprintf(stderr, "[os_mon] memory supervisor port (memsup): %s\r\n", buffer); + fflush(stderr); } - - - - - diff --git a/lib/os_mon/c_src/win32sysinfo.c b/lib/os_mon/c_src/win32sysinfo.c index 2a155aae87..9d4587393f 100644 --- a/lib/os_mon/c_src/win32sysinfo.c +++ b/lib/os_mon/c_src/win32sysinfo.c @@ -89,6 +89,7 @@ typedef BOOL (WINAPI *tfpGetDiskFreeSpaceEx)(LPCTSTR, PULARGE_INTEGER,PULARGE_IN static tfpGetDiskFreeSpaceEx fpGetDiskFreeSpaceEx; +static void print_error(const char *msg); static void return_answer(char* value) { @@ -98,7 +99,7 @@ return_answer(char* value) res = write(1,(char*) &bytes,1); if (res != 1) { - fprintf(stderr,"win32sysinfo:Error writing to pipe"); + print_error("Error writing to pipe"); exit(1); } @@ -107,9 +108,8 @@ return_answer(char* value) while (left > 0) { res = write(1, value+bytes-left, left); - if (res <= 0) - { - fprintf(stderr,"win32sysinfo:Error writing to pipe"); + if (res <= 0) { + print_error("Error writing to pipe"); exit(1); } left -= res; @@ -248,7 +248,6 @@ message_loop() char cmd[512]; int res; - fprintf(stderr,"in message_loop\n"); /* Startup ACK. */ return_answer(OK); while (1) @@ -257,12 +256,12 @@ message_loop() * Wait for command from Erlang */ if ((res = read(0, &cmdLen, 1)) < 0) { - fprintf(stderr,"win32sysinfo:Error reading from Erlang."); + print_error("Error reading from Erlang"); return; } if (res != 1){ /* Exactly one byte read ? */ - fprintf(stderr,"win32sysinfo:Erlang has closed."); + print_error("Erlang has closed"); return; } if ((res = read(0, &cmd, cmdLen)) == cmdLen){ @@ -291,11 +290,11 @@ message_loop() return_answer("xEND"); } else if (res == 0) { - fprintf(stderr,"win32sysinfo:Erlang has closed."); + print_error("Erlang has closed"); return; } else { - fprintf(stderr,"win32sysinfo:Error reading from Erlang."); + print_error("Error reading from Erlang"); return; } } @@ -309,10 +308,9 @@ int main(int argc, char ** argv){ message_loop(); return 0; } - - - - - - - +static void +print_error(const char *msg) { + /* try to use one write only */ + fprintf(stderr, "[os_mon] win32 supervisor port (win32sysinfo): %s\r\n", msg); + fflush(stderr); +} diff --git a/lib/os_mon/doc/src/Makefile b/lib/os_mon/doc/src/Makefile index c9765749c9..aa8fb4b60c 100644 --- a/lib/os_mon/doc/src/Makefile +++ b/lib/os_mon/doc/src/Makefile @@ -109,16 +109,16 @@ 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 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/os_mon/mibs/Makefile b/lib/os_mon/mibs/Makefile index 655190edf4..a6d8ab5042 100644 --- a/lib/os_mon/mibs/Makefile +++ b/lib/os_mon/mibs/Makefile @@ -88,13 +88,13 @@ $(SNMP_BIN_TARGET_DIR)/OTP-OS-MON-MIB.bin: \ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/mibs - $(INSTALL_DIR) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DATA) $(MIB_FILES) $(FUNCS_FILES) $(RELSYSDIR)/mibs - $(INSTALL_DATA) $(V1_MIB_FILES) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_TARGETS) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/priv/mibs - $(INSTALL_DATA) $(BIN_TARGETS) $(RELSYSDIR)/priv/mibs + $(INSTALL_DIR) "$(RELSYSDIR)/mibs" + $(INSTALL_DIR) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DATA) $(MIB_FILES) $(FUNCS_FILES) "$(RELSYSDIR)/mibs" + $(INSTALL_DATA) $(V1_MIB_FILES) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_TARGETS) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/mibs" + $(INSTALL_DATA) $(BIN_TARGETS) "$(RELSYSDIR)/priv/mibs" release_docs_spec: diff --git a/lib/os_mon/src/Makefile b/lib/os_mon/src/Makefile index 9a75446a89..a8ad26f6a1 100644 --- a/lib/os_mon/src/Makefile +++ b/lib/os_mon/src/Makefile @@ -94,11 +94,11 @@ $(EBIN)/memsup.$(EMULATOR): $(MEMSUP_HRL) include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl index 308cd1b7fa..c7abb7a386 100644 --- a/lib/os_mon/src/disksup.erl +++ b/lib/os_mon/src/disksup.erl @@ -43,24 +43,24 @@ start_link() -> gen_server:start_link({local, disksup}, disksup, [], []). get_disk_data() -> - os_mon:call(disksup, get_disk_data). + os_mon:call(disksup, get_disk_data, infinity). get_check_interval() -> - os_mon:call(disksup, get_check_interval). + os_mon:call(disksup, get_check_interval, infinity). set_check_interval(Minutes) -> case param_type(disk_space_check_interval, Minutes) of true -> - os_mon:call(disksup, {set_check_interval, Minutes}); + os_mon:call(disksup, {set_check_interval, Minutes}, infinity); false -> erlang:error(badarg) end. get_almost_full_threshold() -> - os_mon:call(disksup, get_almost_full_threshold). + os_mon:call(disksup, get_almost_full_threshold, infinity). set_almost_full_threshold(Float) -> case param_type(disk_almost_full_threshold, Float) of true -> - os_mon:call(disksup, {set_almost_full_threshold, Float}); + os_mon:call(disksup, {set_almost_full_threshold, Float}, infinity); false -> erlang:error(badarg) end. diff --git a/lib/os_mon/src/memsup.erl b/lib/os_mon/src/memsup.erl index 5ef240f128..54771b4703 100644 --- a/lib/os_mon/src/memsup.erl +++ b/lib/os_mon/src/memsup.erl @@ -112,7 +112,7 @@ get_helper_timeout() -> set_helper_timeout(Seconds) -> case param_type(memsup_helper_timeout, Seconds) of true -> - os_mon:call(memsup, {set_helper_timeout, Seconds}); + os_mon:call(memsup, {set_helper_timeout, Seconds}, infinity); false -> erlang:error(badarg) end. diff --git a/lib/os_mon/src/os_mon.erl b/lib/os_mon/src/os_mon.erl index 3098c38808..2b6cd7c498 100644 --- a/lib/os_mon/src/os_mon.erl +++ b/lib/os_mon/src/os_mon.erl @@ -85,13 +85,13 @@ open_port(Name, Opts) -> %% Check os_mon*/priv/bin/Name case filelib:is_regular(ReleasedPath) of true -> - erlang:open_port({spawn, ReleasedPath}, Opts); + erlang:open_port({spawn, "\""++ReleasedPath++"\""}, Opts); false -> %% Use os_mon*/priv/bin/Arch/Name ArchPath = filename:join( [PrivDir,"bin",erlang:system_info(system_architecture),Name]), - erlang:open_port({spawn, ArchPath}, Opts) + erlang:open_port({spawn, "\""++ArchPath++"\""}, Opts) end. diff --git a/lib/os_mon/test/Makefile b/lib/os_mon/test/Makefile index a240640f92..954cfef7fb 100644 --- a/lib/os_mon/test/Makefile +++ b/lib/os_mon/test/Makefile @@ -84,9 +84,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) os_mon.spec os_mon.cover $(EMAKEFILE) $(SOURCE) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) os_mon.spec os_mon.cover $(EMAKEFILE) $(SOURCE) "$(RELSYSDIR)" -## tar chf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) +## tar chf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/otp_mibs/doc/src/Makefile b/lib/otp_mibs/doc/src/Makefile index bd2810034b..16a5bb39b4 100644 --- a/lib/otp_mibs/doc/src/Makefile +++ b/lib/otp_mibs/doc/src/Makefile @@ -102,14 +102,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/otp_mibs/mibs/Makefile b/lib/otp_mibs/mibs/Makefile index 5e59824d3f..7ef8480b3e 100644 --- a/lib/otp_mibs/mibs/Makefile +++ b/lib/otp_mibs/mibs/Makefile @@ -76,13 +76,13 @@ v1/%.mib.v1: %.mib include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/mibs - $(INSTALL_DIR) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DATA) $(MIB_FILES) $(FUNCS_FILES) $(RELSYSDIR)/mibs - $(INSTALL_DATA) $(V1_MIB_FILES) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_TARGETS) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/priv/mibs - $(INSTALL_DATA) $(BIN_TARGETS) $(RELSYSDIR)/priv/mibs + $(INSTALL_DIR) "$(RELSYSDIR)/mibs" + $(INSTALL_DIR) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DATA) $(MIB_FILES) $(FUNCS_FILES) "$(RELSYSDIR)/mibs" + $(INSTALL_DATA) $(V1_MIB_FILES) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_TARGETS) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/mibs" + $(INSTALL_DATA) $(BIN_TARGETS) "$(RELSYSDIR)/priv/mibs" release_docs_spec: diff --git a/lib/otp_mibs/src/Makefile b/lib/otp_mibs/src/Makefile index 833a439adb..c9c40fec3a 100644 --- a/lib/otp_mibs/src/Makefile +++ b/lib/otp_mibs/src/Makefile @@ -95,10 +95,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) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGETS) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGETS) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/parsetools/doc/src/Makefile b/lib/parsetools/doc/src/Makefile index 6e693e0cf0..464ea66af0 100644 --- a/lib/parsetools/doc/src/Makefile +++ b/lib/parsetools/doc/src/Makefile @@ -105,14 +105,14 @@ debug opt: 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/parsetools/src/Makefile b/lib/parsetools/src/Makefile index 89e079e411..e228ee88ef 100644 --- a/lib/parsetools/src/Makefile +++ b/lib/parsetools/src/Makefile @@ -89,12 +89,12 @@ $(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) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/parsetools/test/Makefile b/lib/parsetools/test/Makefile index 624c4e6975..430386bfe5 100644 --- a/lib/parsetools/test/Makefile +++ b/lib/parsetools/test/Makefile @@ -70,9 +70,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) parsetools.spec parsetools.cover $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - # @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) parsetools.spec parsetools.cover $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + # @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/percept/doc/src/Makefile b/lib/percept/doc/src/Makefile index f0d43c5a01..e749103783 100644 --- a/lib/percept/doc/src/Makefile +++ b/lib/percept/doc/src/Makefile @@ -175,15 +175,15 @@ debug opt: 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) $(HTML_EXAMPLE_FILES) $(HTML_STYLESHEET_FILES) \ $(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/percept/priv/Makefile b/lib/percept/priv/Makefile index 7c37ccd98b..4d7942c49c 100644 --- a/lib/percept/priv/Makefile +++ b/lib/percept/priv/Makefile @@ -77,20 +77,20 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt # Finished - $(INSTALL_DIR) $(RELSYSDIR)/priv/logs - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root/htdocs - $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/priv/server_root/htdocs - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root/conf - $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/priv/server_root/conf - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root/scripts - $(INSTALL_DATA) $(SCRIPT_FILES) $(RELSYSDIR)/priv/server_root/scripts - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root/css - $(INSTALL_DATA) $(CSS_FILES) $(RELSYSDIR)/priv/server_root/css - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root/images - $(INSTALL_DATA) $(IMAGE_FILES) $(RELSYSDIR)/priv/server_root/images - $(INSTALL_DIR) $(RELSYSDIR)/priv/fonts - $(INSTALL_DATA) $(FONT_FILES) $(RELSYSDIR)/priv/fonts + $(INSTALL_DIR) "$(RELSYSDIR)/priv/logs" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/htdocs" + $(INSTALL_DATA) $(HTDOCS_FILES) "$(RELSYSDIR)/priv/server_root/htdocs" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/conf" + $(INSTALL_DATA) $(CONF_FILES) "$(RELSYSDIR)/priv/server_root/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/scripts" + $(INSTALL_DATA) $(SCRIPT_FILES) "$(RELSYSDIR)/priv/server_root/scripts" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/css" + $(INSTALL_DATA) $(CSS_FILES) "$(RELSYSDIR)/priv/server_root/css" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/images" + $(INSTALL_DATA) $(IMAGE_FILES) "$(RELSYSDIR)/priv/server_root/images" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/fonts" + $(INSTALL_DATA) $(FONT_FILES) "$(RELSYSDIR)/priv/fonts" release_docs_spec: diff --git a/lib/percept/src/Makefile b/lib/percept/src/Makefile index 5dfc72575a..4b9a10cdb8 100644 --- a/lib/percept/src/Makefile +++ b/lib/percept/src/Makefile @@ -93,12 +93,12 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src -# $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" +# $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/percept/test/Makefile b/lib/percept/test/Makefile index d927386d1c..ee70966b01 100644 --- a/lib/percept/test/Makefile +++ b/lib/percept/test/Makefile @@ -81,10 +81,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) percept.spec percept.cover $(EMAKEFILE) $(SOURCE) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) percept.spec percept.cover $(EMAKEFILE) $(SOURCE) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/pman/doc/src/Makefile b/lib/pman/doc/src/Makefile index 4e116cb23e..ec759091ab 100644 --- a/lib/pman/doc/src/Makefile +++ b/lib/pman/doc/src/Makefile @@ -103,14 +103,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/pman/priv/Makefile b/lib/pman/priv/Makefile index 43f9d45d94..a89278fd3a 100644 --- a/lib/pman/priv/Makefile +++ b/lib/pman/priv/Makefile @@ -50,8 +50,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(TOOLBAR_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(TOOLBAR_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/pman/src/Makefile b/lib/pman/src/Makefile index e573e57220..2cd1c64c49 100644 --- a/lib/pman/src/Makefile +++ b/lib/pman/src/Makefile @@ -103,10 +103,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(TOOLBOX_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(TARGET_TOOLBOX_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(TOOLBOX_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(TARGET_TOOLBOX_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile index 943d97bdb8..d48f48a5d5 100644 --- a/lib/public_key/asn1/Makefile +++ b/lib/public_key/asn1/Makefile @@ -91,13 +91,13 @@ $(INCLUDE)/%.hrl: %.hrl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/asn1 + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/asn1" $(INSTALL_DATA) $(ASN_ASNS) $(ASN_ERLS) $(ASN_HRLS) $(ASN_CONFIGS) \ - $(GEN_ERLS) $(RELSYSDIR)/asn1 - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(GEN_ERLS) "$(RELSYSDIR)/asn1" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/public_key/asn1/OTP-PKIX.asn1 b/lib/public_key/asn1/OTP-PKIX.asn1 index fbf531df40..e94a77a3e7 100644 --- a/lib/public_key/asn1/OTP-PKIX.asn1 +++ b/lib/public_key/asn1/OTP-PKIX.asn1 @@ -225,7 +225,17 @@ dnQualifier ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { countryName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { ID id-at-countryName - TYPE X520countryName } + TYPE X520countryName } -- this is currently not used when decoding + -- The decoding and mapping between ID and Type is done in the code + -- in module publickey_cert_records via the function attribute_type + -- To be more forgiving and compatible with other SSL implementations + -- regarding how to handle and sometimes accept incorrect certificates + -- we define and use the type below instead of X520countryName + + OTP-X520countryname ::= CHOICE { + printableString PrintableString (SIZE (2)), + utf8String UTF8String (SIZE (2)) +} serialNumber ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { ID id-at-serialNumber diff --git a/lib/public_key/asn1/PKCS-1.asn1 b/lib/public_key/asn1/PKCS-1.asn1 index b06f5efa9d..c83289e779 100644 --- a/lib/public_key/asn1/PKCS-1.asn1 +++ b/lib/public_key/asn1/PKCS-1.asn1 @@ -33,6 +33,9 @@ sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } +sha224WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 14 } + + id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile index 9616a96195..928aa62c1b 100644 --- a/lib/public_key/doc/src/Makefile +++ b/lib/public_key/doc/src/Makefile @@ -114,14 +114,14 @@ debug opt: 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: info: diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index 0b6673e826..5c227557f2 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2008</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -82,9 +82,9 @@ <p><code> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'</code></p> - <p><code> rsa_digest_type() = 'md5' | 'sha' </code></p> + <p><code> rsa_digest_type() = 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512' </code></p> - <p><code> dss_digest_type() = 'none' | 'sha' </code></p> + <p><code> dss_digest_type() = 'sha' </code></p> <p><code> ssh_file() = openssh_public_key | rfc4716_public_key | known_hosts | auth_keys </code></p> @@ -396,14 +396,14 @@ <name>sign(Msg, DigestType, Key) -> binary()</name> <fsummary> Create digital signature.</fsummary> <type> - <v>Msg = binary()</v> + <v>Msg = binary() | {digest,binary()}</v> <d>The msg is either the binary "plain text" data to be - signed or in the case that digest type is <c>none</c> - it is the hashed value of "plain text" i.e. the digest.</d> - <v>DigestType = rsa_digest_type() | dsa_digest_type()</v> + signed or it is the hashed value of "plain text" i.e. the + digest.</d> + <v>DigestType = rsa_digest_type() | dss_digest_type()</v> <v>Key = rsa_private_key() | dsa_private_key()</v> - </type> - <desc> + </type> + <desc> <p> Creates a digital signature.</p> </desc> </func> @@ -461,11 +461,10 @@ <name>verify(Msg, DigestType, Signature, Key) -> boolean()</name> <fsummary>Verifies a digital signature.</fsummary> <type> - <v>Msg = binary()</v> + <v>Msg = binary() | {digest,binary()}</v> <d>The msg is either the binary "plain text" data - or in the case that digest type is <c>none</c> - it is the hashed value of "plain text" i.e. the digest.</d> - <v>DigestType = rsa_digest_type() | dsa_digest_type()</v> + or it is the hashed value of "plain text" i.e. the digest.</d> + <v>DigestType = rsa_digest_type() | dss_digest_type()</v> <v>Signature = binary()</v> <v>Key = rsa_public_key() | dsa_public_key()</v> </type> diff --git a/lib/public_key/src/Makefile b/lib/public_key/src/Makefile index 062c495a65..8a72da477f 100644 --- a/lib/public_key/src/Makefile +++ b/lib/public_key/src/Makefile @@ -102,11 +102,11 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl index b86d7a1f0c..33fe940ea2 100644 --- a/lib/public_key/src/pubkey_cert_records.erl +++ b/lib/public_key/src/pubkey_cert_records.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -57,6 +57,15 @@ transform(#'OTPTBSCertificate'{}= TBS, decode) -> transform(#'AttributeTypeAndValue'{type=Id,value=Value0} = ATAV, Func) -> {ok, Value} = case attribute_type(Id) of + 'X520countryName'when Func == decode -> + %% Workaround that some certificates break the ASN-1 spec + %% and encode countryname as utf8 + case 'OTP-PUB-KEY':Func('OTP-X520countryname', Value0) of + {ok, {utf8String, Utf8Value}} -> + {ok, unicode:characters_to_list(Utf8Value)}; + {ok, {printableString, ASCCI}} -> + {ok, ASCCI} + end; Type when is_atom(Type) -> 'OTP-PUB-KEY':Func(Type, Value0); _UnknownType -> {ok, Value0} end, diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 2e2a6cd296..d5df53e848 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -48,8 +48,8 @@ -type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'. -type public_crypt_options() :: [{rsa_pad, rsa_padding()}]. --type rsa_digest_type() :: 'md5' | 'sha'| 'sha256' | 'sha512'. --type dss_digest_type() :: 'none' | 'sha'. +-type rsa_digest_type() :: 'md5' | 'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512'. +-type dss_digest_type() :: 'none' | 'sha'. %% None is for backwards compatibility -define(UINT32(X), X:32/unsigned-big-integer). -define(DER_NULL, <<5, 0>>). @@ -241,15 +241,15 @@ pkix_encode(Asn1Type, Term0, otp) when is_atom(Asn1Type) -> decrypt_private(CipherText, Key) -> decrypt_private(CipherText, Key, []). -decrypt_private(CipherText, - #'RSAPrivateKey'{modulus = N,publicExponent = E, - privateExponent = D}, - Options) when is_binary(CipherText), - is_list(Options) -> +decrypt_private(CipherText, + #'RSAPrivateKey'{modulus = N, publicExponent = E, + privateExponent = D} = Key, + Options) + when is_binary(CipherText), + is_integer(N), is_integer(E), is_integer(D), + is_list(Options) -> Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:rsa_private_decrypt(CipherText, - [crypto:mpint(E), crypto:mpint(N), - crypto:mpint(D)], Padding). + crypto:rsa_private_decrypt(CipherText, format_rsa_private_key(Key), Padding). %%-------------------------------------------------------------------- -spec decrypt_public(CipherText :: binary(), rsa_public_key() | rsa_private_key()) -> @@ -307,72 +307,86 @@ encrypt_public(PlainText, #'RSAPrivateKey'{modulus=N,publicExponent=E}, encrypt_private(PlainText, Key) -> encrypt_private(PlainText, Key, []). -encrypt_private(PlainText, #'RSAPrivateKey'{modulus = N, - publicExponent = E, - privateExponent = D}, - Options) when is_binary(PlainText), is_list(Options) -> +encrypt_private(PlainText, + #'RSAPrivateKey'{modulus = N, publicExponent = E, + privateExponent = D} = Key, + Options) + when is_binary(PlainText), + is_integer(N), is_integer(E), is_integer(D), + is_list(Options) -> Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:rsa_private_encrypt(PlainText, [crypto:mpint(E), - crypto:mpint(N), - crypto:mpint(D)], Padding). + crypto:rsa_private_encrypt(PlainText, format_rsa_private_key(Key), Padding). + + +format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E, + privateExponent = D, + prime1 = P1, prime2 = P2, + exponent1 = E1, exponent2 = E2, + coefficient = C}) + when is_integer(P1), is_integer(P2), + is_integer(E1), is_integer(E2), is_integer(C) -> + [crypto:mpint(K) || K <- [E, N, D, P1, P2, E1, E2, C]]; + +format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E, + privateExponent = D}) -> + [crypto:mpint(K) || K <- [E, N, D]]. %%-------------------------------------------------------------------- --spec sign(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(), - rsa_private_key() | +-spec sign(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type(), + rsa_private_key() | dsa_private_key()) -> Signature :: binary(). -%% %% Description: Create digital signature. %%-------------------------------------------------------------------- -sign(PlainText, DigestType, #'RSAPrivateKey'{modulus = N, publicExponent = E, - privateExponent = D}) - when is_binary(PlainText), - (DigestType == md5 orelse - DigestType == sha) -> - - crypto:rsa_sign(DigestType, sized_binary(PlainText), [crypto:mpint(E), - crypto:mpint(N), - crypto:mpint(D)]); - -sign(Digest, none, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) - when is_binary(Digest)-> - crypto:dss_sign(none, Digest, - [crypto:mpint(P), crypto:mpint(Q), +sign({digest,_}=Digest, DigestType, Key = #'RSAPrivateKey'{}) -> + crypto:rsa_sign(DigestType, Digest, format_rsa_private_key(Key)); + +sign(PlainText, DigestType, Key = #'RSAPrivateKey'{}) -> + crypto:rsa_sign(DigestType, sized_binary(PlainText), format_rsa_private_key(Key)); + +sign({digest,_}=Digest, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> + crypto:dss_sign(Digest, + [crypto:mpint(P), crypto:mpint(Q), crypto:mpint(G), crypto:mpint(X)]); - -sign(PlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) - when is_binary(PlainText) -> - crypto:dss_sign(sized_binary(PlainText), - [crypto:mpint(P), crypto:mpint(Q), - crypto:mpint(G), crypto:mpint(X)]). + +sign(PlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> + crypto:dss_sign(sized_binary(PlainText), + [crypto:mpint(P), crypto:mpint(Q), + crypto:mpint(G), crypto:mpint(X)]); + +%% Backwards compatible +sign(Digest, none, #'DSAPrivateKey'{} = Key) -> + sign({digest,Digest}, sha, Key). %%-------------------------------------------------------------------- --spec verify(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(), - Signature :: binary(), rsa_public_key() +-spec verify(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type(), + Signature :: binary(), rsa_public_key() | dsa_public_key()) -> boolean(). -%% %% Description: Verifies a digital signature. %%-------------------------------------------------------------------- -verify(PlainText, DigestType, Signature, - #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) - when is_binary (PlainText) and (DigestType == sha orelse - DigestType == sha256 orelse - DigestType == sha512 orelse - DigestType == md5) -> +verify({digest,_}=Digest, DigestType, Signature, + #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) -> + crypto:rsa_verify(DigestType, Digest, + sized_binary(Signature), + [crypto:mpint(Exp), crypto:mpint(Mod)]); + +verify(PlainText, DigestType, Signature, + #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) -> crypto:rsa_verify(DigestType, sized_binary(PlainText), sized_binary(Signature), [crypto:mpint(Exp), crypto:mpint(Mod)]); -verify(Digest, none, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) - when is_integer(Key), is_binary(Digest), is_binary(Signature) -> - crypto:dss_verify(none, - Digest, - sized_binary(Signature), +verify({digest,_}=Digest, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) + when is_integer(Key), is_binary(Signature) -> + crypto:dss_verify(Digest, sized_binary(Signature), [crypto:mpint(P), crypto:mpint(Q), crypto:mpint(G), crypto:mpint(Key)]); - +%% Backwards compatibility +verify(Digest, none, Signature, {_, #'Dss-Parms'{}} = Key ) -> + verify({digest,Digest}, sha, Signature, Key); + verify(PlainText, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) - when is_integer(Key), is_binary(PlainText), is_binary(Signature) -> + when is_integer(Key), is_binary(PlainText), is_binary(Signature) -> crypto:dss_verify(sized_binary(PlainText), sized_binary(Signature), [crypto:mpint(P), crypto:mpint(Q), diff --git a/lib/public_key/test/Makefile b/lib/public_key/test/Makefile index b7f91981a5..41d77f103b 100644 --- a/lib/public_key/test/Makefile +++ b/lib/public_key/test/Makefile @@ -21,7 +21,7 @@ include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk -INCLUDES= -I. -I ../include +INCLUDES= -I. -I ../include -pa $(ERL_TOP)/lib/public_key/ebin # ---------------------------------------------------- # Target Specs @@ -78,11 +78,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index a91dcfa029..6a879867e1 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -107,7 +107,7 @@ all() -> {group, ssh_public_key_decode_encode}, encrypt_decrypt, {group, sign_verify}, - pkix, pkix_path_validation]. + pkix, pkix_countryname, pkix_path_validation]. groups() -> [{pem_decode_encode, [], [dsa_pem, rsa_pem, encrypted_pem, @@ -626,6 +626,34 @@ pkix(Config) when is_list(Config) -> VerifyStr = public_key:pkix_normalize_name(TestStr), ok. + +%%-------------------------------------------------------------------- +pkix_countryname(doc) -> + "Test workaround for certs that code x509countryname as utf8"; +pkix_countryname(suite) -> + []; +pkix_countryname(Config) when is_list(Config) -> + Cert = incorrect_pkix_cert(), + OTPCert = public_key:pkix_decode_cert(Cert, otp), + TBSCert = OTPCert#'OTPCertificate'.tbsCertificate, + Issuer = TBSCert#'OTPTBSCertificate'.issuer, + Subj = TBSCert#'OTPTBSCertificate'.subject, + check_countryname(Issuer), + check_countryname(Subj). + +check_countryname({rdnSequence,DirName}) -> + do_check_countryname(DirName). +do_check_countryname([]) -> + ok; +do_check_countryname([#'AttributeTypeAndValue'{type = ?'id-at-countryName', + value = "US"}|_]) -> + ok; +do_check_countryname([#'AttributeTypeAndValue'{type = ?'id-at-countryName', + value = Value}|_]) -> + test_server:fail({incorrect_cuntry_name, Value}); +do_check_countryname([_| Rest]) -> + do_check_countryname(Rest). + %%-------------------------------------------------------------------- pkix_path_validation(doc) -> "Misc pkix tests not covered elsewhere"; @@ -716,3 +744,6 @@ check_entry_type(_,_) -> strip_ending_newlines(Bin) -> string:strip(binary_to_list(Bin), right, 10). + +incorrect_pkix_cert() -> + <<48,130,5,186,48,130,4,162,160,3,2,1,2,2,7,7,250,61,63,6,140,137,48,13,6,9,42, 134,72,134,247,13,1,1,5,5,0,48,129,220,49,11,48,9,6,3,85,4,6,19,2,85,83,49, 16,48,14,6,3,85,4,8,19,7,65,114,105,122,111,110,97,49,19,48,17,6,3,85,4,7,19, 10,83,99,111,116,116,115,100,97,108,101,49,37,48,35,6,3,85,4,10,19,28,83,116, 97,114,102,105,101,108,100,32,84,101,99,104,110,111,108,111,103,105,101,115, 44,32,73,110,99,46,49,57,48,55,6,3,85,4,11,19,48,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 49,49,48,47,6,3,85,4,3,19,40,83,116,97,114,102,105,101,108,100,32,83,101,99, 117,114,101,32,67,101,114,116,105,102,105,99,97,116,105,111,110,32,65,117, 116,104,111,114,105,116,121,49,17,48,15,6,3,85,4,5,19,8,49,48,54,56,56,52,51, 53,48,30,23,13,49,48,49,48,50,51,48,49,51,50,48,53,90,23,13,49,50,49,48,50, 51,48,49,51,50,48,53,90,48,122,49,11,48,9,6,3,85,4,6,12,2,85,83,49,11,48,9,6, 3,85,4,8,12,2,65,90,49,19,48,17,6,3,85,4,7,12,10,83,99,111,116,116,115,100, 97,108,101,49,38,48,36,6,3,85,4,10,12,29,83,112,101,99,105,97,108,32,68,111, 109,97,105,110,32,83,101,114,118,105,99,101,115,44,32,73,110,99,46,49,33,48, 31,6,3,85,4,3,12,24,42,46,108,111,103,105,110,46,115,101,99,117,114,101,115, 101,114,118,101,114,46,110,101,116,48,130,1,34,48,13,6,9,42,134,72,134,247, 13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,185,136,240,80,141,36,124, 245,182,130,73,19,188,74,166,117,72,228,185,209,43,129,244,40,44,193,231,11, 209,12,234,88,43,142,1,162,48,122,17,95,230,105,171,131,12,147,46,204,36,80, 250,171,33,253,35,62,83,22,71,212,186,141,14,198,89,89,121,204,224,122,246, 127,110,188,229,162,67,95,6,74,231,127,99,131,7,240,85,102,203,251,50,58,58, 104,245,103,181,183,134,32,203,121,232,54,32,188,139,136,112,166,126,14,91, 223,153,172,164,14,61,38,163,208,215,186,210,136,213,143,70,147,173,109,217, 250,169,108,31,211,104,238,103,93,182,59,165,43,196,189,218,241,30,148,240, 109,90,69,176,194,52,116,173,151,135,239,10,209,179,129,192,102,75,11,25,168, 223,32,174,84,223,134,70,167,55,172,143,27,130,123,226,226,7,34,142,166,39, 48,246,96,231,150,84,220,106,133,193,55,95,159,227,24,249,64,36,1,142,171,16, 202,55,126,7,156,15,194,22,116,53,113,174,104,239,203,120,45,131,57,87,84, 163,184,27,83,57,199,91,200,34,43,98,61,180,144,76,65,170,177,2,3,1,0,1,163, 130,1,224,48,130,1,220,48,15,6,3,85,29,19,1,1,255,4,5,48,3,1,1,0,48,29,6,3, 85,29,37,4,22,48,20,6,8,43,6,1,5,5,7,3,1,6,8,43,6,1,5,5,7,3,2,48,14,6,3,85, 29,15,1,1,255,4,4,3,2,5,160,48,56,6,3,85,29,31,4,49,48,47,48,45,160,43,160, 41,134,39,104,116,116,112,58,47,47,99,114,108,46,115,116,97,114,102,105,101, 108,100,116,101,99,104,46,99,111,109,47,115,102,115,50,45,48,46,99,114,108, 48,83,6,3,85,29,32,4,76,48,74,48,72,6,11,96,134,72,1,134,253,110,1,7,23,2,48, 57,48,55,6,8,43,6,1,5,5,7,2,1,22,43,104,116,116,112,115,58,47,47,99,101,114, 116,115,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99,111,109, 47,114,101,112,111,115,105,116,111,114,121,47,48,129,141,6,8,43,6,1,5,5,7,1, 1,4,129,128,48,126,48,42,6,8,43,6,1,5,5,7,48,1,134,30,104,116,116,112,58,47, 47,111,99,115,112,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99, 111,109,47,48,80,6,8,43,6,1,5,5,7,48,2,134,68,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 47,115,102,95,105,110,116,101,114,109,101,100,105,97,116,101,46,99,114,116, 48,31,6,3,85,29,35,4,24,48,22,128,20,73,75,82,39,209,27,188,242,161,33,106, 98,123,81,66,122,138,215,213,86,48,59,6,3,85,29,17,4,52,48,50,130,24,42,46, 108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118,101,114,46,110, 101,116,130,22,108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118, 101,114,46,110,101,116,48,29,6,3,85,29,14,4,22,4,20,138,233,191,208,157,203, 249,85,242,239,20,195,48,10,148,49,144,101,255,116,48,13,6,9,42,134,72,134, 247,13,1,1,5,5,0,3,130,1,1,0,82,31,121,162,49,50,143,26,167,202,143,61,71, 189,201,199,57,81,122,116,90,192,88,24,102,194,174,48,157,74,27,87,210,223, 253,93,3,91,150,109,120,1,110,27,11,200,198,141,222,246,14,200,71,105,41,138, 13,114,122,106,63,17,197,181,234,121,61,89,74,65,41,231,248,219,129,83,176, 219,55,107,55,211,112,98,38,49,69,77,96,221,108,123,152,12,210,159,157,141, 43,226,55,187,129,3,82,49,136,66,81,196,91,234,196,10,82,48,6,80,163,83,71, 127,102,177,93,209,129,26,104,2,84,24,255,248,161,3,244,169,234,92,122,110, 43,4,17,113,185,235,108,219,210,236,132,216,177,227,17,169,58,162,159,182, 162,93,160,229,200,9,163,229,110,121,240,168,232,14,91,214,188,196,109,210, 164,222,0,109,139,132,113,91,16,118,173,178,176,80,132,34,41,199,51,206,250, 224,132,60,115,192,94,107,163,219,212,226,225,65,169,148,108,213,46,174,173, 103,110,189,229,166,149,254,31,51,44,144,108,187,182,11,251,201,206,86,138, 208,59,51,86,132,235,81,225,88,34,190,8,184>>. diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk index ab4ee8b0ff..c8165fa247 100644 --- a/lib/public_key/vsn.mk +++ b/lib/public_key/vsn.mk @@ -1 +1 @@ -PUBLIC_KEY_VSN = 0.15 +PUBLIC_KEY_VSN = 0.16 diff --git a/lib/reltool/doc/src/Makefile b/lib/reltool/doc/src/Makefile index 8bc1488f77..0b85b7e02c 100644 --- a/lib/reltool/doc/src/Makefile +++ b/lib/reltool/doc/src/Makefile @@ -97,13 +97,13 @@ 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_DATA) $(HTMLDIR)/* $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + $(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" release_spec: diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index 9a4e2d130e..2567a72999 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -144,7 +144,7 @@ value <c>include</c> implies that all applications and escripts that do not have any explicit <c>incl_cond</c> setting will be included. <c>exclude</c> implies that all - applications and escripts) that do not have any explicit + applications and escripts that do not have any explicit <c>incl_cond</c> setting will be excluded.</p> </item> @@ -221,6 +221,52 @@ system.</p> </item> + <tag><c>excl_lib</c></tag> + <item> + <warning><p>This option is experimental.</p></warning> + <p>If the <c>excl_lib</c> option is set to <c>otp_root</c> + then reltool will not copy anything from the Erlang/OTP + installation ($OTP_ROOT) into the target structure. The goal + is to create a "slim" release which can be used together with + an existing Erlang/OTP installation. The target structure will + therefore only contain a <c>lib</c> directory with the + applications that were found outside of $OTP_ROOT (typically + your own applications), and a <c>releases</c> directory with + the generated <c>.rel,</c> <c>.script</c> and <c>.boot</c> + files.</p> + + <p>When starting this release, three things must be specified:</p> + <taglist> + <tag><b>Which <c>releases</c> directory to use</b></tag> + <item>Tell the release handler to use the <c>releases</c> + directory in our target structure instead of + <c>$OTP_ROOT/releases</c>. This is done by setting the SASL + environment variable <c>releases_dir</c>, either from the + command line (<c>-sasl releases_dir + <target-dir>/releases</c>) or in + <c>sys.config</c>.</item> + + <tag><b>Which boot file to use</b></tag> + <item>The default boot file is <c>$OTP_ROOT/bin/start</c>, + but in this case we need to specify a boot file from our + target structure, typically + <c><target-dir>/releases/<vsn>/<RelName></c>. This + is done with the <c>-boot</c> command line option to + <c>erl</c></item> + + <tag><b>The location of our applications</b></tag> + <item>The generated .script (and .boot) file uses the + environment variable <c>$RELTOOL_EXT_LIB</c> as prefix for + the paths to all applications. The <c>-boot_var</c> option + to <c>erl</c> can be used for specifying the value of this + variable, typically <c>-boot_var RELTOOL_EXT_LIB + <target-dir>/lib</c>.</item> + </taglist> + + <p>Example:</p> + <p><code>erl -sasl releases_dir \"mytarget/releases\" -boot mytarget/releases/1.0/myrel -boot_var RELTOOL_EXT_LIB mytarget/lib</code></p> + </item> + <tag><c>incl_sys_filters</c></tag> <item> <p>This parameter normally contains a list of regular @@ -452,6 +498,7 @@ app() = {vsn, app_vsn()} | {incl_cond, incl_cond()} | {debug_info, debug_info()} | {app_file, app_file()} + | {excl_lib, excl_lib()} | {incl_sys_filters, incl_sys_filters()} | {excl_sys_filters, excl_sys_filters()} | {incl_app_filters, incl_app_filters()} @@ -477,6 +524,7 @@ escript() = {incl_cond, incl_cond()} escript_file() = file() excl_app_filters() = regexps() excl_archive_filters() = regexps() +excl_lib() = otp_root excl_sys_filters() = regexps() file() = string() incl_app() = app_name() diff --git a/lib/reltool/examples/Makefile b/lib/reltool/examples/Makefile index dacfd4e7d4..afc47e1d12 100644 --- a/lib/reltool/examples/Makefile +++ b/lib/reltool/examples/Makefile @@ -72,8 +72,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/reltool/src/Makefile b/lib/reltool/src/Makefile index 4e6a112b7e..4730b4acff 100644 --- a/lib/reltool/src/Makefile +++ b/lib/reltool/src/Makefile @@ -97,11 +97,11 @@ $(TARGET_FILES): $(HRL_FILES) $(INTERNAL_HRL_FILES) include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 26dcd82447..f0d8b38519 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -79,6 +79,7 @@ | {debug_info, debug_info()} | {app_file, app_file()} | {profile, profile()} + | {excl_lib, excl_lib()} | {incl_sys_filters, incl_sys_filters()} | {excl_sys_filters, excl_sys_filters()} | {incl_app_filters, incl_app_filters()} @@ -123,6 +124,7 @@ -type incl_defaults() :: boolean(). -type incl_derived() :: boolean(). -type status() :: missing | ok. +-type excl_lib() :: otp_root. -record(common, { @@ -233,6 +235,7 @@ rels :: [#rel{}], emu_name :: emu_name(), profile :: profile(), + excl_lib :: excl_lib(), incl_sys_filters :: [#regexp{}], excl_sys_filters :: [#regexp{}], incl_app_filters :: [#regexp{}], diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 034a42e1e2..3d1d7e54bf 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -1408,6 +1408,8 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals]) -> ExclApp, Sys#sys.excl_app_filters), embedded_app_type = AppType}; + excl_lib when Val =:= otp_root -> + Sys#sys{excl_lib=Val}; incl_sys_filters -> Sys#sys{incl_sys_filters = dec_re(Key, Val, Sys#sys.incl_sys_filters)}; diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl index e3a7b02143..c39ed0ecd5 100644 --- a/lib/reltool/src/reltool_target.erl +++ b/lib/reltool/src/reltool_target.erl @@ -687,6 +687,8 @@ strip_name_ebin(Dir, Name, Vsn) -> case lists:reverse(Dir) of ["ebin", Name | D] -> {ok, lists:reverse(D)}; ["ebin", FullName | D] -> {ok, lists:reverse(D)}; + [Name | D] -> {ok, lists:reverse(D)}; + [FullName | D] -> {ok, lists:reverse(D)}; _ -> false end. @@ -733,8 +735,20 @@ do_spec_rel_files(#rel{name = RelName} = Rel, Sys) -> BootFile = RelName ++ ".boot", MergedApps = merge_apps(Rel, Sys), GenRel = do_gen_rel(Rel, Sys, MergedApps), + Variables = + case Sys#sys.excl_lib of + otp_root -> + %% All applications that are fetched from somewhere + %% other than $OTP_ROOT/lib will get $RELTOOL_EXT_LIB + %% as path prefix in the .script file. + [{"RELTOOL_EXT_LIB",LibDir} || LibDir <- Sys#sys.lib_dirs] ++ + [{"RELTOOL_EXT_LIB",filename:dirname(AppLibDir)} || + #app{active_dir=AppLibDir,use_selected_vsn=dir} + <- MergedApps]; + _ -> + [] + end, PathFlag = true, - Variables = [], {ok, Script} = do_gen_script(Rel, Sys, MergedApps, PathFlag, Variables), {ok, BootBin} = gen_boot(Script), Date = date(), @@ -771,29 +785,34 @@ gen_spec(Sys) -> end. do_gen_spec(#sys{root_dir = RootDir, + excl_lib = ExclLib, incl_sys_filters = InclRegexps, excl_sys_filters = ExclRegexps, relocatable = Relocatable, apps = Apps} = Sys) -> - {create_dir, _, SysFiles} = spec_dir(RootDir), - {ExclRegexps2, SysFiles2} = - strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps), RelFiles = spec_rel_files(Sys), - {InclRegexps2, BinFiles} = - spec_bin_files(Sys, SysFiles, SysFiles2, RelFiles, InclRegexps), + {SysFiles, InclRegexps2, ExclRegexps2, Mandatory} = + case ExclLib of + otp_root -> + {[],InclRegexps,ExclRegexps,["lib"]}; + _ -> + {create_dir, _, SF} = spec_dir(RootDir), + {ER2, SF2} = strip_sys_files(Relocatable, SF, Apps, ExclRegexps), + {IR2, BinFiles} = + spec_bin_files(Sys, SF, SF2, RelFiles, InclRegexps), + SF3 = [{create_dir, "bin", BinFiles}] ++ SF2, + {SF3,IR2,ER2,["bin","erts","lib"]} + end, LibFiles = spec_lib_files(Sys), {BootVsn, StartFile} = spec_start_file(Sys), - SysFiles3 = - [ - {create_dir, "releases", + SysFiles2 = + [{create_dir, "releases", [StartFile, - {create_dir,BootVsn, RelFiles}]}, - {create_dir, "bin", BinFiles} - ] ++ SysFiles2, - SysFiles4 = filter_spec(SysFiles3, InclRegexps2, ExclRegexps2), - SysFiles5 = SysFiles4 ++ [{create_dir, "lib", LibFiles}], - check_sys(["bin", "erts", "lib"], SysFiles5), - SysFiles5. + {create_dir,BootVsn, RelFiles}]}] ++ SysFiles, + SysFiles3 = filter_spec(SysFiles2, InclRegexps2, ExclRegexps2), + SysFiles4 = SysFiles3 ++ [{create_dir, "lib", LibFiles}], + check_sys(Mandatory, SysFiles4), + SysFiles4. strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps) -> ExclRegexps2 = @@ -967,22 +986,35 @@ safe_lookup_spec(Prefix, Specs) -> %% Specify applications %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec_lib_files(#sys{apps = Apps} = Sys) -> +spec_lib_files(#sys{root_dir = RootDir, + apps = Apps, + excl_lib = ExclLib} = Sys) -> Filter = fun(#app{is_escript = IsEscript, is_included = IsIncl, - is_pre_included = IsPre, name = Name}) -> + is_pre_included = IsPre, name = Name, + active_dir = ActiveDir}) -> if Name =:= ?MISSING_APP_NAME -> false; IsEscript =/= false -> false; IsIncl; IsPre -> - true; + case ExclLib of + otp_root -> + not lists:prefix(RootDir,ActiveDir); + _ -> + true + end; true -> false end end, SelectedApps = lists:filter(Filter, Apps), - check_apps([kernel, stdlib], SelectedApps), + case ExclLib of + otp_root -> + ok; + _ -> + check_apps([kernel, stdlib], SelectedApps) + end, lists:flatten([spec_app(App, Sys) || App <- SelectedApps]). check_apps([Mandatory | Names], Apps) -> diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index d8a7adb837..52cdef44da 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/Makefile @@ -49,7 +49,7 @@ RELSYSDIR = $(RELEASE_PATH)/reltool_test # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -#ERL_COMPILE_FLAGS += +ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/reltool/ebin/ EBIN = . @@ -73,12 +73,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) reltool.spec reltool.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_SCRIPT) rtt $(INSTALL_PROGS) $(RELSYSDIR) - $(INSTALL_DATA) $(INSTALL_PROGS) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) reltool.spec reltool.cover $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_SCRIPT) rtt $(INSTALL_PROGS) "$(RELSYSDIR)" + $(INSTALL_DATA) $(INSTALL_PROGS) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 8a98dc36cf..f29f6049a5 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -67,6 +67,7 @@ all() -> create_standalone_app_clash, create_multiple_standalone, create_old_target, + create_slim, eval_target_spec, otp_9135, otp_9229_dupl_mod_exclude_app, @@ -985,6 +986,56 @@ create_old_target(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate target system + +create_slim(Config) -> + %% Configure the server + RelName = "slim", + RelVsn = "1.0", + + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"slim"), + + Sys = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, []}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [sasl, stdlib, kernel, a]}, + {app, sasl, [{incl_cond, include}]}, + {app, a, [{incl_cond, include}, + {lib_dir,filename:join(LibDir,"a-1.0")}]}, + {excl_lib,otp_root} + ]}, + + %% Generate target file + TargetDir = filename:absname(filename:join([?WORK_DIR, "target_slim"])), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]), + ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)), + + TargetLibDir = filename:join(TargetDir,"lib"), + TargetRelDir = filename:join(TargetDir,"releases"), + TargetRelVsnDir = filename:join(TargetRelDir,RelVsn), + + {ok,["a-1.0.ez"]} = file:list_dir(TargetLibDir), + + RootDir = code:root_dir(), + Erl = filename:join([RootDir, "bin", "erl"]), + Args = "-boot_var RELTOOL_EXT_LIB " ++ TargetLibDir ++ + " -boot " ++ filename:join(TargetRelVsnDir,RelName) ++ + " -sasl releases_dir \\\"" ++ TargetRelDir ++ "\\\"", + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl, Args)), + ?msym(RootDir, rpc:call(Node, code, root_dir, [])), + ?msym([{RelName,RelVsn,_,permanent}], + rpc:call(Node,release_handler,which_releases,[])), + ?msym(ok, stop_node(Node)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate target system with eval_target_spec/3 eval_target_spec(_Config) -> @@ -2226,8 +2277,10 @@ mod_path(Node,Mod) -> %% Node handling start_node(Name, ErlPath) -> + start_node(Name, ErlPath, []). +start_node(Name, ErlPath, Args) -> FullName = full_node_name(Name), - CmdLine = mk_node_cmdline(Name, ErlPath), + CmdLine = mk_node_cmdline(Name, ErlPath, Args), io:format("Starting node ~p: ~s~n", [FullName, CmdLine]), case open_port({spawn, CmdLine}, []) of Port when is_port(Port) -> @@ -2246,14 +2299,7 @@ stop_node(Node) -> spawn(Node, fun () -> halt() end), receive {nodedown, Node} -> ok end. -mk_node_cmdline(Name) -> - Prog = case catch init:get_argument(progname) of - {ok,[[P]]} -> P; - _ -> exit(no_progname_argument_found) - end, - mk_node_cmdline(Name, Prog). - -mk_node_cmdline(Name, Prog) -> +mk_node_cmdline(Name, Prog, Args) -> Static = "-detached -noinput", Pa = filename:dirname(code:which(?MODULE)), NameSw = case net_kernel:longnames() of @@ -2268,7 +2314,8 @@ mk_node_cmdline(Name, Prog) -> ++ NameSw ++ " " ++ NameStr ++ " " ++ "-pa " ++ Pa ++ " " ++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr ++ " " - ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()). + ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()) + ++ " " ++ Args. full_node_name(PreName) -> HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end, diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src index 21410ceaa9..02846f42b6 100644 --- a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src +++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src @@ -22,7 +22,11 @@ SEL_VSN= \ use_selected_vsn/b-3.0/ebin/b.@EMULATOR@ \ use_selected_vsn/lib2/b-2.0/ebin/b.@EMULATOR@ -all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) $(SEL_VSN) +SLIM= \ + slim/a-1.0/ebin/a_sup.@EMULATOR@ \ + slim/a-1.0/ebin/a.@EMULATOR@ + +all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) $(SEL_VSN) $(SLIM) otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl @@ -55,3 +59,8 @@ use_selected_vsn/b-3.0/ebin/b.@EMULATOR@: use_selected_vsn/b-3.0/src/b.erl erlc $(EFLAGS) -ouse_selected_vsn/b-3.0/ebin use_selected_vsn/b-3.0/src/b.erl use_selected_vsn/lib2/b-2.0/ebin/b.@EMULATOR@: use_selected_vsn/lib2/b-2.0/src/b.erl erlc $(EFLAGS) -ouse_selected_vsn/lib2/b-2.0/ebin use_selected_vsn/lib2/b-2.0/src/b.erl + +slim/a-1.0/ebin/a_sup.@EMULATOR@: slim/a-1.0/src/a_sup.erl + erlc $(EFLAGS) -oslim/a-1.0/ebin slim/a-1.0/src/a_sup.erl +slim/a-1.0/ebin/a.@EMULATOR@: slim/a-1.0/src/a.erl + erlc $(EFLAGS) -oslim/a-1.0/ebin slim/a-1.0/src/a.erl diff --git a/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app new file mode 100644 index 0000000000..3160e00da7 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app @@ -0,0 +1,7 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [a, a_sup]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {mod, {a_sup, []}}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl @@ -0,0 +1,49 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/runtime_tools/c_src/Makefile.in b/lib/runtime_tools/c_src/Makefile.in index cbbcfa2d56..754e6ccd78 100644 --- a/lib/runtime_tools/c_src/Makefile.in +++ b/lib/runtime_tools/c_src/Makefile.in @@ -193,10 +193,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/obj - $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_PROGRAM) $(DYNTRACE_OBJS) $(RELSYSDIR)/priv/obj - $(INSTALL_PROGRAM) $(NIF_LIB) $(SOLIBS) $(RELSYSDIR)/priv/lib + $(INSTALL_DIR) "$(RELSYSDIR)/priv/obj" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" + $(INSTALL_PROGRAM) $(DYNTRACE_OBJS) "$(RELSYSDIR)/priv/obj" + $(INSTALL_PROGRAM) $(NIF_LIB) $(SOLIBS) "$(RELSYSDIR)/priv/lib" release_docs_spec: diff --git a/lib/runtime_tools/c_src/dtrace_user.d b/lib/runtime_tools/c_src/dtrace_user.d index 3a80d0f7a3..9e180a3cb2 100644 --- a/lib/runtime_tools/c_src/dtrace_user.d +++ b/lib/runtime_tools/c_src/dtrace_user.d @@ -19,31 +19,11 @@ */ provider erlang { - /** - * Send a single string to a probe. - * - * @param NUL-terminated string + /* + * The set of probes for use by Erlang code ... moved from here to + * erts/emulator/beam/erlang_dtrace.d until a more portable solution is + * found; see erlang_dtrace.d for details. */ - probe user_trace__s1(char* message); - - /** - * Multi-purpose probe: up to 4 NUL-terminated strings and 4 - * 64-bit integer arguments. - * - * @param proc, the PID (string form) of the sending process - * @param user_tag, the user tag of the sender - * @param i1, integer - * @param i2, integer - * @param i3, integer - * @param i4, integer - * @param s1, string/iolist. D's arg6 is NULL if not given by Erlang - * @param s2, string/iolist. D's arg7 is NULL if not given by Erlang - * @param s3, string/iolist. D's arg8 is NULL if not given by Erlang - * @param s4, string/iolist. D's arg9 is NULL if not given by Erlang - */ - probe user_trace__i4s4(char *proc, char *user_tag, - int i1, int i2, int i3, int i4, - char *s1, char *s2, char *s3, char *s4); }; #pragma D attributes Evolving/Evolving/Common provider erlang provider diff --git a/lib/runtime_tools/c_src/dyntrace.c b/lib/runtime_tools/c_src/dyntrace.c index 96dbebbdfa..eef03afd1c 100644 --- a/lib/runtime_tools/c_src/dyntrace.c +++ b/lib/runtime_tools/c_src/dyntrace.c @@ -36,6 +36,10 @@ void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf); void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz); +#ifdef HAVE_USE_DTRACE +ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#endif #ifdef VALGRIND # include <valgrind/memcheck.h> @@ -56,11 +60,13 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ErlNifFunc nif_funcs[] = { {"available", 0, available}, {"user_trace_s1", 1, user_trace_s1}, - {"user_trace_i4s4", 9, user_trace_i4s4} + {"user_trace_i4s4", 9, user_trace_i4s4}, + {"user_trace_n", 10, user_trace_n} }; ERL_NIF_INIT(dyntrace, nif_funcs, load, NULL, NULL, NULL) @@ -96,76 +102,25 @@ static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { #ifdef HAVE_USE_DTRACE - ErlNifBinary message_bin; - DTRACE_CHARBUF(messagebuf, MESSAGE_BUFSIZ + 1); - - if (DTRACE_ENABLED(user_trace_s1)) { - if (!enif_inspect_iolist_as_binary(env, argv[0], &message_bin) || - message_bin.size > MESSAGE_BUFSIZ) { - return atom_badarg; - } - memcpy(messagebuf, (char *) message_bin.data, message_bin.size); - messagebuf[message_bin.size] = '\0'; - DTRACE1(user_trace_s1, messagebuf); - return atom_true; - } else { - return atom_false; - } + return erl_nif_user_trace_s1(env, argc, argv); #else return atom_error; #endif } -void -get_string_maybe(ErlNifEnv *env, - const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz) +static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ErlNifBinary str_bin; - - if (!enif_inspect_iolist_as_binary(env, term, &str_bin) || - str_bin.size > bufsiz) { - *ptr = NULL; - } else { - memcpy(buf, (char *) str_bin.data, str_bin.size); - buf[str_bin.size] = '\0'; - *ptr = buf; - } +#ifdef HAVE_USE_DTRACE + return erl_nif_user_trace_i4s4(env, argc, argv); +#else + return atom_error; +#endif } -static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { #ifdef HAVE_USE_DTRACE - DTRACE_CHARBUF(procbuf, 32 + 1); - DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1); - char *utbuf = NULL; - ErlNifSInt64 i1, i2, i3, i4; - DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1); - DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1); - DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1); - DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1); - char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL; - - if (DTRACE_ENABLED(user_trace_i4s4)) { - dtrace_nifenv_str(env, procbuf); - get_string_maybe(env, argv[0], &utbuf, user_tagbuf, MESSAGE_BUFSIZ); - if (! enif_get_int64(env, argv[1], &i1)) - i1 = 0; - if (! enif_get_int64(env, argv[2], &i2)) - i2 = 0; - if (! enif_get_int64(env, argv[3], &i3)) - i3 = 0; - if (! enif_get_int64(env, argv[4], &i4)) - i4 = 0; - get_string_maybe(env, argv[5], &mbuf1, messagebuf1, MESSAGE_BUFSIZ); - get_string_maybe(env, argv[6], &mbuf2, messagebuf2, MESSAGE_BUFSIZ); - get_string_maybe(env, argv[7], &mbuf3, messagebuf3, MESSAGE_BUFSIZ); - get_string_maybe(env, argv[8], &mbuf4, messagebuf4, MESSAGE_BUFSIZ); - DTRACE10(user_trace_i4s4, procbuf, utbuf, - i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4); - return atom_true; - } else { - return atom_false; - } + return erl_nif_user_trace_n(env, argc, argv); #else return atom_error; #endif diff --git a/lib/runtime_tools/c_src/trace_ip_drv.c b/lib/runtime_tools/c_src/trace_ip_drv.c index 7f7ab8dd9d..6b77128761 100644 --- a/lib/runtime_tools/c_src/trace_ip_drv.c +++ b/lib/runtime_tools/c_src/trace_ip_drv.c @@ -590,8 +590,8 @@ static void *my_alloc(size_t size) void *ret; if ((ret = driver_alloc(size)) == NULL) { /* May or may not work... */ - fprintf(stderr, "Could not allocate %d bytes of memory in %s.", - (int) size, __FILE__); + fprintf(stderr, "Could not allocate %lu bytes of memory in %s.", + (unsigned long) size, __FILE__); exit(1); } return ret; @@ -605,8 +605,8 @@ static ErlDrvBinary *my_alloc_binary(int size) ErlDrvBinary *ret; if ((ret = driver_alloc_binary(size)) == NULL) { /* May or may not work... */ - fprintf(stderr, "Could not allocate a binary of %d bytes in %s.", - (int) size, __FILE__); + fprintf(stderr, "Could not allocate a binary of %lu bytes in %s.", + (unsigned long) size, __FILE__); exit(1); } return ret; diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile index c67f7bd1f6..d240b287c3 100644 --- a/lib/runtime_tools/doc/src/Makefile +++ b/lib/runtime_tools/doc/src/Makefile @@ -108,16 +108,16 @@ 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 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/runtime_tools/examples/user-probe-n.d b/lib/runtime_tools/examples/user-probe-n.d new file mode 100644 index 0000000000..06a3e5c9b9 --- /dev/null +++ b/lib/runtime_tools/examples/user-probe-n.d @@ -0,0 +1,44 @@ +/* example usage: dtrace -q -s /path/to/user-probe.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-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 + * 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% + */ + +erlang*:::user_trace-n0 +{ + printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + copyinstr(arg0), + arg1 == NULL ? "" : copyinstr(arg1), + arg2, arg3, arg4, arg5, + arg6 == NULL ? "" : copyinstr(arg6), + arg7 == NULL ? "" : copyinstr(arg7), + arg8 == NULL ? "" : copyinstr(arg8), + arg9 == NULL ? "" : copyinstr(arg9)); +} + +erlang*:::user_trace-n1 +{ + printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + copyinstr(arg0), + arg1 == NULL ? "" : copyinstr(arg1), + arg2, arg3, arg4, arg5, + arg6 == NULL ? "" : copyinstr(arg6), + arg7 == NULL ? "" : copyinstr(arg7), + arg8 == NULL ? "" : copyinstr(arg8), + arg9 == NULL ? "" : copyinstr(arg9)); +} + diff --git a/lib/runtime_tools/examples/user-probe-n.systemtap b/lib/runtime_tools/examples/user-probe-n.systemtap new file mode 100644 index 0000000000..6aa415bb67 --- /dev/null +++ b/lib/runtime_tools/examples/user-probe-n.systemtap @@ -0,0 +1,53 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-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 + * 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% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("user_trace-n0") +{ + printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + user_string($arg1), + $arg2 == NULL ? "" : user_string($arg2), + $arg3, $arg4, $arg5, $arg6, + $arg7 == NULL ? "" : user_string($arg7), + $arg8 == NULL ? "" : user_string($arg8), + $arg9 == NULL ? "" : user_string($arg9), + $arg9 == NULL ? "" : user_string($arg9)); +} + +probe process("beam").mark("user_trace-n1") +{ + printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + user_string($arg1), + $arg2 == NULL ? "" : user_string($arg2), + $arg3, $arg4, $arg5, $arg6, + $arg7 == NULL ? "" : user_string($arg7), + $arg8 == NULL ? "" : user_string($arg8), + $arg9 == NULL ? "" : user_string($arg9), + $arg9 == NULL ? "" : user_string($arg9)); +} diff --git a/lib/runtime_tools/examples/user-probe.systemtap b/lib/runtime_tools/examples/user-probe.systemtap index b41d7483ad..0482235324 100644 --- a/lib/runtime_tools/examples/user-probe.systemtap +++ b/lib/runtime_tools/examples/user-probe.systemtap @@ -28,12 +28,12 @@ * environment. */ -probe process("dyntrace.so").mark("user_trace-s1") +probe process("beam").mark("user_trace-s1") { printf("%s\n", user_string($arg1)); } -probe process("dyntrace.so").mark("user_trace-i4s4") +probe process("beam").mark("user_trace-i4s4") { printf("%s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", user_string($arg1), diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile index cb302b79c2..810e3e8741 100644 --- a/lib/runtime_tools/src/Makefile +++ b/lib/runtime_tools/src/Makefile @@ -99,14 +99,14 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLE_FILES) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/runtime_tools/src/dyntrace.erl b/lib/runtime_tools/src/dyntrace.erl index 388c7679b9..b4579fd5ce 100644 --- a/lib/runtime_tools/src/dyntrace.erl +++ b/lib/runtime_tools/src/dyntrace.erl @@ -6,23 +6,22 @@ %%% work on any operating system platform where user-space DTrace/Systemtap %%% (and in the future LttNG UST) probes are supported. %%% -%%% Use the `dyntrace:init()' function to load the NIF shared library and -%%% to initialize library's private state. -%%% %%% It is recommended that you use the `dyntrace:p()' function to add %%% Dynamic trace probes to your Erlang code. This function can accept up to %%% four integer arguments and four string arguments; the integer -%%% argument(s) must come before any string argument. For example: +%%% argument(s) must come before any string argument. +%%% +%%% If using DTrace, enable the dynamic trace probe using the 'dtrace' +%%% command, for example: +%%% +%%% dtrace -s /your/path/to/lib/runtime_tools-1.8.7/examples/user-probe.d +%%% +%%% Then, back at the Erlang shell, try this example: %%% ``` %%% 1> dyntrace:put_tag("GGOOOAAALL!!!!!"). %%% true -%%% 2> dyntrace:init(). -%%% ok -%%% -%%% % % % If using dtrace, enable the Dynamic trace probe using the 'dtrace' -%%% % % % command. %%% -%%% 3> dyntrace:p(7, 8, 9, "one", "four"). +%%% 2> dyntrace:p(7, 8, 9, "one", "four"). %%% true %%% ''' %%% @@ -38,15 +37,16 @@ -export([available/0, user_trace_s1/1, % TODO: unify with pid & tag args like user_trace_i4s4 - p/0, p/1, p/2, p/3, p/4, p/5, p/6, p/7, p/8]). + p/0, p/1, p/2, p/3, p/4, p/5, p/6, p/7, p/8, + pn/1, pn/2, pn/3, pn/4, pn/5, pn/6, pn/7, pn/8, pn/9]). -export([put_tag/1, get_tag/0, get_tag_data/0, spread_tag/1, restore_tag/1]). --export([scaff/0]). % Development only -export([user_trace_i4s4/9]). % Know what you're doing! -on_load(on_load/0). -type probe_arg() :: integer() | iolist(). -type int_p_arg() :: integer() | iolist() | undef. +-type n_probe_label() :: 0..1023. %% The *_maybe() types use atom() instead of a stricter 'undef' %% because user_trace_i4s4/9 is exposed to the outside world, and @@ -115,6 +115,16 @@ user_trace_s1(_Message) -> user_trace_i4s4(_, _, _, _, _, _, _, _, _) -> erlang:nif_error(nif_not_loaded). +-spec user_trace_n(n_probe_label(), iolist(), + integer_maybe(), integer_maybe(), + integer_maybe(), integer_maybe(), + iolist_maybe(), iolist_maybe(), + iolist_maybe(), iolist_maybe()) -> + true | false | error | badarg. + +user_trace_n(_, _, _, _, _, _, _, _, _, _) -> + erlang:nif_error(nif_not_loaded). + %%% %%% Erlang support functions %%% @@ -218,6 +228,106 @@ user_trace_int(I1, I2, I3, I4, S1, S2, S3, S4) -> false end. +-spec pn(n_probe_label()) -> true | false | error | badarg. + +pn(ProbeLabel) -> + user_trace_n_int(ProbeLabel, undef, undef, undef, undef, undef, undef, undef, undef). + +-spec pn(n_probe_label(), probe_arg()) -> true | false | error | badarg. + +pn(ProbeLabel, I1) when is_integer(I1) -> + user_trace_n_int(ProbeLabel, I1, undef, undef, undef, undef, undef, undef, undef); +pn(ProbeLabel, S1) -> + user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, undef, undef, undef). + +-spec pn(n_probe_label(), probe_arg(), probe_arg()) -> true | false | error | badarg. + +pn(ProbeLabel, I1, I2) when is_integer(I1), is_integer(I2) -> + user_trace_n_int(ProbeLabel, I1, I2, undef, undef, undef, undef, undef, undef); +pn(ProbeLabel, I1, S1) when is_integer(I1) -> + user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, undef, undef, undef); +pn(ProbeLabel, S1, S2) -> + user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, S2, undef, undef). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg()) -> true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, undef, undef, undef, undef, undef); +pn(ProbeLabel, I1, I2, S1) when is_integer(I1), is_integer(I2) -> + user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, undef, undef, undef); +pn(ProbeLabel, I1, S1, S2) when is_integer(I1) -> + user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, S2, undef, undef); +pn(ProbeLabel, S1, S2, S3) -> + user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, S2, S3, undef). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3, I4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, I4, undef, undef, undef, undef); +pn(ProbeLabel, I1, I2, I3, S1) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, undef, undef, undef); +pn(ProbeLabel, I1, I2, S1, S2) when is_integer(I1), is_integer(I2) -> + user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, S2, undef, undef); +pn(ProbeLabel, I1, S1, S2, S3) when is_integer(I1) -> + user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, S2, S3, undef); +pn(ProbeLabel, S1, S2, S3, S4) -> + user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, S2, S3, S4). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg()) -> + true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3, I4, S1) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, undef, undef, undef); +pn(ProbeLabel, I1, I2, I3, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, S2, undef, undef); +pn(ProbeLabel, I1, I2, S1, S2, S3) when is_integer(I1), is_integer(I2) -> + user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, S2, S3, undef); +pn(ProbeLabel, I1, S1, S2, S3, S4) when is_integer(I1) -> + user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, S2, S3, S4). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg()) -> + true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3, I4, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, undef, undef); +pn(ProbeLabel, I1, I2, I3, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, S2, S3, undef); +pn(ProbeLabel, I1, I2, S1, S2, S3, S4) when is_integer(I1), is_integer(I2) -> + user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, S2, S3, S4). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3, I4, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, undef); +pn(ProbeLabel, I1, I2, I3, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, S2, S3, S4). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, S4). + +-spec user_trace_n_int(n_probe_label(), + int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg(), + int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg()) -> + true | false | error | badarg. + +user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, S4) -> + UTag = get_tag(), + try + user_trace_n(ProbeLabel, UTag, I1, I2, I3, I4, S1, S2, S3, S4) + catch + error:nif_not_loaded -> + false + end. + -spec put_tag(undefined | iodata()) -> binary() | undefined. put_tag(Data) -> erlang:dt_put_tag(unicode:characters_to_binary(Data)). @@ -240,40 +350,3 @@ spread_tag(B) -> -spec restore_tag(true | {non_neg_integer(), binary() | []}) -> true. restore_tag(T) -> erlang:dt_restore_tag(T). - - -%% Scaffolding to write tedious code: quick brute force and not 100% correct. - -scaff_int_args(N) -> - L = lists:sublist(["I1", "I2", "I3", "I4"], N), - [string:join(L, ", ")]. - -scaff_int_guards(N) -> - L = lists:sublist(["is_integer(I1)", "is_integer(I2)", "is_integer(I3)", - "is_integer(I4)"], N), - lists:flatten(string:join(L, ", ")). - -scaff_char_args(N) -> - L = lists:sublist(["S1", "S2", "S3", "S4"], N), - [string:join(L, ", ")]. - -scaff_fill(N) -> - [string:join(lists:duplicate(N, "undef"), ", ")]. - -scaff() -> - L = [begin - IntArgs = scaff_int_args(N_int), - IntGuards = scaff_int_guards(N_int), - IntFill = scaff_fill(4 - N_int), - CharArgs = scaff_char_args(N_char), - CharFill = scaff_fill(4 - N_char), - InArgs = string:join(IntArgs ++ CharArgs, ", "), - OutArgs = string:join(IntArgs ++ IntFill ++ CharArgs ++ CharFill, - ", "), - {N_int + N_char, - lists:flatten([io_lib:format("p(~s) when ~s ->\n", - [InArgs, IntGuards]), - io_lib:format(" user_trace_int(~s);\n", [OutArgs]) - ])} - end || N_int <- [0,1,2,3,4], N_char <- [0,1,2,3,4]], - [io:format("%%~p\n~s", [N, Str]) || {N, Str} <- lists:sort(L)]. diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile index 3e50ba02ca..4979b9c7b1 100644 --- a/lib/runtime_tools/test/Makefile +++ b/lib/runtime_tools/test/Makefile @@ -57,10 +57,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) runtime_tools.spec runtime_tools.cover $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(EMAKEFILE) runtime_tools.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) runtime_tools.spec runtime_tools.cover $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) runtime_tools.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/sasl/doc/src/Makefile b/lib/sasl/doc/src/Makefile index eb880ccb78..b0ec671adc 100644 --- a/lib/sasl/doc/src/Makefile +++ b/lib/sasl/doc/src/Makefile @@ -116,18 +116,18 @@ 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 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man4 - $(INSTALL_DATA) $(MAN4_FILES) $(RELEASE_PATH)/man/man4 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4" + $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/sasl/examples/src/Makefile b/lib/sasl/examples/src/Makefile index 9cf0d4c25d..47c8626205 100644 --- a/lib/sasl/examples/src/Makefile +++ b/lib/sasl/examples/src/Makefile @@ -63,10 +63,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/src - $(INSTALL_DIR) $(RELSYSDIR)/examples/ebin - (cd ..; tar cf - src ebin | (cd $(RELSYSDIR)/examples; tar xf -)) - chmod -R ug+w $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/ebin" + (cd ..; tar cf - src ebin | (cd "$(RELSYSDIR)/examples"; tar xf -)) + chmod -R ug+w "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/sasl/src/Makefile b/lib/sasl/src/Makefile index 9a5d1e42d9..cae8146ebc 100644 --- a/lib/sasl/src/Makefile +++ b/lib/sasl/src/Makefile @@ -91,10 +91,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) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile index 91a8c42484..bb3f82f411 100644 --- a/lib/sasl/test/Makefile +++ b/lib/sasl/test/Makefile @@ -85,10 +85,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) sasl.spec sasl.cover $(EMAKEFILE) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cfh - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) sasl.spec sasl.cover $(EMAKEFILE) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cfh - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl index 6942ec21ea..634218e3fb 100644 --- a/lib/sasl/test/installer.erl +++ b/lib/sasl/test/installer.erl @@ -876,7 +876,9 @@ trace_disallowed_calls(Node) -> MasterProc = self(), rpc:call(Node,dbg,tracer,[process,{fun(T,_) -> MasterProc ! T end,[]}]), rpc:call(Node,dbg,p,[all,call]), - rpc:call(Node,dbg,tp,[file,[{'_',[],[{message,{caller}}]}]]). + rpc:call(Node,dbg,tp,[file,[{'_',[],[{message,{caller}}]}]]), + %% File:native_name_encoding/0 is a BIF and OK to use + rpc:call(Node,dbg,ctp,[file,native_name_encoding,0]). check_disallowed_calls(TestNode,Line) -> receive diff --git a/lib/snmp/doc/src/Makefile b/lib/snmp/doc/src/Makefile index 8645886590..6c58ae994a 100644 --- a/lib/snmp/doc/src/Makefile +++ b/lib/snmp/doc/src/Makefile @@ -170,20 +170,20 @@ $(MAN1DIR)/snmpc.1: snmpc_cmd.xml 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/man1 - $(INSTALL_DATA) $(MAN1DIR)/* $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man7 - $(INSTALL_DATA) $(MAN7DIR)/* $(RELEASE_PATH)/man/man7 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN1DIR)/* "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man7" + $(INSTALL_DATA) $(MAN7DIR)/* "$(RELEASE_PATH)/man/man7" release_spec: diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 2d045faa0f..442837d57d 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -34,6 +34,98 @@ <section> + <title>SNMP Development Toolkit 4.22.1</title> + <p>Version 4.22.1 supports code replacement in runtime from/to + version 4.22, 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and + 4.21. </p> + + <section> + <title>Improvements and new features</title> +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>[agent] Sematic fixes to SNMP-USER-BASED-SM-MIB. + The semantics allow the <c>usmUserAuthKeyChange</c> and + <c>usmUserPrivKeyChange</c> objects to be written to in the + same set requests that also creates and clones the user. + This was not possible beforehand, causing test tools checking + semantic SNMPv3 behaviour to fail on a lot of test cases. </p> + <p>Furthermore, once the user has been cloned by writing to an + instance of <c>usmUserCloneFrom</c>, further set-operations to + the same object will not return an error, but be no-ops. + Especially, it must be avoided to copy security parameters + again (possibly even from a different user). </p> + <p>Stefan Zegenhagen</p> + <p>Own Id: OTP-10166</p> + </item> + + <item> + <p>[agent] Errors in <c>vacmAccessTable</c> RowStatus handling. + There are problems with the handling of vacmAccessTableStatus + that cause some SNMP test suites to report errors. + Most notably, erroneous set operations frequently cause "genErr" + errors to be returned. These "genErr" errors are usually caused + by badmatch exceptions coming from + <c>{ok, Row} = snmpa_vacm:get_row(RowIndex)</c> + if the row does not exist. </p> + <p>The semantics of the RowStatus handling in that table has + been adjusted to be compliant with the RowStatus + textual description of SNPMv2-TC MIB. </p> + <p>Stefan Zegenhagen</p> + <p>Own Id: OTP-10164</p> + </item> + </list> + + </section> + + <section> + <title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>[agent] Fix walk over vacmAccessTable. + Fix the get_next implementation of vacmAccessTable to + return all table entries. </p> + <p>The get_next implementation of vacmAccessTable did not return + all available table data. Instead, it only returned the first + column for each row, and all columns for the last row available. </p> + <p>Stefan Zegenhagen</p> + <p>Own Id: OTP-10165</p> + </item> + + <item> + <p>[manager] + <seealso marker="snmpm#log_to_io">snmpm:log_to_io/6</seealso> + did not use the LogName argument. </p> + <p>Own Id: OTP-10066</p> + </item> + + <item> + <p>Incorrect TimeTicks decode. Also bad handling of + invalid encode (value outside of value range) for both + <c>TimeTicks</c> and <c>Unsigned32</c>. </p> + <p>Own Id: OTP-10132</p> + </item> + + </list> + + </section> + + <section> + <title>Incompatibilities</title> + <p>-</p> + </section> + + </section> <!-- 4.22.1 --> + + + <section> <title>SNMP Development Toolkit 4.22</title> <p>Version 4.22 supports code replacement in runtime from/to version 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and 4.21. </p> diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml index 340f2f1fa7..eec53162a1 100644 --- a/lib/snmp/doc/src/snmp_config.xml +++ b/lib/snmp/doc/src/snmp_config.xml @@ -963,7 +963,8 @@ Manager snmp config: 7b. User name? hobbes 7c. Security name? [hobbes] 7d. Authentication protocol (no/sha/md5)? [no] sha -7e Authentication [sha] key (length 0 or 20)? [""] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] +7e Authentication [sha] key (length 0 or 20)? [""] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, \ + 17,18,19,20] 7d. Priv protocol (no/des/aes)? [no] des 7f Priv [des] key (length 0 or 16)? [""] 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 7. Configure an usm user handled by this manager (y/n)? [y] n diff --git a/lib/snmp/doc/src/snmpa_network_interface_filter.xml b/lib/snmp/doc/src/snmpa_network_interface_filter.xml index 10419517dd..84953c5270 100644 --- a/lib/snmp/doc/src/snmpa_network_interface_filter.xml +++ b/lib/snmp/doc/src/snmpa_network_interface_filter.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> @@ -84,7 +84,9 @@ <title>DATA TYPES</title> <code type="none"> port() = integer() > 0 -pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | 'set-request' | trap | 'get-bulk-request' | 'inform-request' | report +pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | + 'set-request' | trap | 'get-bulk-request' | 'inform-request' | + report </code> <marker id="accept_recv"></marker> </section> diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index 9bbb6cdbdb..8ab3be8e18 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -173,16 +173,16 @@ sec_level() = noAuthNoPriv | authNoPriv | authPriv </type> <desc> <p>Register the manager entity (=user) responsible for specific - agent(s). </p> + agent(s). </p> <p><c>Module</c> is the callback module (snmpm_user behaviour) which - will be called whenever something happens (detected - agent, incoming reply or incoming trap/notification). - Note that this could have already been done as a - consequence of the node config. (see users.conf).</p> + will be called whenever something happens (detected + agent, incoming reply or incoming trap/notification). + Note that this could have already been done as a + consequence of the node config. (see users.conf).</p> - <p>The argument <c>DefaultAgentConfig</c> is used as default values when - this user register agents.</p> + <p>The argument <c>DefaultAgentConfig</c> is used as default + values when this user register agents.</p> <p>The type of <c>Val</c> depends on <c>Item</c>: </p> <code type="none"><![CDATA[ diff --git a/lib/snmp/doc/src/snmpm_network_interface_filter.xml b/lib/snmp/doc/src/snmpm_network_interface_filter.xml index 5f80cec94e..4dc133dd71 100644 --- a/lib/snmp/doc/src/snmpm_network_interface_filter.xml +++ b/lib/snmp/doc/src/snmpm_network_interface_filter.xml @@ -82,7 +82,9 @@ <title>DATA TYPES</title> <code type="none"> port() = integer() > 0 -pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | 'set-request' | trap | 'get-bulk-request' | 'inform-request' | report | trappdu +pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | + 'set-request' | trap | 'get-bulk-request' | 'inform-request' | + report | trappdu </code> <marker id="accept_recv"></marker> </section> diff --git a/lib/snmp/examples/ex1/Makefile b/lib/snmp/examples/ex1/Makefile index 1a55ba0470..996b686e0e 100644 --- a/lib/snmp/examples/ex1/Makefile +++ b/lib/snmp/examples/ex1/Makefile @@ -46,7 +46,7 @@ ERL_COMPILE_FLAGS += -I../include \ # Release directory specification # ---------------------------------------------------- RELSYSDIR = $(RELEASE_PATH)/lib/snmp-$(VSN) -EXAMPLE_RELSYSDIR = $(RELSYSDIR)/examples +EXAMPLE_RELSYSDIR = "$(RELSYSDIR)/examples" EX1_RELSYSDIR = $(EXAMPLE_RELSYSDIR)/ex1 # ---------------------------------------------------- diff --git a/lib/snmp/examples/ex2/Makefile b/lib/snmp/examples/ex2/Makefile index 7fbd7f530b..090b9274d4 100644 --- a/lib/snmp/examples/ex2/Makefile +++ b/lib/snmp/examples/ex2/Makefile @@ -47,7 +47,7 @@ ERL_COMPILE_FLAGS += -I../include \ # Release directory specification # ---------------------------------------------------- RELSYSDIR = $(RELEASE_PATH)/lib/snmp-$(VSN) -EXAMPLE_RELSYSDIR = $(RELSYSDIR)/examples +EXAMPLE_RELSYSDIR = "$(RELSYSDIR)/examples" EX2_RELSYSDIR = $(EXAMPLE_RELSYSDIR)/ex2 # ---------------------------------------------------- diff --git a/lib/snmp/mibs/Makefile.in b/lib/snmp/mibs/Makefile.in index 993a67c6f2..c74c0b2ac4 100644 --- a/lib/snmp/mibs/Makefile.in +++ b/lib/snmp/mibs/Makefile.in @@ -211,7 +211,7 @@ info: @echo "" @echo "SNMP_VSN = $(SNMP_VSN)" @echo "VSN = $(VSN)" - @echo "RELSYSDIR = $(RELSYSDIR)" + @echo "RELSYSDIR = "$(RELSYSDIR)"" v1/%.mib.v1: %.mib $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 -o $@ $< @@ -223,17 +223,17 @@ v1/%.mib.v1: %.mib $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/mibs - $(INSTALL_DIR) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DATA) $(MIB_FILES) $(RELSYSDIR)/mibs - $(INSTALL_DATA) $(STD_v2_MIB_FILES) $(RELSYSDIR)/mibs - $(INSTALL_DATA) $(FUNCS_FILES) $(RELSYSDIR)/mibs - $(INSTALL_DATA) $(STD_v1_MIB_FILES) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DATA) $(V1_MIB_FILES) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/priv/mibs - $(INSTALL_DATA) $(BIN_TARGETS) $(RELSYSDIR)/priv/mibs + $(INSTALL_DIR) "$(RELSYSDIR)/mibs" + $(INSTALL_DIR) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DATA) $(MIB_FILES) "$(RELSYSDIR)/mibs" + $(INSTALL_DATA) $(STD_v2_MIB_FILES) "$(RELSYSDIR)/mibs" + $(INSTALL_DATA) $(FUNCS_FILES) "$(RELSYSDIR)/mibs" + $(INSTALL_DATA) $(STD_v1_MIB_FILES) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DATA) $(V1_MIB_FILES) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/mibs" + $(INSTALL_DATA) $(BIN_TARGETS) "$(RELSYSDIR)/priv/mibs" release_docs_spec: diff --git a/lib/snmp/priv/conf/agent/Makefile b/lib/snmp/priv/conf/agent/Makefile index ee3a915c2b..c7921bacc2 100644 --- a/lib/snmp/priv/conf/agent/Makefile +++ b/lib/snmp/priv/conf/agent/Makefile @@ -55,9 +55,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/conf - $(INSTALL_DIR) $(RELSYSDIR)/priv/conf/agent - $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/priv/conf/agent + $(INSTALL_DIR) "$(RELSYSDIR)/priv/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/conf/agent" + $(INSTALL_DATA) $(CONF_FILES) "$(RELSYSDIR)/priv/conf/agent" release_docs_spec: diff --git a/lib/snmp/priv/conf/manager/Makefile b/lib/snmp/priv/conf/manager/Makefile index 16355f0ff3..ba5a74c3fe 100644 --- a/lib/snmp/priv/conf/manager/Makefile +++ b/lib/snmp/priv/conf/manager/Makefile @@ -55,9 +55,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/conf - $(INSTALL_DIR) $(RELSYSDIR)/priv/conf/manager - $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/priv/conf/manager + $(INSTALL_DIR) "$(RELSYSDIR)/priv/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/conf/manager" + $(INSTALL_DATA) $(CONF_FILES) "$(RELSYSDIR)/priv/conf/manager" release_docs_spec: diff --git a/lib/snmp/src/agent/Makefile b/lib/snmp/src/agent/Makefile index a67fe4d17c..9a982edf91 100644 --- a/lib/snmp/src/agent/Makefile +++ b/lib/snmp/src/agent/Makefile @@ -128,14 +128,14 @@ info: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/agent - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/agent - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/agent" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src/agent" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \ - $(RELSYSDIR)/ebin -# $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include + "$(RELSYSDIR)/ebin" +# $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(EXT_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl index 2e801622e7..3c4ba1af66 100644 --- a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl +++ b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl @@ -54,6 +54,7 @@ %% Columns not accessible via SNMP -define(usmUserAuthKey, 14). -define(usmUserPrivKey, 15). +-define(is_cloning, 16). %%%----------------------------------------------------------------- @@ -564,7 +565,9 @@ usmUserTable(set, RowIndex, Cols0) -> {ok, Cols} -> ?vtrace("usmUserTable(set) -> verified" "~n Cols: ~p", [Cols]), - NCols = pre_set(RowIndex, Cols), + % check whether we're cloning. if so, get cloned params and add a few + % defaults that might be needed. + NCols = pre_set(RowIndex, validate_clone_from(RowIndex, Cols)), ?vtrace("usmUserTable(set) -> pre-set: " "~n NCols: ~p", [NCols]), %% NOTE: The NCols parameter is sent to snmp_generic, but not to @@ -730,30 +733,40 @@ validate_is_set_ok(Error, _RowIndex, _Cols) -> Error. do_validate_is_set_ok(RowIndex, Cols) -> - validate_clone_from(RowIndex, Cols), - validate_auth_protocol(RowIndex, Cols), - validate_auth_key_change(RowIndex, Cols), - validate_own_auth_key_change(RowIndex, Cols), - validate_priv_protocol(RowIndex, Cols), - validate_priv_key_change(RowIndex, Cols), - validate_own_priv_key_change(RowIndex, Cols), + NCols = validate_clone_from(RowIndex, Cols), + validate_auth_protocol(RowIndex, NCols), + validate_auth_key_change(RowIndex, NCols), + validate_own_auth_key_change(RowIndex, NCols), + validate_priv_protocol(RowIndex, NCols), + validate_priv_key_change(RowIndex, NCols), + validate_own_priv_key_change(RowIndex, NCols), ok. pre_set(RowIndex, Cols) -> + %% Remove the ?is_cloning member again; it must no longer be + %% present. + Cols0 = key1delete(?is_cloning, Cols), %% Possibly initialize the usmUserSecurityName and privacy keys case snmp_generic:table_row_exists(db(usmUserTable), RowIndex) of - true -> Cols; + true -> Cols0; false -> SecName = get_user_name(RowIndex), - [{?usmUserSecurityName, SecName} | Cols] ++ - [{?usmUserAuthKey, ""}, - {?usmUserPrivKey, ""}] + Cols1 = [{?usmUserSecurityName, SecName} | Cols0], + case proplists:get_value(?is_cloning, Cols) of + true -> + % the row is just being cloned. the cloned user's + % passwords are already present in Cols and must + % not be overwritten. + Cols1; + _ -> + Cols1 ++ [{?usmUserAuthKey, ""}, + {?usmUserPrivKey, ""}] + end end. validate_set({noError, 0}, RowIndex, Cols) -> %% Now, all is_set_ok validation steps have been executed. So %% everything is ready for the set. - set_clone_from(RowIndex, Cols), set_auth_key_change(RowIndex, Cols), set_own_auth_key_change(RowIndex, Cols), set_priv_key_change(RowIndex, Cols), @@ -769,7 +782,7 @@ validate_set(Error, _RowIndex, _Cols) -> %% no further checks. %%----------------------------------------------------------------- validate_clone_from(RowIndex, Cols) -> - case lists:keysearch(?usmUserCloneFrom, 1, Cols) of + case key1search(?usmUserCloneFrom, Cols) of {value, {_Col, RowPointer}} -> RowIndex2 = extract_row(RowPointer), OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), @@ -778,35 +791,63 @@ validate_clone_from(RowIndex, Cols) -> case OldCloneFrom of {value, Val} when Val /= noinit -> %% This means that the cloning is already done... - ok; + no_cloning(Cols); _ -> - %% Otherwise, we must check the CloneFrom value - case snmp_generic:table_get_element(db(usmUserTable), - RowIndex2, - ?usmUserStatus) of - {value, ?'RowStatus_active'} -> ok; - _ -> inconsistentName(?usmUserCloneFrom) - end + %% Otherwise, we must check the CloneFrom value. It + %% must relate to a usmUserEntry that exists and is active. + case snmp_generic:table_get_row(db(usmUserTable), RowIndex2) of + CloneFromRow when is_tuple(CloneFromRow) -> + case element(?usmUserStatus, CloneFromRow) of + ?'RowStatus_active' -> + get_cloned_cols(CloneFromRow, Cols); + _ -> + inconsistentName(?usmUserCloneFrom) + end; + undefined -> + inconsistentName(?usmUserCloneFrom) + end end; false -> - ok + % no ?usmUserCloneFrom specified, don't modify columns + no_cloning(Cols) end. +get_cloned_cols(CloneFromRow, Cols) -> + % initialize cloned columns with data from CloneFromRow + % and overwrite that again with data found in Cols + AuthP = element(?usmUserAuthProtocol, CloneFromRow), + PrivP = element(?usmUserPrivProtocol, CloneFromRow), + AuthK = element(?usmUserAuthKey, CloneFromRow), + PrivK = element(?usmUserPrivKey, CloneFromRow), + ClonedCols = [{?usmUserAuthProtocol, AuthP}, + {?usmUserPrivProtocol, PrivP}, + {?usmUserAuthKey, AuthK}, + {?usmUserPrivKey, PrivK}, + {?is_cloning, true} + ], + Func = fun({Col, _} = Item, NCols) -> + key1store(Col, NCols, Item) + end, + Cols1 = lists:foldl(Func, ClonedCols, Cols), + key1sort(Cols1). + +no_cloning(Cols0) -> + Cols1 = key1delete(?usmUserCloneFrom, Cols0), + key1delete(?is_cloning, Cols1). + validate_auth_protocol(RowIndex, Cols) -> - case lists:keysearch(?usmUserAuthProtocol, 1, Cols) of + case key1search(?usmUserAuthProtocol, Cols) of {value, {_Col, AuthProtocol}} -> - %% Check if the row has been cloned; we can't check the + %% Check if the row is being cloned; we can't check the %% old value of authProtocol, because if the row was %% createAndWaited, the default value would have been %% written (usmNoAuthProtocol). - OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), - RowIndex, - ?usmUserCloneFrom), - case OldCloneFrom of - {value, Val} when Val /= noinit -> - %% This means that the cloning is already done; set is ok - %% if new protocol is usmNoAuthProtocol + IsCloning = proplists:get_value(?is_cloning, Cols, false), + if + not IsCloning -> + %% This means that the row is not being cloned right + %% now; set is ok if new protocol is usmNoAuthProtocol case AuthProtocol of ?usmNoAuthProtocol -> %% Check that the Priv protocl is noPriv @@ -821,7 +862,7 @@ validate_auth_protocol(RowIndex, Cols) -> _ -> wrongValue(?usmUserAuthProtocol) end; - _ -> + true -> %% Otherwise, check that the new protocol is known, %% and that the system we're running supports the %% hash function. @@ -867,7 +908,7 @@ validate_own_priv_key_change(RowIndex, Cols) -> %% Check that the requesting user is the same as the modified user validate_requester(RowIndex, Cols, KeyChangeCol) -> - case lists:keysearch(KeyChangeCol, 1, Cols) of + case key1search(KeyChangeCol, Cols) of {value, _} -> case get(sec_model) of % Check the securityModel in the request ?SEC_USM -> ok; @@ -890,17 +931,14 @@ validate_requester(RowIndex, Cols, KeyChangeCol) -> end. validate_key_change(RowIndex, Cols, KeyChangeCol, Type) -> - case lists:keysearch(KeyChangeCol, 1, Cols) of + case key1search(KeyChangeCol, Cols) of {value, {_Col, KeyC}} -> %% Check if the row has been cloned; or if it is cloned in %% this set-operation. OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), RowIndex, ?usmUserCloneFrom), - IsClonePresent = case lists:keysearch(?usmUserCloneFrom, 1, Cols) of - {value, _} -> true; - false -> false - end, + IsClonePresent = proplists:get_value(?is_cloning, Cols, false), %% Set is ok if 1) the user already is created, 2) this is %% a new user, which has been cloned, or is about to be %% cloned. @@ -912,7 +950,7 @@ validate_key_change(RowIndex, Cols, KeyChangeCol, Type) -> %% The user is cloned in this operation ok; _ -> - %% The user doen't exist, or hasn't been cloned, + %% The user doesn't exist, or hasn't been cloned, %% and is not cloned in this operation. inconsistentName(KeyChangeCol) end, @@ -939,17 +977,15 @@ validate_key_change(RowIndex, Cols, KeyChangeCol, Type) -> end. validate_priv_protocol(RowIndex, Cols) -> - case lists:keysearch(?usmUserPrivProtocol, 1, Cols) of + case key1search(?usmUserPrivProtocol, Cols) of {value, {_Col, PrivProtocol}} -> %% Check if the row has been cloned; we can't check the %% old value of privhProtocol, because if the row was %% createAndWaited, the default value would have been %% written (usmNoPrivProtocol). - OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), - RowIndex, - ?usmUserCloneFrom), - case OldCloneFrom of - {value, Val} when Val /= noinit -> + IsCloning = proplists:get_value(?is_cloning, Cols, false), + if + not IsCloning -> %% This means that the cloning is already done; set is ok %% if new protocol is usmNoPrivProtocol case PrivProtocol of @@ -962,7 +998,7 @@ validate_priv_protocol(RowIndex, Cols) -> _ -> wrongValue(?usmUserPrivProtocol) end; - _ -> + true -> %% Otherwise, check that the new protocol is known, %% and that the system we're running supports the %% crypto function. @@ -1005,31 +1041,6 @@ validate_priv_protocol(RowIndex, Cols) -> end. -set_clone_from(RowIndex, Cols) -> - %% If CloneFrom is modified, do the cloning. - case lists:keysearch(?usmUserCloneFrom, 1, Cols) of - {value, {_Col, RowPointer}} -> - RowIndex2 = extract_row(RowPointer), % won't fail - CloneRow = snmp_generic:table_get_row(db(usmUserTable), RowIndex2, - foi(usmUserTable)), - AuthP = element(?usmUserAuthProtocol, CloneRow), - PrivP = element(?usmUserPrivProtocol, CloneRow), - AuthK = element(?usmUserAuthKey, CloneRow), - PrivK = element(?usmUserPrivKey, CloneRow), - SCols = [{?usmUserAuthProtocol, AuthP}, - {?usmUserPrivProtocol, PrivP}, - {?usmUserAuthKey, AuthK}, - {?usmUserPrivKey, PrivK}], - case snmp_generic:table_set_elements(db(usmUserTable), - RowIndex, - SCols) of - true -> ok; - false -> {commitFailed, ?usmUserCloneFrom} - end; - false -> - ok - end. - set_auth_key_change(RowIndex, Cols) -> set_key_change(RowIndex, Cols, ?usmUserAuthKeyChange, auth). @@ -1043,7 +1054,7 @@ set_own_priv_key_change(RowIndex, Cols) -> set_key_change(RowIndex, Cols, ?usmUserOwnPrivKeyChange, priv). set_key_change(RowIndex, Cols, KeyChangeCol, Type) -> - case lists:keysearch(KeyChangeCol, 1, Cols) of + case key1search(KeyChangeCol, Cols) of {value, {_Col, KeyChange}} -> KeyCol = case Type of auth -> ?usmUserAuthKey; @@ -1071,11 +1082,11 @@ extract_row([H | T], [H | T2]) -> extract_row(T, T2); extract_row([], [?usmUserSecurityName | T]) -> T; extract_row(_, _) -> wrongValue(?usmUserCloneFrom). -%% Pre: the user exixt +%% Pre: the user exists or is being cloned in this operation get_auth_proto(RowIndex, Cols) -> - %% The protocol can be chanegd by the request too, otherwise, + %% The protocol can be changed by the request too, otherwise, %% check the stored protocol. - case lists:keysearch(?usmUserAuthProtocol, 1, Cols) of + case key1search(?usmUserAuthProtocol, Cols) of {value, {_, Protocol}} -> Protocol; false -> @@ -1090,11 +1101,11 @@ get_auth_proto(RowIndex, Cols) -> end end. -%% Pre: the user exixt +%% Pre: the user exists or is being cloned in this operation get_priv_proto(RowIndex, Cols) -> - %% The protocol can be chanegd by the request too, otherwise, + %% The protocol can be changed by the request too, otherwise, %% check the stored protocol. - case lists:keysearch(?usmUserPrivProtocol, 1, Cols) of + case key1search(?usmUserPrivProtocol, Cols) of {value, {_, Protocol}} -> Protocol; false -> @@ -1232,6 +1243,27 @@ set_sname(_) -> %% Keep it, if already set. error(Reason) -> throw({error, Reason}). + +%%----------------------------------------------------------------- +%% lists key-function(s) wrappers + +-compile({inline,key1delete/2}). +key1delete(Key, List) -> + lists:keydelete(Key, 1, List). + +-compile({inline,key1search/2}). +key1search(Key, List) -> + lists:keysearch(Key, 1, List). + +-compile({inline,key1store/3}). +key1store(Key, List, Elem) -> + lists:keystore(Key, 1, List, Elem). + +-compile({inline,key1sort/1}). +key1sort(List) -> + lists:keysort(1, List). + + %%----------------------------------------------------------------- info_msg(F, A) -> diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl index 479a44795f..436f15eb9c 100644 --- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl +++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl @@ -565,45 +565,85 @@ vacmAccessTable(is_set_ok, RowIndex, Cols0) -> case (catch verify_vacmAccessTable_cols(Cols0, [])) of {ok, Cols} -> IsValidKey = is_valid_key(RowIndex), - case lists:keysearch(?vacmAccessStatus, 1, Cols) of - %% Ok, if contextMatch is init - {value, {Col, ?'RowStatus_active'}} -> - {ok, Row} = snmpa_vacm:get_row(RowIndex), - case element(?vacmAContextMatch, Row) of - noinit -> {inconsistentValue, Col}; - _ -> {noError, 0} + StatusCol = lists:keyfind(?vacmAccessStatus, 1, Cols), + MaybeRow = snmpa_vacm:get_row(RowIndex), + case {StatusCol, MaybeRow} of + {{Col, ?'RowStatus_active'}, false} -> + %% row does not yet exist => inconsistentValue + %% (see SNMPv2-TC.mib, RowStatus textual convention) + {inconsistentValue, Col}; + {{Col, ?'RowStatus_active'}, {ok, Row}} -> + %% Ok, if contextMatch is init + case element(?vacmAContextMatch, Row) of + noinit -> + %% check whether ContextMatch is being set in + %% the same operation + case proplists:get_value(?vacmAccessContextMatch, + Cols) of + undefined -> + %% no, we can't make this row active yet + {inconsistentValue, Col}; + _ -> + %% ok, activate the row + {noError, 0} + end; + _ -> + {noError, 0} end; - {value, {Col, ?'RowStatus_notInService'}} -> % Ok, if not notReady - {ok, Row} = snmpa_vacm:get_row(RowIndex), + {{Col, ?'RowStatus_notInService'}, false} -> + %% row does not yet exist => inconsistentValue + %% (see SNMPv2-TC.mib, RowStatus textual convention) + {inconsistentValue, Col}; + {{Col, ?'RowStatus_notInService'}, {ok, Row}} -> + %% Ok, if not notReady case element(?vacmAStatus, Row) of - ?'RowStatus_notReady' -> {inconsistentValue, Col}; - _ -> {noError, 0} + ?'RowStatus_notReady' -> + {inconsistentValue, Col}; + _ -> + {noError, 0} end; - {value, {Col, ?'RowStatus_notReady'}} -> % never ok! + {{Col, ?'RowStatus_notReady'}, _} -> + %% never ok! {inconsistentValue, Col}; - {value, {Col, ?'RowStatus_createAndGo'}} -> % ok, if it doesn't exist + {{Col, ?'RowStatus_createAndGo'}, false} -> + %% ok, if it doesn't exist Res = lists:keysearch(?vacmAccessContextMatch, 1, Cols), - case snmpa_vacm:get_row(RowIndex) of - false when (IsValidKey =:= true) andalso - is_tuple(Res) -> {noError, 0}; - false -> {noCreation, Col}; % Bad RowIndex - _ -> {inconsistentValue, Col} + if + IsValidKey =/= true -> + %% bad RowIndex => noCreation + {noCreation, Col}; + is_tuple(Res) -> + %% required field is present => noError + {noError, 0}; + true -> + %% required field is missing => inconsistentValue + {inconsistentValue, Col} end; - {value, {Col, ?'RowStatus_createAndWait'}} -> % ok, if it doesn't exist - case snmpa_vacm:get_row(RowIndex) of - false when (IsValidKey =:= true) -> {noError, 0}; - false -> {noCreation, Col}; % Bad RowIndex - _ -> {inconsistentValue, Col} + {{Col, ?'RowStatus_createAndGo'}, _} -> + %% row already exists => inconsistentValue + {inconsistentValue, Col}; + {{Col, ?'RowStatus_createAndWait'}, false} -> + %% ok, if it doesn't exist + if + IsValidKey =:= true -> + %% RowIndex is valid => noError + {noError, 0}; + true -> + {noCreation, Col} end; - {value, {_Col, ?'RowStatus_destroy'}} -> % always ok! + {{Col, ?'RowStatus_createAndWait'}, _} -> + %% Row already exists => inconsistentValue + {inconsistentValue, Col}; + {value, {_Col, ?'RowStatus_destroy'}} -> + %% always ok! {noError, 0}; - _ -> % otherwise, it's a change; it must exist - case snmpa_vacm:get_row(RowIndex) of - {ok, _} -> - {noError, 0}; - false -> - {inconsistentName, element(1, hd(Cols))} - end + {_, false} -> + %% otherwise, it's a row change; + %% row does not exist => inconsistentName + {inconsistentName, element(1, hd(Cols))}; + _ -> + %% row change and row exists => noError + {noError, 0} end; Error -> Error @@ -734,10 +774,15 @@ do_vacmAccessTable_set(RowIndex, Cols) -> %% Cols are sorted, and all columns are > 3. +do_get_next(_RowIndex, []) -> + % Cols can be empty because we're called for each + % output of split_cols(); and one of that may well + % be empty. + []; do_get_next(RowIndex, Cols) -> case snmpa_vacm:get_next_row(RowIndex) of {NextIndex, Row} -> - F1 = fun(Col) when Col < ?vacmAccessStatus -> + F1 = fun(Col) when Col =< ?vacmAccessStatus -> {[Col | NextIndex], element(Col-3, Row)}; (_) -> endOfTable @@ -745,9 +790,9 @@ do_get_next(RowIndex, Cols) -> lists:map(F1, Cols); false -> case snmpa_vacm:get_next_row([]) of - {_NextIndex, Row} -> + {NextIndex2, Row} -> F2 = fun(Col) when Col < ?vacmAccessStatus -> - {[Col+1 | RowIndex], element(Col-2, Row)}; + {[Col+1 | NextIndex2], element(Col-2, Row)}; (_) -> endOfTable end, diff --git a/lib/snmp/src/app/Makefile b/lib/snmp/src/app/Makefile index d89eb4e723..7a16d42c30 100644 --- a/lib/snmp/src/app/Makefile +++ b/lib/snmp/src/app/Makefile @@ -127,14 +127,14 @@ $(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/app - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/app - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/app" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src/app" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \ - $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include + "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXT_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 8360d88c94..593ddd82bd 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,8 +22,18 @@ %% ----- U p g r a d e ------------------------------------------------------- [ + {"4.22", + [ + {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, []} + ] + }, {"4.21.7", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -50,6 +60,8 @@ }, {"4.21.6", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -76,6 +88,8 @@ }, {"4.21.5", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -107,6 +121,8 @@ }, {"4.21.4", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -141,6 +157,8 @@ }, {"4.21.3", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -175,6 +193,8 @@ }, {"4.21.2", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -211,6 +231,8 @@ }, {"4.21.1", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -248,6 +270,8 @@ }, {"4.21", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -288,8 +312,19 @@ %% ------D o w n g r a d e --------------------------------------------------- [ + {"4.22", + [ + {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, []} + ] + }, {"4.21.7", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_pdus, soft_purge, soft_purge, []}, {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -316,6 +351,8 @@ }, {"4.21.6", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -342,6 +379,8 @@ }, {"4.21.5", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -373,6 +412,8 @@ }, {"4.21.4", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -407,6 +448,8 @@ }, {"4.21.3", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -441,6 +484,8 @@ }, {"4.21.2", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -477,6 +522,8 @@ }, {"4.21.1", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -514,6 +561,8 @@ }, {"4.21", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, diff --git a/lib/snmp/src/compile/Makefile b/lib/snmp/src/compile/Makefile index 627af6f185..9658ee0005 100644 --- a/lib/snmp/src/compile/Makefile +++ b/lib/snmp/src/compile/Makefile @@ -120,13 +120,13 @@ $(GENERATED_PARSER): $(PARSER_SRC) include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/compiler - $(INSTALL_DATA) $(ESCRIPT_SRC) $(PARSER_SRC) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/compiler - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(EBIN_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/bin - $(INSTALL_SCRIPT) $(ESCRIPT_BIN) $(RELSYSDIR)/bin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/compiler" + $(INSTALL_DATA) $(ESCRIPT_SRC) $(PARSER_SRC) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/compiler" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(EBIN_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/bin" + $(INSTALL_SCRIPT) $(ESCRIPT_BIN) "$(RELSYSDIR)/bin" release_docs_spec: diff --git a/lib/snmp/src/manager/Makefile b/lib/snmp/src/manager/Makefile index c1d5703300..1f30669f02 100644 --- a/lib/snmp/src/manager/Makefile +++ b/lib/snmp/src/manager/Makefile @@ -111,13 +111,13 @@ info: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/manager - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/manager - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin -# $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/manager" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src/manager" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" +# $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(EXT_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 89eaee9f80..f590892c66 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -1411,7 +1411,7 @@ log_to_io(LogDir, Mibs, LogName, LogFile) -> log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start). log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> - snmp:log_to_io(LogDir, Mibs, LogFile, Start, Stop). + snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop). change_log_size(NewSize) -> diff --git a/lib/snmp/src/misc/Makefile b/lib/snmp/src/misc/Makefile index 48d76bdbed..c6a5064d89 100644 --- a/lib/snmp/src/misc/Makefile +++ b/lib/snmp/src/misc/Makefile @@ -109,13 +109,13 @@ info: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/misc - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/misc - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin -# $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/misc" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src/misc" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" +# $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(EXT_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl index 0788d86b2d..5b8be90a71 100644 --- a/lib/snmp/src/misc/snmp_pdus.erl +++ b/lib/snmp/src/misc/snmp_pdus.erl @@ -254,68 +254,95 @@ strip(0, _Tail) -> %%---------------------------------------------------------------------- %% Returns:{Type, Value} %%---------------------------------------------------------------------- + +%% OBJECT IDENTIFIER dec_value([6 | Bytes]) -> {Value, Rest} = dec_oid_notag(Bytes), {{'OBJECT IDENTIFIER', Value}, Rest}; dec_value([5,0 | T]) -> {{'NULL', 'NULL'}, T}; + +%% INTEGER dec_value([2 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), {{'INTEGER', Value}, Rest}; + +%% OCTET STRING dec_value([4 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'OCTET STRING', Value}, Rest}; + +%% IpAddress dec_value([64 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'IpAddress', Value}, Rest}; + +%% Counter32 dec_value([65 | Bytes]) -> %% Counter32 is an unsigned 32 but is actually encoded as %% a signed integer 32 (INTEGER). {Value, Rest} = dec_integer_notag(Bytes), Value2 = - if + if + (Value >= 0) andalso (Value =< 16#ffffffff) -> + %% We accept value above 16#7fffffff + %% in order to be backward bug-compatible + Value; + (Value < 0) -> + 16#ffffffff + Value + 1; + true -> + exit({error, {bad_counter32, Value}}) + end, + {{'Counter32', Value2}, Rest}; + +%% Unsigned32 +dec_value([66 | Bytes]) -> + {Value, Rest} = dec_integer_notag(Bytes), + Value2 = + if (Value >= 0) andalso (Value =< 16#ffffffff) -> - %% We accept value above 16#7fffffff - %% in order to be backward bug-compatible Value; (Value < 0) -> 16#ffffffff + Value + 1; true -> - exit({error, {bad_counter32, Value}}) + exit({error, {bad_unsigned32, Value}}) end, - {{'Counter32', Value2}, Rest}; -dec_value([66 | Bytes]) -> - {Value, Rest} = dec_integer_notag(Bytes), - if - (Value >= 0) andalso (Value =< 4294967295) -> - {{'Unsigned32', Value}, Rest}; - true -> - exit({error, {bad_unsigned32, Value}}) - end; + {{'Unsigned32', Value2}, Rest}; + +%% TimeTicks dec_value([67 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), - if - (Value >= 0) andalso (Value =< 4294967295) -> - {{'TimeTicks', Value}, Rest}; + Value2 = + if + (Value >= 0) andalso (Value =< 16#ffffffff) -> + Value; + (Value < 0) -> + 16#ffffffff + Value + 1; true -> exit({error, {bad_timeticks, Value}}) - end; + end, + {{'TimeTicks', Value2}, Rest}; + +%% Opaque dec_value([68 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'Opaque', Value}, Rest}; + +%% Counter64 dec_value([70 | Bytes]) -> %% Counter64 is an unsigned 64 but is actually encoded as %% a signed integer 64. {Value, Rest} = dec_integer_notag(Bytes), Value2 = - if - (Value >= 0) andalso (Value < 16#8000000000000000) -> - Value; - (Value < 0) -> - 18446744073709551615 + Value + 1; - true -> - exit({error, {bad_counter64, Value}}) end, + if + (Value >= 0) andalso (Value < 16#8000000000000000) -> + Value; + (Value < 0) -> + 16#ffffffffffffffff + Value + 1; + true -> + exit({error, {bad_counter64, Value}}) end, {{'Counter64', Value2}, Rest}; + dec_value([128,0|T]) -> {{'NULL', noSuchObject}, T}; dec_value([129,0|T]) -> @@ -629,7 +656,7 @@ enc_VarBind_attributes(#varbind{oid = Oid, variabletype = Type,value = Val}) -> ValueBytes = enc_value(Type, Val), lists:append(OidBytes, ValueBytes). -enc_value('INTEGER',Val) -> +enc_value('INTEGER', Val) -> enc_integer_tag(Val); enc_value('OCTET STRING', Val) -> enc_oct_str_tag(Val); @@ -637,7 +664,7 @@ enc_value('BITS', Val) -> enc_oct_str_tag(bits_to_str(Val)); enc_value('OBJECT IDENTIFIER', Val) -> enc_oid_tag(Val); -enc_value('IpAddress',Val) -> +enc_value('IpAddress', Val) -> Bytes2 = enc_oct_str_notag(Val), Len2 = elength(length(Bytes2)), lists:append([64 | Len2],Bytes2); @@ -668,6 +695,24 @@ enc_value('Counter32', Val) -> Bytes2 = enc_integer_notag(Val2), Len2 = elength(length(Bytes2)), lists:append([65 | Len2],Bytes2); +enc_value('Unsigned32', Val) -> + if + (Val >= 0) andalso (Val =< 4294967295) -> + Bytes2 = enc_integer_notag(Val), + Len2 = elength(length(Bytes2)), + lists:append([66 | Len2], Bytes2); + true -> + exit({error, {bad_counter32, Val}}) + end; +enc_value('TimeTicks', Val) -> + if + (Val >= 0) andalso (Val =< 4294967295) -> + Bytes2 = enc_integer_notag(Val), + Len2 = elength(length(Bytes2)), + lists:append([67 | Len2], Bytes2); + true -> + exit({error, {bad_timeticks, Val}}) + end; enc_value('Counter64', Val) -> Val2 = if @@ -682,18 +727,7 @@ enc_value('Counter64', Val) -> end, Bytes2 = enc_integer_notag(Val2), Len2 = elength(length(Bytes2)), - lists:append([70 | Len2],Bytes2); -enc_value(Type, Val) -> - Bytes2 = enc_integer_notag(Val), - Len2 = elength(length(Bytes2)), - lists:append([enc_val_tag(Type,Val) | Len2],Bytes2). - -enc_val_tag('Counter32',Val) when (Val >= 0) andalso (Val =< 4294967295) -> - 65; -enc_val_tag('Unsigned32', Val) when (Val >= 0) andalso (Val =< 4294967295) -> - 66; -enc_val_tag('TimeTicks', Val) when (Val >= 0) andalso (Val =< 4294967295) -> - 67. + lists:append([70 | Len2],Bytes2). %%---------------------------------------------------------------------- diff --git a/lib/snmp/test/Makefile b/lib/snmp/test/Makefile index 40a294f783..546d46a002 100644 --- a/lib/snmp/test/Makefile +++ b/lib/snmp/test/Makefile @@ -79,7 +79,7 @@ COVER_SPEC_FILE = snmp.cover # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/snmp_test +RELSYSDIR = $(RELEASE_PATH)/snmp_test # ---------------------------------------------------- @@ -235,10 +235,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(RELTEST_FILES) $(COVER_SPEC_FILE) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - tar cf - snmp_test_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(RELTEST_FILES) $(COVER_SPEC_FILE) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + tar cf - snmp_test_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: @@ -254,7 +254,7 @@ info: @echo "" @echo "ERL_COMPILE_FLAGS = $(ERL_COMPILE_FLAGS)" @echo "" - @echo "RELSYSDIR = $(RELSYSDIR)" + @echo "RELSYSDIR = "$(RELSYSDIR)"" @echo "" @echo "SOURCE = $(SOURCE)" @echo "" diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index 8ee5dd534d..e1d7f33b3f 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -346,47 +346,72 @@ end_per_group(_GroupName, Config) -> -init_per_testcase(otp8395 = Case, Config) when is_list(Config) -> +%% ---- Init Per TestCase ---- + +init_per_testcase(Case, Config) when is_list(Config) -> ?DBG("init_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + p("Agent Info: " + "~n ~p", [snmpa:info()]), + + init_per_testcase1(Case, Config). + +init_per_testcase1(otp8395 = Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [Case, Config]), otp8395({init, init_per_testcase2(Case, Config)}); -init_per_testcase(otp9884 = Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" +init_per_testcase1(otp9884 = Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [Case, Config]), otp9884({init, init_per_testcase2(Case, Config)}); -init_per_testcase(otp_7157_test = _Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" +init_per_testcase1(otp_7157_test = _Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(1)), [{watchdog, Dog} | Config ]; -init_per_testcase(v2_inform_i = _Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" +init_per_testcase1(v2_inform_i = _Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(10)), [{watchdog, Dog} | Config ]; -init_per_testcase(v3_inform_i = _Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" +init_per_testcase1(v3_inform_i = _Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(10)), [{watchdog, Dog} | Config ]; -init_per_testcase(_Case, Config) when is_list(Config) -> +init_per_testcase1(_Case, Config) when is_list(Config) -> ?DBG("init_per_testcase -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(6)), [{watchdog, Dog}| Config ]. -end_per_testcase(otp8395, Config) when is_list(Config) -> + +%% ---- End Per TestCase ---- + +end_per_testcase(Case, Config) when is_list(Config) -> + ?DBG("end_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + p("Agent Info: " + "~n ~p", [snmpa:info()]), + + display_log(Config), + + end_per_testcase1(Case, Config). + +end_per_testcase1(otp8395, Config) when is_list(Config) -> otp8395({fin, Config}); -end_per_testcase(otp9884, Config) when is_list(Config) -> +end_per_testcase1(otp9884, Config) when is_list(Config) -> otp9884({fin, Config}); -end_per_testcase(_Case, Config) when is_list(Config) -> - ?DBG("end_per_testcase -> entry with" +end_per_testcase1(_Case, Config) when is_list(Config) -> + ?DBG("end_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?config(watchdog, Config), @@ -1406,9 +1431,6 @@ simple(Config) when is_list(Config) -> try_test(simple_standard_test), - p("Display log"), - display_log(Config), - p("done"), ok. diff --git a/lib/snmp/test/snmp_pdus_test.erl b/lib/snmp/test/snmp_pdus_test.erl index 07b6d6b657..0d78749bcb 100644 --- a/lib/snmp/test/snmp_pdus_test.erl +++ b/lib/snmp/test/snmp_pdus_test.erl @@ -39,6 +39,7 @@ otp7575/1, otp8563/1, otp9022/1, + otp10132/1, init_per_testcase/2, end_per_testcase/2 ]). @@ -74,16 +75,16 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %% Test case definitions %%====================================================================== all() -> -[{group, tickets}]. + [{group, tickets}]. groups() -> - [{tickets, [], [otp7575, otp8563, otp9022]}]. + [{tickets, [], [otp7575, otp8563, otp9022, otp10132]}]. init_per_group(_GroupName, Config) -> - Config. + Config. end_per_group(_GroupName, Config) -> - Config. + Config. @@ -94,7 +95,7 @@ end_per_group(_GroupName, Config) -> %%====================================================================== otp7575(suite) -> []; -otp7575(doc) -> ["OTP-7575"]; +otp7575(doc) -> ["OTP-7575 - Message version"]; otp7575(Config) when is_list(Config) -> io:format("attempt to decode message with valid version~n", []), MsgWithOkVersion = <<48,39,2,1,0,4,6,112,117,98,108,105,99,160,26,2,2,1,49,2,1,0,2,1,0,48,14,48,12,6,8,43,6,1,2,1,1,5,0,5,0>>, @@ -127,48 +128,55 @@ otp7575(Config) when is_list(Config) -> otp8563(suite) -> []; -otp8563(doc) -> ["OTP-8563"]; +otp8563(doc) -> ["OTP-8563 - Counter64"]; otp8563(Config) when is_list(Config) -> Val1 = 16#7fffffffffffffff, - io:format("try encode and decode ~w~n", [Val1]), + io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), Enc1 = snmp_pdus:enc_value('Counter64', Val1), + io:format(" => ~w~n", [Enc1]), {{'Counter64', Val1}, []} = snmp_pdus:dec_value(Enc1), Val2 = Val1 + 1, - io:format("try encode and decode ~w~n", [Val2]), + io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), Enc2 = snmp_pdus:enc_value('Counter64', Val2), + io:format(" => ~w~n", [Enc2]), {{'Counter64', Val2}, []} = snmp_pdus:dec_value(Enc2), Val3 = Val2 + 1, - io:format("try encode and decode ~w~n", [Val3]), + io:format("try encode and decode valule 3: ~w (0x~.16b)~n", [Val3, Val3]), Enc3 = snmp_pdus:enc_value('Counter64', Val3), + io:format(" => ~w~n", [Enc3]), {{'Counter64', Val3}, []} = snmp_pdus:dec_value(Enc3), Val4 = 16#fffffffffffffffe, - io:format("try encode and decode ~w~n", [Val4]), + io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), Enc4 = snmp_pdus:enc_value('Counter64', Val4), + io:format(" => ~w~n", [Enc4]), {{'Counter64', Val4}, []} = snmp_pdus:dec_value(Enc4), Val5 = Val4 + 1, - io:format("try encode and decode ~w~n", [Val5]), + io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), Enc5 = snmp_pdus:enc_value('Counter64', Val5), + io:format(" => ~w~n", [Enc5]), {{'Counter64', Val5}, []} = snmp_pdus:dec_value(Enc5), Val6 = 16#ffffffffffffffff + 1, - io:format("try and fail to encode ~w~n", [Val6]), + io:format("try and fail to encode value 6: ~w (0x~.16b)~n", [Val6, Val6]), case (catch snmp_pdus:enc_value('Counter64', Val6)) of {'EXIT', {error, {bad_counter64, Val6}}} -> ok; Unexpected6 -> + io:format(" => ~w~n", [Unexpected6]), exit({unexpected_encode_result, Unexpected6, Val6}) end, Val7 = -1, - io:format("try and fail to encode ~w~n", [Val7]), + io:format("try and fail to encode value 7: ~w~n", [Val7]), case (catch snmp_pdus:enc_value('Counter64', Val7)) of {'EXIT', {error, {bad_counter64, Val7}}} -> ok; Unexpected7 -> + io:format(" => ~w~n", [Unexpected7]), exit({unexpected_encode_result, Unexpected7, Val7}) end, @@ -176,51 +184,151 @@ otp8563(Config) when is_list(Config) -> otp9022(suite) -> []; -otp9022(doc) -> ["OTP-9022"]; +otp9022(doc) -> ["OTP-9022 - Counter32"]; otp9022(Config) when is_list(Config) -> - Val1 = 16#7fffffff, - io:format("try encode and decode ~w~n", [Val1]), + Val0 = 2908389204, + io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]), + Enc0 = snmp_pdus:enc_value('Counter32', Val0), + io:format(" => ~w~n", [Enc0]), + {{'Counter32', Val0}, []} = snmp_pdus:dec_value(Enc0), + + Val1 = 0, + io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), Enc1 = snmp_pdus:enc_value('Counter32', Val1), + io:format(" => ~w~n", [Enc1]), {{'Counter32', Val1}, []} = snmp_pdus:dec_value(Enc1), Val2 = Val1 + 1, - io:format("try encode and decode ~w~n", [Val2]), + io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), Enc2 = snmp_pdus:enc_value('Counter32', Val2), + io:format(" => ~w~n", [Enc2]), {{'Counter32', Val2}, []} = snmp_pdus:dec_value(Enc2), - Val3 = Val2 + 1, - io:format("try encode and decode ~w~n", [Val3]), + Val3 = 16#7ffffffe, + io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]), Enc3 = snmp_pdus:enc_value('Counter32', Val3), + io:format(" => ~w~n", [Enc3]), {{'Counter32', Val3}, []} = snmp_pdus:dec_value(Enc3), - Val4 = 16#fffffffe, - io:format("try encode and decode ~w~n", [Val4]), + Val4 = Val3 + 1, + io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), Enc4 = snmp_pdus:enc_value('Counter32', Val4), + io:format(" => ~w~n", [Enc4]), {{'Counter32', Val4}, []} = snmp_pdus:dec_value(Enc4), Val5 = Val4 + 1, - io:format("try encode and decode ~w~n", [Val5]), + io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), Enc5 = snmp_pdus:enc_value('Counter32', Val5), + io:format(" => ~w~n", [Enc5]), {{'Counter32', Val5}, []} = snmp_pdus:dec_value(Enc5), - Val6 = 16#ffffffff + 1, - io:format("try and fail to encode ~w~n", [Val6]), - case (catch snmp_pdus:enc_value('Counter32', Val6)) of - {'EXIT', {error, {bad_counter32, Val6}}} -> + Val6 = 16#fffffffe, + io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]), + Enc6 = snmp_pdus:enc_value('Counter32', Val6), + io:format(" => ~w~n", [Enc6]), + {{'Counter32', Val6}, []} = snmp_pdus:dec_value(Enc6), + + Val7 = Val6 + 1, + io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]), + Enc7 = snmp_pdus:enc_value('Counter32', Val7), + io:format(" => ~w~n", [Enc7]), + {{'Counter32', Val7}, []} = snmp_pdus:dec_value(Enc7), + + Val8 = 16#ffffffff + 1, + io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]), + case (catch snmp_pdus:enc_value('Counter32', Val8)) of + {'EXIT', {error, {bad_counter32, Val8}}} -> ok; - Unexpected6 -> - exit({unexpected_encode_result, Unexpected6, Val6}) + Unexpected8 -> + io:format(" => ~w~n", [Unexpected8]), + exit({unexpected_encode_result, Unexpected8, Val8}) end, - Val7 = -1, - io:format("try and fail to encode ~w~n", [Val7]), - case (catch snmp_pdus:enc_value('Counter32', Val7)) of - {'EXIT', {error, {bad_counter32, Val7}}} -> + Val9 = -1, + io:format("try and fail to encode value 9: ~w~n", [Val9]), + case (catch snmp_pdus:enc_value('Counter32', Val9)) of + {'EXIT', {error, {bad_counter32, Val9}}} -> ok; - Unexpected7 -> - exit({unexpected_encode_result, Unexpected7, Val7}) + Unexpected9 -> + io:format(" => ~w~n", [Unexpected9]), + exit({unexpected_encode_result, Unexpected9, Val9}) + end, + + ok. + + +otp10132(suite) -> []; +otp10132(doc) -> ["OTP-10132 - TimeTicks"]; +otp10132(Config) when is_list(Config) -> + Val0 = 2159001034, + io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]), + Enc0 = snmp_pdus:enc_value('TimeTicks', Val0), + io:format(" => ~w~n", [Enc0]), + {{'TimeTicks', Val0}, []} = snmp_pdus:dec_value(Enc0), + + Val1 = 0, + io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), + Enc1 = snmp_pdus:enc_value('TimeTicks', Val1), + io:format(" => ~w~n", [Enc1]), + {{'TimeTicks', Val1}, []} = snmp_pdus:dec_value(Enc1), + + Val2 = Val1 + 1, + io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), + Enc2 = snmp_pdus:enc_value('TimeTicks', Val2), + io:format(" => ~w~n", [Enc2]), + {{'TimeTicks', Val2}, []} = snmp_pdus:dec_value(Enc2), + + Val3 = 16#7ffffffe, + io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]), + Enc3 = snmp_pdus:enc_value('TimeTicks', Val3), + io:format(" => ~w~n", [Enc3]), + {{'TimeTicks', Val3}, []} = snmp_pdus:dec_value(Enc3), + + Val4 = Val3 + 1, + io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), + Enc4 = snmp_pdus:enc_value('TimeTicks', Val4), + io:format(" => ~w~n", [Enc4]), + {{'TimeTicks', Val4}, []} = snmp_pdus:dec_value(Enc4), + + Val5 = Val4 + 1, + io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), + Enc5 = snmp_pdus:enc_value('TimeTicks', Val5), + io:format(" => ~w~n", [Enc5]), + {{'TimeTicks', Val5}, []} = snmp_pdus:dec_value(Enc5), + + Val6 = 16#fffffffe, + io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]), + Enc6 = snmp_pdus:enc_value('TimeTicks', Val6), + io:format(" => ~w~n", [Enc6]), + {{'TimeTicks', Val6}, []} = snmp_pdus:dec_value(Enc6), + + Val7 = Val6 + 1, + io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]), + Enc7 = snmp_pdus:enc_value('TimeTicks', Val7), + io:format(" => ~w~n", [Enc7]), + {{'TimeTicks', Val7}, []} = snmp_pdus:dec_value(Enc7), + + Val8 = Val7 + 1, + io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]), + case (catch snmp_pdus:enc_value('TimeTicks', Val8)) of + {'EXIT', {error, {bad_timeticks, Val8}}} -> + ok; + Unexpected8 -> + io:format(" => ~w~n", [Unexpected8]), + exit({unexpected_encode_result, Unexpected8, Val8}) end, + Val9 = -1, + io:format("try and fail to encode value 9: ~w~n", [Val9]), + case (catch snmp_pdus:enc_value('TimeTicks', Val9)) of + {'EXIT', {error, {bad_timeticks, Val9}}} -> + ok; + Unexpected9 -> + io:format(" => ~w~n", [Unexpected9]), + exit({unexpected_encode_result, Unexpected9, Val9}) + end, + + io:format("done~n", []), ok. diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index 36b9764bc8..b90dbe4eef 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -18,6 +18,6 @@ # %CopyrightEnd% APPLICATION = snmp -SNMP_VSN = 4.22 +SNMP_VSN = 4.22.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)" diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile index 125dcf8775..38782a3b00 100644 --- a/lib/ssh/doc/src/Makefile +++ b/lib/ssh/doc/src/Makefile @@ -109,13 +109,13 @@ debug opt: 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/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index e30c6f1ccc..b84b3a3dcb 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -259,11 +259,17 @@ that identifies the host for ssh. The default is <c><![CDATA[/etc/ssh]]></c>, note that SSH normally requires the host files there to be readable only by - root.</p> + root.</p> + </item> + <tag><c><![CDATA[{auth_methods, string()}]]></c></tag> + <item> + <p>Comma separated string that determines which authentication methodes that the server + should support and in what order they will be tried. Defaults to + <c><![CDATA["publickey,keyboard_interactive,password"]]></c></p> </item> - <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag> - <item> - <p>Provide passwords for password authentication.They will + <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag> + <item> + <p>Provide passwords for password authentication.They will be used when someone tries to connect to the server and public key user authentication fails. The option provides a list of valid user names and the corresponding password. diff --git a/lib/ssh/examples/Makefile b/lib/ssh/examples/Makefile index 5f17542fb8..84c7edac8c 100644 --- a/lib/ssh/examples/Makefile +++ b/lib/ssh/examples/Makefile @@ -69,8 +69,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 1734ae4df4..b8eecd3fa2 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -38,15 +38,17 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssh-$(VSN) # Behaviour (api) modules are first so they are compiled when # the compiler reaches a callback module using them. -MODULES= \ +BEHAVIOUR_MODULES= \ ssh_sftpd_file_api \ - ssh_key_api \ + ssh_channel \ + ssh_key_api + +MODULES= \ ssh \ ssh_sup \ sshc_sup \ sshd_sup \ ssh_connection_sup \ - ssh_channel \ ssh_connection \ ssh_connection_handler \ ssh_connection_manager \ @@ -73,11 +75,14 @@ MODULES= \ PUBLIC_HRL_FILES= ssh.hrl ssh_userauth.hrl ssh_xfer.hrl -ERL_FILES= $(MODULES:%=%.erl) +ERL_FILES= \ + $(MODULES:%=%.erl) \ + $(BEHAVIOUR_MODULES:%=%.erl) -ALL_MODULES= $(MODULES) -TARGET_FILES= $(ALL_MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET) +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR)) APP_FILE= ssh.app APPUP_FILE= ssh.appup @@ -93,29 +98,29 @@ INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += -pa$(EBIN)\ - -pz $(ERL_TOP)/lib/public_key/ebin +EXTRA_ERLC_FLAGS = +warn_unused_vars +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ + -pz $(EBIN) \ + -pz $(ERL_TOP)/lib/public_key/ebin \ + $(EXTRA_ERLC_FLAGS) + + # ---------------------------------------------------- # Targets # ---------------------------------------------------- -debug opt: $(TARGET_FILES) +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) -debug: ERLC_FLAGS += -Ddebug +debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) clean: - rm -f $(TARGET_FILES) + rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) rm -f errs core *~ -$(TARGET_FILES): ssh.hrl - -# $(EBIN)/ssh_sftpd_file.$(EMULATOR): ERLC_FLAGS += -pa$(EBIN) -# $(EBIN)/ssh_sftpd_file.$(EMULATOR): $(EBIN)/ssh_sftpd_file_api.$(EMULATOR) - -$(APP_TARGET): $(APP_SRC) ../vsn.mk +$(APP_TARGET): $(APP_SRC) ../vsn.mk sed -e 's;%VSN%;$(VSN);' $< > $@ -$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk +$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk sed -e 's;%VSN%;$(VSN);' $< > $@ @@ -127,12 +132,13 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(PUBLIC_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) $(APP_TARGET) \ + $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(PUBLIC_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 0542054596..d08dbafc32 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -18,12 +18,26 @@ %% {"%VSN%", - [ - {<<"2\\.*">>, [{restart_application, ssh}]}, - {<<"1\\.*">>, [{restart_application, ssh}]} + [ + {<<"2.1">>, [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, + {load_module, ssh_connection, soft_purge, soft_purge, []}, + {load_module, ssh_connection_manager, soft_purge, soft_purge, []}, + {load_module, ssh_auth, soft_purge, soft_purge, []}, + {load_module, ssh_channel, soft_purge, soft_purge, []}, + {load_module, ssh_file, soft_purge, soft_purge, []}]}, + {load_module, ssh, soft_purge, soft_purge, []}]}, + {<<"2.0\\.*">>, [{restart_application, ssh}]}, + {<<"1\\.*">>, [{restart_application, ssh}]} ], [ - {<<"2\\.*">>, [{restart_application, ssh}]}, - {<<"1\\.*">>, [{restart_application, ssh}]} + {<<"2.1">>,[{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, + {load_module, ssh_connection, soft_purge, soft_purge, []}, + {load_module, ssh_connection_manager, soft_purge, soft_purge, []}, + {load_module, ssh_auth, soft_purge, soft_purge, []}, + {load_module, ssh_channel, soft_purge, soft_purge, []}, + {load_module, ssh_file, soft_purge, soft_purge, []}]}, + {load_module, ssh, soft_purge, soft_purge, []}]}, + {<<"2.0\\.*">>, [{restart_application, ssh}]}, + {<<"1\\.*">>, [{restart_application, ssh}]} ] }. diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 39c7fe329e..3395f73884 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -91,10 +91,8 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) -> {ok, ConnectionSup} -> {ok, Manager} = ssh_connection_sup:connection_manager(ConnectionSup), - MRef = erlang:monitor(process, Manager), receive {Manager, is_connected} -> - do_demonitor(MRef, Manager), {ok, Manager}; %% When the connection fails %% ssh_connection_sup:connection_manager @@ -102,30 +100,13 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) -> %% could allready have terminated, so we will not %% match the Manager in this case {_, not_connected, {error, econnrefused}} when DisableIpv6 == false -> - do_demonitor(MRef, Manager), do_connect(Host, Port, proplists:delete(inet6, SocketOptions), SshOptions, Timeout, true); {_, not_connected, {error, Reason}} -> - do_demonitor(MRef, Manager), {error, Reason}; {_, not_connected, Other} -> - do_demonitor(MRef, Manager), - {error, Other}; - {'DOWN', MRef, _, Manager, Reason} when is_pid(Manager) -> - error_logger:warning_report([{ssh, connect}, - {diagnose, - "Connection was closed before properly set up."}, - {host, Host}, - {port, Port}, - {reason, Reason}]), - receive %% Clear EXIT message from queue - {'EXIT', Manager, _What} -> - {error, channel_closed} - after 0 -> - {error, channel_closed} - end + {error, Other} after Timeout -> - do_demonitor(MRef, Manager), ssh_connection_manager:stop(Manager), {error, timeout} end @@ -134,16 +115,6 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) -> {error, ssh_not_started} end. -do_demonitor(MRef, Manager) -> - erlang:demonitor(MRef), - receive - {'DOWN', MRef, _, Manager, _} -> - ok - after 0 -> - ok - end. - - %%-------------------------------------------------------------------- %% Function: close(ConnectionRef) -> ok %% @@ -346,8 +317,9 @@ handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{compression, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{allow_user_interaction, _} = Opt | Rest], SocketOptions, SshOptions) -> - handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +%%Backwards compatibility +handle_option([{allow_user_interaction, Value} | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option({user_interaction, Value}) | SshOptions]); handle_option([{infofun, _} = Opt | Rest],SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{connectfun, _} = Opt | Rest], SocketOptions, SshOptions) -> @@ -366,6 +338,10 @@ handle_option([{ssh_cli, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{shell, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{exec, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions). @@ -401,8 +377,11 @@ handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) -> Opt; handle_ssh_option({compression, Value} = Opt) when is_atom(Value) -> Opt; -handle_ssh_option({allow_user_interaction, Value} = Opt) when Value == true; - Value == false -> +handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module), + is_atom(Function) -> + + Opt; +handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) -> Opt; handle_ssh_option({infofun, Value} = Opt) when is_function(Value) -> Opt; @@ -412,11 +391,12 @@ handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) -> Opt; handle_ssh_option({failfun, Value} = Opt) when is_function(Value) -> Opt; -handle_ssh_option({ip_v6_disabled, Value} = Opt) when is_function(Value) -> +handle_ssh_option({ip_v6_disabled, Value} = Opt) when Value == true; + Value == false -> Opt; handle_ssh_option({transport, {Protocol, Cb, ClosTag}} = Opt) when is_atom(Protocol), - is_atom(Cb), - is_atom(ClosTag) -> + is_atom(Cb), + is_atom(ClosTag) -> Opt; handle_ssh_option({subsystems, Value} = Opt) when is_list(Value) -> Opt; @@ -495,4 +475,3 @@ verify_data(Data, Signature, Algorithm) when is_binary(Data), is_binary(Signatur Error -> Error end. - diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 1a4517c689..aa452a8e09 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -71,7 +71,7 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb, ssh_bits:install_messages(userauth_passwd_messages()), Password = case proplists:get_value(password, Opts) of undefined -> - user_interaction(Opts, IoCb); + user_interaction(IoCb); PW -> PW end, @@ -89,13 +89,10 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb, Ssh) end. -user_interaction(Opts, IoCb) -> - case proplists:get_value(allow_user_interaction, Opts, true) of - true -> - IoCb:read_password("ssh password: "); - false -> - not_ok - end. +user_interaction(ssh_no_io) -> + not_ok; +user_interaction(IoCb) -> + IoCb:read_password("ssh password: "). %% See RFC 4256 for info on keyboard-interactive @@ -124,8 +121,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> FirstAlg = algorithm(proplists:get_value(public_key_alg, Opts, ?PREFERRED_PK_ALG)), SecondAlg = other_alg(FirstAlg), - AllowUserInt = proplists:get_value(allow_user_interaction, Opts, - true), + AllowUserInt = proplists:get_value(user_interaction, Opts, true), Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt), ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, userauth_preference = Prefs, diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl index 7b600ed8b2..1938858420 100644 --- a/lib/ssh/src/ssh_channel.erl +++ b/lib/ssh/src/ssh_channel.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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,7 +215,7 @@ handle_info({ssh_cm, ConnectionManager, {closed, ChannelId}}, close_sent = false} = State) -> %% To be on the safe side, i.e. the manager has already been terminated. (catch ssh_connection:close(ConnectionManager, ChannelId)), - {stop, normal, State}; + {stop, normal, State#state{close_sent = true}}; handle_info({ssh_cm, _, _} = Msg, #state{cm = ConnectionManager, channel_cb = Module, diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index 46f0c7e688..c2a7c63cbe 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -436,32 +436,32 @@ handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId, #connection{channel_cache = Cache} = Connection, ConnectionPid, _) -> - #channel{send_window_size = Size} = + #channel{send_window_size = Size, remote_id = RemoteId} = Channel0 = ssh_channel:cache_lookup(Cache, ChannelId), - + {SendList, Channel} = %% TODO: Datatype 0 ? update_send_window(Channel0#channel{send_window_size = Size + Add}, 0, <<>>, Connection), Replies = lists:map(fun({Type, Data}) -> {connection_reply, ConnectionPid, - channel_data_msg(ChannelId, Type, Data)} + channel_data_msg(RemoteId, Type, Data)} end, SendList), FlowCtrlMsgs = flow_control(Channel, Cache), {{replies, Replies ++ FlowCtrlMsgs}, Connection}; handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type, - sender_channel = ChannelId, + sender_channel = RemoteId, initial_window_size = WindowSz, maximum_packet_size = PacketSz}, Connection0, ConnectionPid, server) -> - try setup_session(Connection0, ConnectionPid, ChannelId, + try setup_session(Connection0, ConnectionPid, RemoteId, Type, WindowSz, PacketSz) of Result -> Result catch _:_ -> - FailMsg = channel_open_failure_msg(ChannelId, + FailMsg = channel_open_failure_msg(RemoteId, ?SSH_OPEN_CONNECT_FAILED, "Connection refused", "en"), {{replies, [{connection_reply, ConnectionPid, FailMsg}]}, @@ -532,9 +532,9 @@ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip", {{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection}; -handle_msg(#ssh_msg_channel_open{sender_channel = ChannelId}, Connection, +handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection, ConnectionPid, _) -> - FailMsg = channel_open_failure_msg(ChannelId, + FailMsg = channel_open_failure_msg(RemoteId, ?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "Not allowed", "en"), {{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection}; @@ -720,12 +720,17 @@ handle_msg(#ssh_msg_channel_request{request_type = "env"}, handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, request_type = _Other, - want_reply = WantReply}, Connection, + want_reply = WantReply}, #connection{channel_cache = Cache} = Connection, ConnectionPid, _) -> if WantReply == true -> - FailMsg = channel_failure_msg(ChannelId), - {{replies, [{connection_reply, ConnectionPid, FailMsg}]}, - Connection}; + case ssh_channel:cache_lookup(Cache, ChannelId) of + #channel{remote_id = RemoteId} -> + FailMsg = channel_failure_msg(RemoteId), + {{replies, [{connection_reply, ConnectionPid, FailMsg}]}, + Connection}; + undefined -> %% Chanel has been closed + {noreply, Connection} + end; true -> {noreply, Connection} end; diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl index e993f597a5..e53cd4f4f7 100644 --- a/lib/ssh/src/ssh_connection_manager.erl +++ b/lib/ssh/src/ssh_connection_manager.erl @@ -267,7 +267,7 @@ handle_call({ssh_msg, Pid, Msg}, From, disconnect_fun(Reason, SSHOpts), {stop, {shutdown, normal}, State#state{connection_state = Connection}} catch - _:Reason -> + _:Error -> {disconnect, Reason, {{replies, Replies}, Connection}} = ssh_connection:handle_msg( #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, @@ -277,7 +277,7 @@ handle_call({ssh_msg, Pid, Msg}, From, lists:foreach(fun send_msg/1, Replies), SSHOpts = proplists:get_value(ssh_opts, Opts), disconnect_fun(Reason, SSHOpts), - {stop, {shutdown, Reason}, State#state{connection_state = Connection}} + {stop, {shutdown, Error}, State#state{connection_state = Connection}} end; handle_call({global_request, Pid, _, _, _} = Request, From, @@ -384,9 +384,10 @@ handle_call({close, ChannelId}, _, #state{connection = Pid, connection_state = #connection{channel_cache = Cache}} = State) -> case ssh_channel:cache_lookup(Cache, ChannelId) of - #channel{remote_id = Id} -> + #channel{remote_id = Id} = Channel -> send_msg({connection_reply, Pid, ssh_connection:channel_close_msg(Id)}), + ssh_channel:cache_update(Cache, Channel#channel{sent_close = true}), {reply, ok, State}; undefined -> {reply, ok, State} @@ -585,7 +586,7 @@ call(Pid, Msg, Timeout) -> catch exit:{timeout, _} -> {error, timeout}; - exit:{normal} -> + exit:{normal, _} -> {error, channel_closed}; exit:{{shutdown, _}, _} -> {error, channel_closed}; diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl index e3544af1c6..b620056310 100644 --- a/lib/ssh/src/ssh_connection_sup.erl +++ b/lib/ssh/src/ssh_connection_sup.erl @@ -48,8 +48,12 @@ start_manager_child(Sup, Args) -> supervisor:start_child(Sup, Spec). connection_manager(SupPid) -> - Children = supervisor:which_children(SupPid), - {ok, ssh_connection_manager(Children)}. + try supervisor:which_children(SupPid) of + Children -> + {ok, ssh_connection_manager(Children)} + catch exit:{noproc,_} -> + {ok, undefined} + end. %%%========================================================================= %%% Supervisor callback @@ -107,6 +111,8 @@ handler_spec([Role, Socket, Opts]) -> Type = worker, {Name, StartFunc, Restart, Shutdown, Type, Modules}. +ssh_connection_manager([]) -> + undefined; ssh_connection_manager([{_, Child, _, [ssh_connection_manager]} | _]) -> Child; ssh_connection_manager([_ | Rest]) -> diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl index d05fa8e09a..a6b82a7a13 100644 --- a/lib/ssh/src/ssh_file.erl +++ b/lib/ssh/src/ssh_file.erl @@ -232,7 +232,7 @@ lookup_host_key_fd(Fd, Host, KeyType) -> eof -> {error, not_found}; Line -> - case public_key:ssh_decode(Line, known_hosts) of + case ssh_decode_line(Line, known_hosts) of [{Key, Attributes}] -> handle_host(Fd, Host, proplists:get_value(hostnames, Attributes), Key, KeyType); [] -> @@ -240,6 +240,13 @@ lookup_host_key_fd(Fd, Host, KeyType) -> end end. +ssh_decode_line(Line, Type) -> + try + public_key:ssh_decode(Line, Type) + catch _:_ -> + [] + end. + handle_host(Fd, Host, HostList, Key, KeyType) -> Host1 = host_name(Host), case lists:member(Host1, HostList) and key_match(Key, KeyType) of @@ -285,7 +292,7 @@ lookup_user_key_fd(Fd, Key) -> eof -> {error, not_found}; Line -> - case public_key:ssh_decode(Line, auth_keys) of + case ssh_decode_line(Line, auth_keys) of [{AuthKey, _}] -> case is_auth_key(Key, AuthKey) of true -> diff --git a/lib/ssh/src/ssh_sftpd_file_api.erl b/lib/ssh/src/ssh_sftpd_file_api.erl index 38371f809d..83d90907f5 100644 --- a/lib/ssh/src/ssh_sftpd_file_api.erl +++ b/lib/ssh/src/ssh_sftpd_file_api.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -22,40 +22,40 @@ -module(ssh_sftpd_file_api). %% To be further specified later --callback close(IoDevice::term(), State::term()) -> - ok | {error, Reason::term()}. --callback delete(Path::term(), State::term()) -> - ok | {error, Reason::term()}. --callback del_dir(Path::term(), State::term()) -> - ok | {error, Reason::term()}. +-callback close(file:io_device(), State::term()) -> + {ok, State::term()} | {{error, Reason::term()}, State::term()}. +-callback delete(file:name(), State::term()) -> + {ok, State::term()} | {{error, Reason::term()}, State::term()}. +-callback del_dir(file:name(), State::term()) -> + {ok, State::term()} | {{error, Reason::term()}, State::term()}. -callback get_cwd(State::term()) -> - {ok, Dir::term()} | {error, Reason::term()}. --callback is_dir(AbsPath::term(), State::term()) -> - boolean(). --callback list_dir(AbsPath::term(), State::term()) -> - {ok, Filenames::term()} | {error, Reason::term()}. + {{ok, Dir::term()}, State::term()} | {{error, Reason::term()}, State::term()}. +-callback is_dir(file:name(), State::term()) -> + {boolean(), State::term()}. +-callback list_dir(file:name(), State::term()) -> + {{ok, Filenames::term()}, State::term()} | {{error, Reason::term()}, State::term()}. -callback make_dir(Dir::term(), State::term()) -> - ok | {error, Reason::term()}. + {{ok, State::term()},State::term()} | {{error, Reason::term()}, State::term()}. -callback make_symlink(Path2::term(), Path::term(), State::term()) -> - ok | {error, Reason::term()}. + {ok, State::term()} | {{error, Reason::term()}, State::term()}. -callback open(Path::term(), Flags::term(), State::term()) -> - {ok, IoDevice::term()} | {error, Reason::term()}. --callback position(IoDevice::term(), Offs::term(), State::term()) -> - {ok, NewPosition::term()} | {error, Reason::term()}. --callback read(IoDevice::term(), Len::term(), State::term()) -> - {ok, Data::term()} | eof | {error, Reason::term()}. --callback read_link(Path::term(), State::term()) -> - {ok, FileName::term()} | {error, Reason::term()}. --callback read_link_info(Path::term(), State::term()) -> - {ok, FileInfo::term()} | {error, Reason::term()}. --callback read_file_info(Path::term(), State::term()) -> - {ok, FileInfo::term()} | {error, Reason::term()}. --callback rename(Path::term(), Path2::term(), State::term()) -> - ok | {error, Reason::term()}. --callback write(IoDevice::term(), Data::term(), State::term()) -> - ok | {error, Reason::term()}. --callback write_file_info(Path::term(),Info::term(), State::term()) -> - ok | {error, Reason::term()}. + {{ok, IoDevice::term()}, State::term()} | {{error, Reason::term()}, State::term()}. +-callback position(file:io_device(), Offs::term(), State::term()) -> + {{ok, NewPosition::term()}, State::term()} | {{error, Reason::term()}, State::term()}. +-callback read(file:io_device(), Len::term(), State::term()) -> + {{ok, Data::term()},State::term()} | {eof, State::term()} | {{error, Reason::term()}, State::term()}. +-callback read_link(file:name(), State::term()) -> + {{ok, FileName::term()}, State::term()} | {{error, Reason::term()}, State::term()}. +-callback read_link_info(file:name(), State::term()) -> + {{ok, FileInfo::term()}, State::term()} | {{error, Reason::term()}, State::term()}. +-callback read_file_info(file:name(), State::term()) -> + {{ok, FileInfo::term()}, State::term()} | {{error, Reason::term()},State::term()}. +-callback rename(file:name(), file:name(), State::term()) -> + {ok, State::term()} | {{error, Reason::term()}, State::term()}. +-callback write(file:io_device(), Data::term(), State::term()) -> + {ok, State::term()} | {{error, Reason::term()}, State::term()}. +-callback write_file_info(file:name(),Info::term(), State::term()) -> + {ok, State::term()} | {{error, Reason::term()}, State::term()}. diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile index 145d5d2ad6..25072688ad 100644 --- a/lib/ssh/test/Makefile +++ b/lib/ssh/test/Makefile @@ -110,11 +110,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) ssh.spec ssh.cover $(RELSYSDIR) - $(INSTALL_DATA) $(HRL_FILES_NEEDED_IN_TEST) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) ssh.spec ssh.cover "$(RELSYSDIR)" + $(INSTALL_DATA) $(HRL_FILES_NEEDED_IN_TEST) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index d66214d415..2ceaa9daa5 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -183,7 +183,29 @@ app_test(doc) -> app_test(Config) when is_list(Config) -> ?t:app_test(ssh), ok. +%%-------------------------------------------------------------------- +misc_ssh_options(doc) -> + ["Test that we can set some misc options not tested elsewhere, " + "some options not yet present are not decided if we should support or " + "if they need thier own test case."]; +misc_ssh_options(suite) -> + []; +misc_ssh_options(Config) when is_list(Config) -> + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + + CMiscOpt0 = [{connecect_timeout, 1000}, {ip_v6_disabled, false}, {user_dir, UserDir}], + CMiscOpt1 = [{connecect_timeout, infinity}, {ip_v6_disabled, true}, {user_dir, UserDir}], + SMiscOpt0 = [{ip_v6_disabled, false}, {user_dir, UserDir}, {system_dir, SystemDir}], + SMiscOpt1 = [{ip_v6_disabled, true}, {user_dir, UserDir}, {system_dir, SystemDir}], + + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + basic_test([{client_opts, CMiscOpt0 ++ ClientOpts}, {server_opts, SMiscOpt0 ++ ServerOpts}]), + basic_test([{client_opts, CMiscOpt1 ++ ClientOpts}, {server_opts, SMiscOpt1 ++ ServerOpts}]). +%%-------------------------------------------------------------------- exec(doc) -> ["Test api function ssh_connection:exec"]; @@ -500,13 +522,14 @@ internal_error(Config) when is_list(Config) -> SystemDir = filename:join(?config(priv_dir, Config), system), UserDir = ?config(priv_dir, Config), - {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, {user_dir, UserDir}, {failfun, fun ssh_test_lib:failfun/2}]), {error,"Internal error"} = ssh:connect(Host, Port, [{silently_accept_hosts, true}, {user_dir, UserDir}, - {user_interaction, false}]). + {user_interaction, false}]), + ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- close(doc) -> @@ -539,3 +562,12 @@ close(Config) when is_list(Config) -> %%-------------------------------------------------------------------- %% Internal functions %%-------------------------------------------------------------------- + +basic_test(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + {Pid, Host, Port} = ssh_test_lib:daemon(ServerOpts), + {ok, CM} = ssh:connect(Host, Port, ClientOpts), + ok = ssh:close(CM), + ssh:stop_daemon(Pid). diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index bff73a1b40..defa47f824 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 2.1 +SSH_VSN = 2.1.1 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile index 5d808d6727..8736913c72 100644 --- a/lib/ssl/doc/src/Makefile +++ b/lib/ssl/doc/src/Makefile @@ -117,15 +117,15 @@ debug opt: 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 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index e019654685..5098d26a3a 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -36,12 +36,16 @@ <list type="bulleted"> <item>ssl requires the crypto and public_key applications.</item> - <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item> + <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0, experimental + support for TLS-1.1 and TLS-1.2 is also available (no support for elliptic curve cipher suites yet).</item> <item>For security reasons sslv2 is not supported.</item> <item>Ephemeral Diffie-Hellman cipher suites are supported but not Diffie Hellman Certificates cipher suites.</item> <item>Export cipher suites are not supported as the U.S. lifted its export restrictions in early 2000.</item> + <item>IDEA cipher suites are not supported as they have + become deprecated by the latest TLS spec so there is not any + real motivation to implement them.</item> <item>CRL and policy certificate extensions are not supported yet. </item> </list> @@ -75,7 +79,7 @@ {keyfile, path()} | {password, string()} | {cacerts, [der_encoded()]} | {cacertfile, path()} | |{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} | - {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()} + {ssl_imp, ssl_imp()}| {reuse_sessions, boolean()} | {reuse_session, fun()} </c></p> <p><c>transportoption() = {CallbackModule, DataTag, ClosedTag} @@ -106,7 +110,7 @@ <p><c>sslsocket() - opaque to the user. </c></p> - <p><c>protocol() = sslv3 | tlsv1 </c></p> + <p><c>protocol() = sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2' </c></p> <p><c>ciphers() = [ciphersuite()] | string() (according to old API)</c></p> @@ -193,13 +197,13 @@ </item> <tag>{depth, integer()}</tag> - <item>Specifies the maximum - verification depth, i.e. how far in a chain of certificates the - verification process can proceed before the verification is - considered to fail. Peer certificate = 0, CA certificate = 1, - higher level CA certificate = 2, etc. The value 2 thus means - that a chain can at most contain peer cert, CA cert, next CA - cert, and an additional CA cert. The default value is 1. + <item> + The depth is the maximum number of non-self-issued + intermediate certificates that may follow the peer certificate + in a valid certification path. So if depth is 0 the PEER must + be signed by the trusted ROOT-CA directly, if 1 the path can + be PEER, CA, ROOT-CA, if it is 2 PEER, CA, CA, ROOT-CA and so + on. The default value is 1. </item> <tag>{verify_fun, {Verifyfun :: fun(), InitialUserState :: term()}}</tag> diff --git a/lib/ssl/examples/certs/Makefile b/lib/ssl/examples/certs/Makefile index a4f067ade6..549f11d709 100644 --- a/lib/ssl/examples/certs/Makefile +++ b/lib/ssl/examples/certs/Makefile @@ -54,8 +54,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/certs + $(INSTALL_DIR) "$(RELSYSDIR)/examples/certs" tar cf - etc | \ - (cd $(RELSYSDIR)/examples/certs; tar xf -) - chmod -R ug+rw $(RELSYSDIR)/examples + (cd "$(RELSYSDIR)/examples/certs"; tar xf -) + chmod -R ug+rw "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/ssl/examples/src/Makefile b/lib/ssl/examples/src/Makefile index c5f31b689c..420634c97e 100644 --- a/lib/ssl/examples/src/Makefile +++ b/lib/ssl/examples/src/Makefile @@ -63,10 +63,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/src - $(INSTALL_DIR) $(RELSYSDIR)/examples/ebin - (cd ..; tar cf - src ebin | (cd $(RELSYSDIR)/examples; tar xf -)) - chmod -R ug+w $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/ebin" + (cd ..; tar cf - src ebin | (cd "$(RELSYSDIR)/examples"; tar xf -)) + chmod -R ug+w "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/ssl/examples/src/client_server.erl b/lib/ssl/examples/src/client_server.erl index baf5a9185e..133a1764bc 100644 --- a/lib/ssl/examples/src/client_server.erl +++ b/lib/ssl/examples/src/client_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-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 @@ -21,18 +21,14 @@ -module(client_server). --export([start/0, start/1, init_connect/1]). +-export([start/0, init_connect/1]). start() -> - start([ssl, subject]). - -start(CertOpts) -> %% Start ssl application + application:start(crypto), + application:start(public_key), application:start(ssl), - %% Always seed - ssl:seed("ellynatefttidppohjeh"), - %% Let the current process be the server that listens and accepts %% Listen {ok, LSock} = ssl:listen(0, mk_opts(listen)), @@ -40,14 +36,14 @@ start(CertOpts) -> io:fwrite("Listen: port = ~w.~n", [LPort]), %% Spawn the client process that connects to the server - spawn(?MODULE, init_connect, [{LPort, CertOpts}]), + spawn(?MODULE, init_connect, [LPort]), %% Accept {ok, ASock} = ssl:transport_accept(LSock), ok = ssl:ssl_accept(ASock), io:fwrite("Accept: accepted.~n"), - {ok, Cert} = ssl:peercert(ASock, CertOpts), - io:fwrite("Accept: peer cert:~n~p~n", [Cert]), + {ok, Cert} = ssl:peercert(ASock), + io:fwrite("Accept: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, otp)]), io:fwrite("Accept: sending \"hello\".~n"), ssl:send(ASock, "hello"), {error, closed} = ssl:recv(ASock, 0), @@ -59,12 +55,12 @@ start(CertOpts) -> %% Client connect -init_connect({LPort, CertOpts}) -> +init_connect(LPort) -> {ok, Host} = inet:gethostname(), {ok, CSock} = ssl:connect(Host, LPort, mk_opts(connect)), io:fwrite("Connect: connected.~n"), - {ok, Cert} = ssl:peercert(CSock, CertOpts), - io:fwrite("Connect: peer cert:~n~p~n", [Cert]), + {ok, Cert} = ssl:peercert(CSock), + io:fwrite("Connect: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, otp)]), {ok, Data} = ssl:recv(CSock, 0), io:fwrite("Connect: got data: ~p~n", [Data]), io:fwrite("Connect: closing and terminating.~n"), diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index dc69b53b28..a08444b8dd 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -37,6 +37,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN) # Common Macros # ---------------------------------------------------- +BEHAVIOUR_MODULES= \ + ssl_session_cache_api + MODULES= \ ssl \ ssl_alert \ @@ -53,7 +56,6 @@ MODULES= \ ssl_handshake \ ssl_manager \ ssl_session \ - ssl_session_cache_api \ ssl_session_cache \ ssl_record \ ssl_ssl2 \ @@ -66,10 +68,15 @@ INTERNAL_HRL_FILES = \ ssl_alert.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_internal.hrl \ ssl_record.hrl -ERL_FILES= $(MODULES:%=%.erl) +ERL_FILES= \ + $(MODULES:%=%.erl) \ + $(BEHAVIOUR_MODULES:%=%.erl) + TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR)) + APP_FILE= ssl.app APPUP_FILE= ssl.appup @@ -83,6 +90,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) # ---------------------------------------------------- EXTRA_ERLC_FLAGS = +warn_unused_vars ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ + -pz $(EBIN) \ -pz $(ERL_TOP)/lib/public_key/ebin \ $(EXTRA_ERLC_FLAGS) -DVSN=\"$(VSN)\" @@ -91,6 +99,8 @@ ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ # Targets # ---------------------------------------------------- +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) + debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) clean: @@ -105,23 +115,18 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk docs: + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) \ - $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) $(APP_TARGET) \ + $(APPUP_TARGET) "$(RELSYSDIR)/ebin" release_docs_spec: - - - - - - diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index e346b1e9e6..76550fa04b 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,11 +1,13 @@ %% -*- erlang -*- {"%VSN%", [ + {"5.0.1", [{restart_application, ssl}]}, {"5.0", [{restart_application, ssl}]}, {<<"4\\.*">>, [{restart_application, ssl}]}, {<<"3\\.*">>, [{restart_application, ssl}]} ], [ + {"5.0.1", [{restart_application, ssl}]}, {"5.0", [{restart_application, ssl}]}, {<<"4\\.*">>, [{restart_application, ssl}]}, {<<"3\\.*">>, [{restart_application, ssl}]} diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 0bcdffbeff..40d933a256 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -25,12 +25,13 @@ -export([start/0, start/1, stop/0, transport_accept/1, transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3, - cipher_suites/0, cipher_suites/1, close/1, shutdown/2, + cipher_suites/0, cipher_suites/1, suite_definition/1, + close/1, shutdown/2, connect/3, connect/2, connect/4, connection_info/1, controlling_process/2, listen/2, pid/1, peername/1, peercert/1, recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1, versions/0, session_info/1, format_error/1, - renegotiate/1, prf/5]). + renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1]). -deprecated({pid, 1, next_major_release}). @@ -304,6 +305,15 @@ peercert(#sslsocket{pid = Pid}) -> end. %%-------------------------------------------------------------------- +-spec suite_definition(cipher_suite()) -> erl_cipher_suite(). +%% +%% Description: Return erlang cipher suite definition. +%%-------------------------------------------------------------------- +suite_definition(S) -> + {KeyExchange, Cipher, Hash, _} = ssl_cipher:suite_definition(S), + {KeyExchange, Cipher, Hash}. + +%%-------------------------------------------------------------------- -spec cipher_suites() -> [erl_cipher_suite()]. -spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()]. @@ -314,7 +324,7 @@ cipher_suites() -> cipher_suites(erlang) -> Version = ssl_record:highest_protocol_version([]), - [ssl_cipher:suite_definition(S) || S <- ssl_cipher:suites(Version)]; + [suite_definition(S) || S <- ssl_cipher:suites(Version)]; cipher_suites(openssl) -> Version = ssl_record:highest_protocol_version([]), @@ -408,7 +418,7 @@ session_info(#sslsocket{pid = Pid, fd = new_ssl}) -> versions() -> Vsns = ssl_record:supported_protocol_versions(), SupportedVsns = [ssl_record:protocol_version(Vsn) || Vsn <- Vsns], - AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS, + AvailableVsns = ?ALL_SUPPORTED_VERSIONS, [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}]. @@ -431,6 +441,15 @@ prf(#sslsocket{pid = Pid, fd = new_ssl}, Secret, Label, Seed, WantedLength) -> ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength). + +%%-------------------------------------------------------------------- +-spec clear_pem_cache() -> ok. +%% +%% Description: Clear the PEM cache +%%-------------------------------------------------------------------- +clear_pem_cache() -> + ssl_manager:clear_pem_cache(). + %%--------------------------------------------------------------- -spec format_error({error, term()}) -> list(). %% @@ -465,6 +484,23 @@ format_error(Error) -> Other end. +%%-------------------------------------------------------------------- +-spec random_bytes(integer()) -> binary(). + +%% +%% Description: Generates cryptographically secure random sequence if possible +%% fallbacks on pseudo random function +%%-------------------------------------------------------------------- +random_bytes(N) -> + try crypto:strong_rand_bytes(N) of + RandBytes -> + RandBytes + catch + error:low_entropy -> + crypto:rand_bytes(N) + end. + + %%%-------------------------------------------------------------- %%% Internal functions %%%-------------------------------------------------------------------- @@ -532,7 +568,7 @@ handle_options(Opts0, _Role) -> throw({error, {eoptions, {verify, Value}}}) end, - CertFile = handle_option(certfile, Opts, ""), + CertFile = handle_option(certfile, Opts, <<>>), SSLOptions = #ssl_options{ versions = handle_option(versions, Opts, []), @@ -619,8 +655,12 @@ validate_option(depth, Value) when is_integer(Value), validate_option(cert, Value) when Value == undefined; is_binary(Value) -> Value; -validate_option(certfile, Value) when Value == undefined; is_list(Value) -> +validate_option(certfile, undefined = Value) -> + Value; +validate_option(certfile, Value) when is_binary(Value) -> Value; +validate_option(certfile, Value) when is_list(Value) -> + list_to_binary(Value); validate_option(key, undefined) -> undefined; @@ -631,8 +671,13 @@ validate_option(key, {KeyType, Value}) when is_binary(Value), KeyType == 'DSAPrivateKey'; KeyType == 'PrivateKeyInfo' -> {KeyType, Value}; -validate_option(keyfile, Value) when is_list(Value) -> + +validate_option(keyfile, undefined) -> + <<>>; +validate_option(keyfile, Value) when is_binary(Value) -> Value; +validate_option(keyfile, Value) when is_list(Value), Value =/= "" -> + list_to_binary(Value); validate_option(password, Value) when is_list(Value) -> Value; @@ -642,16 +687,20 @@ validate_option(cacerts, Value) when Value == undefined; %% certfile must be present in some cases otherwhise it can be set %% to the empty string. validate_option(cacertfile, undefined) -> - ""; -validate_option(cacertfile, Value) when is_list(Value), Value =/= "" -> + <<>>; +validate_option(cacertfile, Value) when is_binary(Value) -> Value; +validate_option(cacertfile, Value) when is_list(Value), Value =/= ""-> + list_to_binary(Value); validate_option(dh, Value) when Value == undefined; is_binary(Value) -> Value; validate_option(dhfile, undefined = Value) -> Value; -validate_option(dhfile, Value) when is_list(Value), Value =/= "" -> +validate_option(dhfile, Value) when is_binary(Value) -> Value; +validate_option(dhfile, Value) when is_list(Value), Value =/= "" -> + list_to_binary(Value); validate_option(ciphers, Value) when is_list(Value) -> Version = ssl_record:highest_protocol_version([]), try cipher_suites(Version, Value) @@ -687,7 +736,8 @@ validate_option(Opt, Value) -> validate_versions([], Versions) -> Versions; -validate_versions([Version | Rest], Versions) when Version == 'tlsv1.1'; +validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2'; + Version == 'tlsv1.1'; Version == tlsv1; Version == sslv3 -> validate_versions(Rest, Versions); diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index eb1228afa4..222b3f1ad7 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-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,6 +84,8 @@ description_txt(?DECOMPRESSION_FAILURE) -> "decompression failure"; description_txt(?HANDSHAKE_FAILURE) -> "handshake failure"; +description_txt(?NO_CERTIFICATE_RESERVED) -> + "No certificate reserved"; description_txt(?BAD_CERTIFICATE) -> "bad certificate"; description_txt(?UNSUPPORTED_CERTIFICATE) -> diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl index 6470b82d50..92548edab7 100644 --- a/lib/ssl/src/ssl_alert.hrl +++ b/lib/ssl/src/ssl_alert.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -43,6 +43,7 @@ %% record_overflow(22), %% decompression_failure(30), %% handshake_failure(40), +%% no_certificate_RESERVED(41), %% Only sslv3 %% bad_certificate(42), %% unsupported_certificate(43), %% certificate_revoked(44), @@ -69,6 +70,7 @@ -define(RECORD_OVERFLOW, 22). -define(DECOMPRESSION_FAILURE, 30). -define(HANDSHAKE_FAILURE, 40). +-define(NO_CERTIFICATE_RESERVED, 41). -define(BAD_CERTIFICATE, 42). -define(UNSUPPORTED_CERTIFICATE, 43). -define(CERTIFICATE_REVOKED, 44). diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 0931b86782..86f5617b54 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -103,7 +103,7 @@ certificate_chain(OwnCert, CertDbHandle, CertsDbRef) -> ErlCert = public_key:pkix_decode_cert(OwnCert, otp), certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]). %%-------------------------------------------------------------------- --spec file_to_certificats(string(), term()) -> [der_cert()]. +-spec file_to_certificats(binary(), term()) -> [der_cert()]. %% %% Description: Return list of DER encoded certificates. %%-------------------------------------------------------------------- @@ -172,7 +172,12 @@ extensions_list(Extensions) -> %% Description: %%-------------------------------------------------------------------- signature_type(RSA) when RSA == ?sha1WithRSAEncryption; - RSA == ?md5WithRSAEncryption -> + RSA == ?md5WithRSAEncryption; + RSA == ?sha224WithRSAEncryption; + RSA == ?sha256WithRSAEncryption; + RSA == ?sha384WithRSAEncryption; + RSA == ?sha512WithRSAEncryption + -> rsa; signature_type(?'id-dsa-with-sha1') -> dsa. diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index cb2473576a..67d00f0da7 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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,12 +24,13 @@ -module(ssl_certificate_db). -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). +-include_lib("kernel/include/file.hrl"). -export([create/0, remove/1, add_trusted_certs/3, - remove_trusted_certs/2, lookup_trusted_cert/4, foldl/3, - lookup_cached_certs/2, cache_pem_file/4, uncache_pem_file/2, lookup/2]). - --type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. + remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1, + ref_count/3, lookup_trusted_cert/4, foldl/3, + lookup_cached_pem/2, cache_pem_file/2, cache_pem_file/3, + lookup/2]). %%==================================================================== %% Internal application API @@ -43,9 +44,14 @@ %% the process that called create may call the other functions. %%-------------------------------------------------------------------- create() -> - [ets:new(ssl_otp_certificate_db, [set, protected]), - ets:new(ssl_file_to_ref, [set, protected]), - ets:new(ssl_pid_to_file, [bag, private])]. + [%% Let connection process delete trusted certs + %% that can only belong to one connection. (Supplied directly + %% on DER format to ssl:connect/listen.) + ets:new(ssl_otp_cacertificate_db, [set, public]), + %% Let connection processes call ref_count/3 directly + ets:new(ssl_otp_ca_file_ref, [set, public]), + ets:new(ssl_otp_pem_cache, [set, protected]) + ]. %%-------------------------------------------------------------------- -spec remove([db_handle()]) -> term(). @@ -53,7 +59,9 @@ create() -> %% Description: Removes database db %%-------------------------------------------------------------------- remove(Dbs) -> - lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs). + lists:foreach(fun(Db) -> + true = ets:delete(Db) + end, Dbs). %%-------------------------------------------------------------------- -spec lookup_trusted_cert(db_handle(), certdb_ref(), serialnumber(), issuer()) -> @@ -72,11 +80,14 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> {ok, Certs} end. -lookup_cached_certs(DbHandle, File) -> - ets:lookup(DbHandle, {file, File}). +lookup_cached_pem([_, _, PemChache], MD5) -> + lookup_cached_pem(PemChache, MD5); +lookup_cached_pem(PemChache, MD5) -> + lookup(MD5, PemChache). %%-------------------------------------------------------------------- --spec add_trusted_certs(pid(), string() | {der, list()}, [db_handle()]) -> {ok, [db_handle()]}. +-spec add_trusted_certs(pid(), {erlang:timestamp(), string()} | + {der, list()}, [db_handle()]) -> {ok, [db_handle()]}. %% %% Description: Adds the trusted certificates from file <File> to the %% runtime database. Returns Ref that should be handed to lookup_trusted_cert @@ -86,82 +97,55 @@ add_trusted_certs(_Pid, {der, DerList}, [CerDb, _,_]) -> NewRef = make_ref(), add_certs_from_der(DerList, NewRef, CerDb), {ok, NewRef}; -add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> - Ref = case lookup(File, FileToRefDb) of - undefined -> - NewRef = make_ref(), - add_certs_from_file(File, NewRef, CertsDb), - insert(File, NewRef, 1, FileToRefDb), - NewRef; - [OldRef] -> - ref_count(File,FileToRefDb,1), - OldRef - end, - insert(Pid, File, PidToFileDb), - {ok, Ref}. + +add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) -> + MD5 = crypto:md5(File), + case lookup_cached_pem(Db, MD5) of + [{_Content, Ref}] -> + ref_count(Ref, RefDb, 1), + {ok, Ref}; + [Content] -> + Ref = make_ref(), + update_counter(Ref, 1, RefDb), + insert(MD5, {Content, Ref}, PemChache), + add_certs_from_pem(Content, Ref, CertsDb), + {ok, Ref}; + undefined -> + new_trusted_cert_entry({MD5, File}, Db) + end. %%-------------------------------------------------------------------- --spec cache_pem_file(pid(), string(), time(), [db_handle()]) -> term(). +-spec cache_pem_file({binary(), binary()}, [db_handle()]) -> term(). +-spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> term(). %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- -cache_pem_file(Pid, File, Time, [CertsDb, _FileToRefDb, PidToFileDb]) -> - {ok, PemBin} = file:read_file(File), +cache_pem_file({MD5, File}, [_CertsDb, _RefDb, PemChache]) -> + {ok, PemBin} = file:read_file(File), Content = public_key:pem_decode(PemBin), - insert({file, File}, {Time, Content}, CertsDb), - insert(Pid, File, PidToFileDb), + insert(MD5, Content, PemChache), {ok, Content}. -%-------------------------------------------------------------------- --spec uncache_pem_file(string(), [db_handle()]) -> no_return(). -%% -%% Description: If a cached file is no longer valid (changed on disk) -%% we must terminate the connections using the old file content, and -%% when those processes are finish the cache will be cleaned. It is -%% a rare but possible case a new ssl client/server is started with -%% a filename with the same name as previously started client/server -%% but with different content. -%% -------------------------------------------------------------------- -uncache_pem_file(File, [_CertsDb, _FileToRefDb, PidToFileDb]) -> - Pids = select(PidToFileDb, [{{'$1', File},[],['$$']}]), - lists:foreach(fun([Pid]) -> - exit(Pid, shutdown) - end, Pids). - -%%-------------------------------------------------------------------- --spec remove_trusted_certs(pid(), [db_handle()]) -> term(). - -%% -%% Description: Removes trusted certs originating from -%% the file associated to Pid from the runtime database. -%%-------------------------------------------------------------------- -remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) -> - Files = lookup(Pid, PidToFileDb), - delete(Pid, PidToFileDb), - Clear = fun(File) -> - delete({file,File}, CertsDb), - try - 0 = ref_count(File, FileToRefDb, -1), - case lookup(File, FileToRefDb) of - [Ref] when is_reference(Ref) -> - remove_certs(Ref, CertsDb); - _ -> ok - end, - delete(File, FileToRefDb) - catch _:_ -> - ok - end - end, - case Files of - undefined -> ok; - _ -> - [Clear(File) || File <- Files], - ok - end. +cache_pem_file(Ref, {MD5, File}, [_CertsDb, _RefDb, PemChache]) -> + {ok, PemBin} = file:read_file(File), + Content = public_key:pem_decode(PemBin), + insert(MD5, {Content, Ref}, PemChache), + {ok, Content}. + +remove_trusted_certs(Ref, CertsDb) -> + remove_certs(Ref, CertsDb). + +%%-------------------------------------------------------------------- +-spec remove(term(), db_handle()) -> term(). +%% +%% Description: Removes an element in a <Db>. +%%-------------------------------------------------------------------- +remove(Key, Db) -> + _ = ets:delete(Db, Key). %%-------------------------------------------------------------------- -spec lookup(term(), db_handle()) -> term() | undefined. %% -%% Description: Looks up an element in a certificat <Db>. +%% Description: Looks up an element in a <Db>. %%-------------------------------------------------------------------- lookup(Key, Db) -> case ets:lookup(Db, Key) of @@ -184,24 +168,44 @@ lookup(Key, Db) -> %%-------------------------------------------------------------------- foldl(Fun, Acc0, Cache) -> ets:foldl(Fun, Acc0, Cache). - + %%-------------------------------------------------------------------- -%%% Internal functions +-spec ref_count(term(), db_handle(), integer()) -> integer(). +%% +%% Description: Updates a reference counter in a <Db>. %%-------------------------------------------------------------------- -insert(Key, Data, Db) -> - true = ets:insert(Db, {Key, Data}). +ref_count(Key, Db, N) -> + ets:update_counter(Db,Key,N). -insert(Key, Data, Count, Db) -> - true = ets:insert(Db, {Key, Count, Data}). +%%-------------------------------------------------------------------- +-spec clear(db_handle()) -> term(). +%% +%% Description: Clears the cache +%%-------------------------------------------------------------------- +clear(Db) -> + ets:delete_all_objects(Db). -ref_count(Key, Db,N) -> - ets:update_counter(Db,Key,N). +%%-------------------------------------------------------------------- +-spec db_size(db_handle()) -> integer(). +%% +%% Description: Returns the size of the db +%%-------------------------------------------------------------------- +db_size(Db) -> + ets:info(Db, size). -delete(Key, Db) -> - _ = ets:delete(Db, Key). +%%-------------------------------------------------------------------- +%%-spec insert(Key::term(), Data::term(), Db::db_handle()) -> no_return(). +%% +%% Description: Inserts data into <Db> +%%-------------------------------------------------------------------- +insert(Key, Data, Db) -> + true = ets:insert(Db, {Key, Data}). -select(Db, MatchSpec)-> - ets:select(Db, MatchSpec). +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +update_counter(Key, Count, Db) -> + true = ets:insert(Db, {Key, Count}). remove_certs(Ref, CertsDb) -> ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}). @@ -210,10 +214,8 @@ add_certs_from_der(DerList, Ref, CertsDb) -> Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end, [Add(Cert) || Cert <- DerList]. -add_certs_from_file(File, Ref, CertsDb) -> +add_certs_from_pem(PemEntries, Ref, CertsDb) -> Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end, - {ok, PemBin} = file:read_file(File), - PemEntries = public_key:pem_decode(PemBin), [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries]. add_certs(Cert, Ref, CertsDb) -> @@ -229,3 +231,10 @@ add_certs(Cert, Ref, CertsDb) -> "it could not be correctly decoded.~n", []), error_logger:info_report(Report) end. + +new_trusted_cert_entry(FileRef, [CertsDb, RefDb, _] = Db) -> + Ref = make_ref(), + update_counter(Ref, 1, RefDb), + {ok, Content} = cache_pem_file(Ref, FileRef, Db), + add_certs_from_pem(Content, Ref, CertsDb), + {ok, Ref}. diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index d43d312be8..567690a413 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -28,25 +28,27 @@ -include("ssl_internal.hrl"). -include("ssl_record.hrl"). -include("ssl_cipher.hrl"). +-include("ssl_handshake.hrl"). -include("ssl_alert.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([security_parameters/2, suite_definition/1, - decipher/5, cipher/4, +-export([security_parameters/3, suite_definition/1, + decipher/5, cipher/5, suite/1, suites/1, anonymous_suites/0, - openssl_suite/1, openssl_suite_name/1, filter/2]). + openssl_suite/1, openssl_suite_name/1, filter/2, + hash_algorithm/1, sign_algorithm/1]). -compile(inline). %%-------------------------------------------------------------------- --spec security_parameters(cipher_suite(), #security_parameters{}) -> +-spec security_parameters(tls_version(), cipher_suite(), #security_parameters{}) -> #security_parameters{}. %% %% Description: Returns a security parameters record where the %% cipher values has been updated according to <CipherSuite> %%------------------------------------------------------------------- -security_parameters(CipherSuite, SecParams) -> - { _, Cipher, Hash} = suite_definition(CipherSuite), +security_parameters(Version, CipherSuite, SecParams) -> + { _, Cipher, Hash, PrfHashAlg} = suite_definition(CipherSuite), SecParams#security_parameters{ cipher_suite = CipherSuite, bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher), @@ -55,20 +57,21 @@ security_parameters(CipherSuite, SecParams) -> expanded_key_material_length = expanded_key_material(Cipher), key_material_length = key_material(Cipher), iv_size = iv_size(Cipher), - mac_algorithm = mac_algorithm(Hash), + mac_algorithm = hash_algorithm(Hash), + prf_algorithm = prf_algorithm(PrfHashAlg, Version), hash_size = hash_size(Hash)}. %%-------------------------------------------------------------------- --spec cipher(cipher_enum(), #cipher_state{}, binary(), binary()) -> +-spec cipher(cipher_enum(), #cipher_state{}, binary(), binary(), tls_version()) -> {binary(), #cipher_state{}}. %% %% Description: Encrypts the data and the MAC using chipher described %% by cipher_enum() and updating the cipher state %%------------------------------------------------------------------- -cipher(?NULL, CipherState, <<>>, Fragment) -> +cipher(?NULL, CipherState, <<>>, Fragment, _Version) -> GenStreamCipherList = [Fragment, <<>>], {GenStreamCipherList, CipherState}; -cipher(?RC4, CipherState, Mac, Fragment) -> +cipher(?RC4, CipherState, Mac, Fragment, _Version) -> State0 = case CipherState#cipher_state.state of undefined -> crypto:rc4_set_key(CipherState#cipher_state.key); S -> S @@ -76,32 +79,41 @@ cipher(?RC4, CipherState, Mac, Fragment) -> GenStreamCipherList = [Fragment, Mac], {State1, T} = crypto:rc4_encrypt_with_state(State0, GenStreamCipherList), {T, CipherState#cipher_state{state = State1}}; -cipher(?DES, CipherState, Mac, Fragment) -> +cipher(?DES, CipherState, Mac, Fragment, Version) -> block_cipher(fun(Key, IV, T) -> crypto:des_cbc_encrypt(Key, IV, T) - end, block_size(des_cbc), CipherState, Mac, Fragment); -cipher(?'3DES', CipherState, Mac, Fragment) -> + end, block_size(des_cbc), CipherState, Mac, Fragment, Version); +cipher(?'3DES', CipherState, Mac, Fragment, Version) -> block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) -> crypto:des3_cbc_encrypt(K1, K2, K3, IV, T) - end, block_size(des_cbc), CipherState, Mac, Fragment); -cipher(?AES, CipherState, Mac, Fragment) -> + end, block_size(des_cbc), CipherState, Mac, Fragment, Version); +cipher(?AES, CipherState, Mac, Fragment, Version) -> block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 -> crypto:aes_cbc_128_encrypt(Key, IV, T); (Key, IV, T) when byte_size(Key) =:= 32 -> crypto:aes_cbc_256_encrypt(Key, IV, T) - end, block_size(aes_128_cbc), CipherState, Mac, Fragment). -%% cipher(?IDEA, CipherState, Mac, Fragment) -> -%% block_cipher(fun(Key, IV, T) -> -%% crypto:idea_cbc_encrypt(Key, IV, T) -%% end, block_size(idea_cbc), CipherState, Mac, Fragment); - -block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, - Mac, Fragment) -> + end, block_size(aes_128_cbc), CipherState, Mac, Fragment, Version). + +build_cipher_block(BlockSz, Mac, Fragment) -> TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1, {PaddingLength, Padding} = get_padding(TotSz, BlockSz), - L = [Fragment, Mac, PaddingLength, Padding], + [Fragment, Mac, PaddingLength, Padding]. + +block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, + Mac, Fragment, {3, N}) + when N == 0; N == 1 -> + L = build_cipher_block(BlockSz, Mac, Fragment), T = Fun(Key, IV, L), NextIV = next_iv(T, IV), + {T, CS0#cipher_state{iv=NextIV}}; + +block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, + Mac, Fragment, {3, N}) + when N == 2; N == 3 -> + NextIV = random_iv(IV), + L0 = build_cipher_block(BlockSz, Mac, Fragment), + L = [NextIV|L0], + T = Fun(Key, IV, L), {T, CS0#cipher_state{iv=NextIV}}. %%-------------------------------------------------------------------- @@ -147,19 +159,16 @@ decipher(?AES, HashSz, CipherState, Fragment, Version) -> (Key, IV, T) when byte_size(Key) =:= 32 -> crypto:aes_cbc_256_decrypt(Key, IV, T) end, CipherState, HashSz, Fragment, Version). -%% decipher(?IDEA, HashSz, CipherState, Fragment, Version) -> -%% block_decipher(fun(Key, IV, T) -> -%% crypto:idea_cbc_decrypt(Key, IV, T) -%% end, CipherState, HashSz, Fragment, Version); block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, HashSz, Fragment, Version) -> try Text = Fun(Key, IV, Fragment), - GBC = generic_block_cipher_from_bin(Text, HashSz), + NextIV = next_iv(Fragment, IV), + GBC = generic_block_cipher_from_bin(Version, Text, NextIV, HashSz), Content = GBC#generic_block_cipher.content, Mac = GBC#generic_block_cipher.mac, - CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)}, + CipherState1 = CipherState0#cipher_state{iv=GBC#generic_block_cipher.next_iv}, case is_correct_padding(GBC, Version) of true -> {Content, Mac, CipherState1}; @@ -187,8 +196,8 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, %%-------------------------------------------------------------------- suites({3, 0}) -> ssl_ssl3:suites(); -suites({3, N}) when N == 1; N == 2 -> - ssl_tls1:suites(). +suites({3, N}) -> + ssl_tls1:suites(N). %%-------------------------------------------------------------------- -spec anonymous_suites() -> [cipher_suite()]. @@ -201,10 +210,12 @@ anonymous_suites() -> ?TLS_DH_anon_WITH_DES_CBC_SHA, ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, ?TLS_DH_anon_WITH_AES_128_CBC_SHA, - ?TLS_DH_anon_WITH_AES_256_CBC_SHA]. + ?TLS_DH_anon_WITH_AES_256_CBC_SHA, + ?TLS_DH_anon_WITH_AES_128_CBC_SHA256, + ?TLS_DH_anon_WITH_AES_256_CBC_SHA256]. %%-------------------------------------------------------------------- --spec suite_definition(cipher_suite()) -> erl_cipher_suite(). +-spec suite_definition(cipher_suite()) -> int_cipher_suite(). %% %% Description: Return erlang cipher suite definition. %% Note: Currently not supported suites are commented away. @@ -212,56 +223,81 @@ anonymous_suites() -> %%------------------------------------------------------------------- %% TLS v1.1 suites suite_definition(?TLS_NULL_WITH_NULL_NULL) -> - {null, null, null}; + {null, null, null, null}; %% suite_definition(?TLS_RSA_WITH_NULL_MD5) -> -%% {rsa, null, md5}; +%% {rsa, null, md5, default_prf}; %% suite_definition(?TLS_RSA_WITH_NULL_SHA) -> -%% {rsa, null, sha}; +%% {rsa, null, sha, default_prf}; suite_definition(?TLS_RSA_WITH_RC4_128_MD5) -> - {rsa, rc4_128, md5}; -suite_definition(?TLS_RSA_WITH_RC4_128_SHA) -> - {rsa, rc4_128, sha}; -%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) -> -%% {rsa, idea_cbc, sha}; -suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) -> - {rsa, des_cbc, sha}; + {rsa, rc4_128, md5, default_prf}; +suite_definition(?TLS_RSA_WITH_RC4_128_SHA) -> + {rsa, rc4_128, sha, default_prf}; +suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) -> + {rsa, des_cbc, sha, default_prf}; suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) -> - {rsa, '3des_ede_cbc', sha}; + {rsa, '3des_ede_cbc', sha, default_prf}; suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) -> - {dhe_dss, des_cbc, sha}; + {dhe_dss, des_cbc, sha, default_prf}; suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) -> - {dhe_dss, '3des_ede_cbc', sha}; + {dhe_dss, '3des_ede_cbc', sha, default_prf}; suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) -> - {dhe_rsa, des_cbc, sha}; + {dhe_rsa, des_cbc, sha, default_prf}; suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) -> - {dhe_rsa, '3des_ede_cbc', sha}; + {dhe_rsa, '3des_ede_cbc', sha, default_prf}; %%% TSL V1.1 AES suites suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) -> - {rsa, aes_128_cbc, sha}; + {rsa, aes_128_cbc, sha, default_prf}; suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) -> - {dhe_dss, aes_128_cbc, sha}; + {dhe_dss, aes_128_cbc, sha, default_prf}; suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) -> - {dhe_rsa, aes_128_cbc, sha}; + {dhe_rsa, aes_128_cbc, sha, default_prf}; suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) -> - {rsa, aes_256_cbc, sha}; + {rsa, aes_256_cbc, sha, default_prf}; suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> - {dhe_dss, aes_256_cbc, sha}; + {dhe_dss, aes_256_cbc, sha, default_prf}; suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> - {dhe_rsa, aes_256_cbc, sha}; + {dhe_rsa, aes_256_cbc, sha, default_prf}; + +%% TLS v1.2 suites + +%% suite_definition(?TLS_RSA_WITH_NULL_SHA) -> +%% {rsa, null, sha, default_prf}; +suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA256) -> + {rsa, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA256) -> + {rsa, aes_256_cbc, sha256, default_prf}; +suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) -> + {dhe_dss, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) -> + {dhe_rsa, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) -> + {dhe_dss, aes_256_cbc, sha256, default_prf}; +suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) -> + {dhe_rsa, aes_256_cbc, sha256, default_prf}; + +%% not defined YET: +%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 DH_DSS AES_128_CBC SHA256 +%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 DH_RSA AES_128_CBC SHA256 +%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 DH_DSS AES_256_CBC SHA256 +%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 DH_RSA AES_256_CBC SHA256 %%% DH-ANON deprecated by TLS spec and not available %%% by default, but good for testing purposes. suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) -> - {dh_anon, rc4_128, md5}; + {dh_anon, rc4_128, md5, default_prf}; suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) -> - {dh_anon, des_cbc, sha}; + {dh_anon, des_cbc, sha, default_prf}; suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) -> - {dh_anon, '3des_ede_cbc', sha}; + {dh_anon, '3des_ede_cbc', sha, default_prf}; suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) -> - {dh_anon, aes_128_cbc, sha}; + {dh_anon, aes_128_cbc, sha, default_prf}; suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) -> - {dh_anon, aes_256_cbc, sha}. + {dh_anon, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) -> + {dh_anon, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) -> + {dh_anon, aes_256_cbc, sha256, default_prf}. %%-------------------------------------------------------------------- -spec suite(erl_cipher_suite()) -> cipher_suite(). @@ -278,8 +314,6 @@ suite({rsa, rc4_128, md5}) -> ?TLS_RSA_WITH_RC4_128_MD5; suite({rsa, rc4_128, sha}) -> ?TLS_RSA_WITH_RC4_128_SHA; -%% suite({rsa, idea_cbc, sha}) -> -%% ?TLS_RSA_WITH_IDEA_CBC_SHA; suite({rsa, des_cbc, sha}) -> ?TLS_RSA_WITH_DES_CBC_SHA; suite({rsa, '3des_ede_cbc', sha}) -> @@ -315,7 +349,28 @@ suite({dhe_dss, aes_256_cbc, sha}) -> suite({dhe_rsa, aes_256_cbc, sha}) -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; suite({dh_anon, aes_256_cbc, sha}) -> - ?TLS_DH_anon_WITH_AES_256_CBC_SHA. + ?TLS_DH_anon_WITH_AES_256_CBC_SHA; + +%% TLS v1.2 suites + +%% suite_definition(?TLS_RSA_WITH_NULL_SHA) -> +%% {rsa, null, sha, sha256}; +suite({rsa, aes_128_cbc, sha256}) -> + ?TLS_RSA_WITH_AES_128_CBC_SHA256; +suite({rsa, aes_256_cbc, sha256}) -> + ?TLS_RSA_WITH_AES_256_CBC_SHA256; +suite({dhe_dss, aes_128_cbc, sha256}) -> + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256; +suite({dhe_rsa, aes_128_cbc, sha256}) -> + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; +suite({dhe_dss, aes_256_cbc, sha256}) -> + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256; +suite({dhe_rsa, aes_256_cbc, sha256}) -> + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; +suite({dh_anon, aes_128_cbc, sha256}) -> + ?TLS_DH_anon_WITH_AES_128_CBC_SHA256; +suite({dh_anon, aes_256_cbc, sha256}) -> + ?TLS_DH_anon_WITH_AES_256_CBC_SHA256. %%-------------------------------------------------------------------- -spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). @@ -323,6 +378,18 @@ suite({dh_anon, aes_256_cbc, sha}) -> %% Description: Return TLS cipher suite definition. %%-------------------------------------------------------------------- %% translate constants <-> openssl-strings +openssl_suite("DHE-RSA-AES256-SHA256") -> + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; +openssl_suite("DHE-DSS-AES256-SHA256") -> + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256; +openssl_suite("AES256-SHA256") -> + ?TLS_RSA_WITH_AES_256_CBC_SHA256; +openssl_suite("DHE-RSA-AES128-SHA256") -> + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; +openssl_suite("DHE-DSS-AES128-SHA256") -> + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256; +openssl_suite("AES128-SHA256") -> + ?TLS_RSA_WITH_AES_128_CBC_SHA256; openssl_suite("DHE-RSA-AES256-SHA") -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; openssl_suite("DHE-DSS-AES256-SHA") -> @@ -341,8 +408,6 @@ openssl_suite("DHE-DSS-AES128-SHA") -> ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA; openssl_suite("AES128-SHA") -> ?TLS_RSA_WITH_AES_128_CBC_SHA; -%%openssl_suite("IDEA-CBC-SHA") -> -%% ?TLS_RSA_WITH_IDEA_CBC_SHA; openssl_suite("RC4-SHA") -> ?TLS_RSA_WITH_RC4_128_SHA; openssl_suite("RC4-MD5") -> @@ -374,8 +439,6 @@ openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) -> "DHE-DSS-AES128-SHA"; openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA) -> "AES128-SHA"; -%% openssl_suite_name(?TLS_RSA_WITH_IDEA_CBC_SHA) -> -%% "IDEA-CBC-SHA"; openssl_suite_name(?TLS_RSA_WITH_RC4_128_SHA) -> "RC4-SHA"; openssl_suite_name(?TLS_RSA_WITH_RC4_128_MD5) -> @@ -384,6 +447,28 @@ openssl_suite_name(?TLS_DHE_RSA_WITH_DES_CBC_SHA) -> "EDH-RSA-DES-CBC-SHA"; openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) -> "DES-CBC-SHA"; +openssl_suite_name(?TLS_RSA_WITH_NULL_SHA256) -> + "NULL-SHA256"; +openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA256) -> + "AES128-SHA256"; +openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA256) -> + "AES256-SHA256"; +openssl_suite_name(?TLS_DH_DSS_WITH_AES_128_CBC_SHA256) -> + "DH-DSS-AES128-SHA256"; +openssl_suite_name(?TLS_DH_RSA_WITH_AES_128_CBC_SHA256) -> + "DH-RSA-AES128-SHA256"; +openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) -> + "DHE-DSS-AES128-SHA256"; +openssl_suite_name(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) -> + "DHE-RSA-AES128-SHA256"; +openssl_suite_name(?TLS_DH_DSS_WITH_AES_256_CBC_SHA256) -> + "DH-DSS-AES256-SHA256"; +openssl_suite_name(?TLS_DH_RSA_WITH_AES_256_CBC_SHA256) -> + "DH-RSA-AES256-SHA256"; +openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) -> + "DHE-DSS-AES256-SHA256"; +openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) -> + "DHE-RSA-AES256-SHA256"; %% No oppenssl name openssl_suite_name(Cipher) -> suite_definition(Cipher). @@ -411,9 +496,6 @@ filter(DerCert, Ciphers) -> bulk_cipher_algorithm(null) -> ?NULL; -%% Not supported yet -%% bulk_cipher_algorithm(idea_cbc) -> -%% ?IDEA; bulk_cipher_algorithm(rc4_128) -> ?RC4; bulk_cipher_algorithm(des_cbc) -> @@ -428,8 +510,7 @@ type(Cipher) when Cipher == null; Cipher == rc4_128 -> ?STREAM; -type(Cipher) when Cipher == idea_cbc; - Cipher == des_cbc; +type(Cipher) when Cipher == des_cbc; Cipher == '3des_ede_cbc'; Cipher == aes_128_cbc; Cipher == aes_256_cbc -> @@ -437,8 +518,7 @@ type(Cipher) when Cipher == idea_cbc; key_material(null) -> 0; -key_material(Cipher) when Cipher == idea_cbc; - Cipher == rc4_128 -> +key_material(rc4_128) -> 16; key_material(des_cbc) -> 8; @@ -451,8 +531,7 @@ key_material(aes_256_cbc) -> expanded_key_material(null) -> 0; -expanded_key_material(Cipher) when Cipher == idea_cbc; - Cipher == rc4_128 -> +expanded_key_material(rc4_128) -> 16; expanded_key_material(Cipher) when Cipher == des_cbc -> 8; @@ -467,8 +546,7 @@ effective_key_bits(null) -> 0; effective_key_bits(des_cbc) -> 56; -effective_key_bits(Cipher) when Cipher == idea_cbc; - Cipher == rc4_128; +effective_key_bits(Cipher) when Cipher == rc4_128; Cipher == aes_128_cbc -> 128; effective_key_bits('3des_ede_cbc') -> @@ -482,8 +560,7 @@ iv_size(Cipher) when Cipher == null; iv_size(Cipher) -> block_size(Cipher). -block_size(Cipher) when Cipher == idea_cbc; - Cipher == des_cbc; +block_size(Cipher) when Cipher == des_cbc; Cipher == '3des_ede_cbc' -> 8; @@ -491,19 +568,51 @@ block_size(Cipher) when Cipher == aes_128_cbc; Cipher == aes_256_cbc -> 16. -mac_algorithm(null) -> - ?NULL; -mac_algorithm(md5) -> - ?MD5; -mac_algorithm(sha) -> - ?SHA. +prf_algorithm(default_prf, {3, N}) when N >= 3 -> + ?SHA256; +prf_algorithm(default_prf, {3, _}) -> + ?MD5SHA; +prf_algorithm(Algo, _) -> + hash_algorithm(Algo). + +hash_algorithm(null) -> ?NULL; +hash_algorithm(md5) -> ?MD5; +hash_algorithm(sha) -> ?SHA; %% Only sha always refers to "SHA-1" +hash_algorithm(sha224) -> ?SHA224; +hash_algorithm(sha256) -> ?SHA256; +hash_algorithm(sha384) -> ?SHA384; +hash_algorithm(sha512) -> ?SHA512; +hash_algorithm(?NULL) -> null; +hash_algorithm(?MD5) -> md5; +hash_algorithm(?SHA) -> sha; +hash_algorithm(?SHA224) -> sha224; +hash_algorithm(?SHA256) -> sha256; +hash_algorithm(?SHA384) -> sha384; +hash_algorithm(?SHA512) -> sha512. + +sign_algorithm(anon) -> ?ANON; +sign_algorithm(rsa) -> ?RSA; +sign_algorithm(dsa) -> ?DSA; +sign_algorithm(ecdsa) -> ?ECDSA; +sign_algorithm(?ANON) -> anon; +sign_algorithm(?RSA) -> rsa; +sign_algorithm(?DSA) -> dsa; +sign_algorithm(?ECDSA) -> ecdsa. hash_size(null) -> 0; hash_size(md5) -> 16; hash_size(sha) -> - 20. + 20; +hash_size(sha256) -> + 32. +%% Currently no supported cipher suites defaults to sha384 or sha512 +%% so these clauses are not needed at the moment. +%% hash_size(sha384) -> +%% 48; +%% hash_size(sha512) -> +%% 64. %% RFC 5246: 6.2.3.2. CBC Block Cipher %% @@ -525,7 +634,8 @@ hash_size(sha) -> %% We return the original (possibly invalid) PadLength in any case. %% An invalid PadLength will be caught by is_correct_padding/2 %% -generic_block_cipher_from_bin(T, HashSize) -> +generic_block_cipher_from_bin({3, N}, T, IV, HashSize) + when N == 0; N == 1 -> Sz1 = byte_size(T) - 1, <<_:Sz1/binary, ?BYTE(PadLength0)>> = T, PadLength = if @@ -536,7 +646,20 @@ generic_block_cipher_from_bin(T, HashSize) -> <<Content:CompressedLength/binary, Mac:HashSize/binary, Padding:PadLength/binary, ?BYTE(PadLength0)>> = T, #generic_block_cipher{content=Content, mac=Mac, - padding=Padding, padding_length=PadLength0}. + padding=Padding, padding_length=PadLength0, + next_iv = IV}; + +generic_block_cipher_from_bin({3, N}, T, IV, HashSize) + when N == 2; N == 3 -> + Sz1 = byte_size(T) - 1, + <<_:Sz1/binary, ?BYTE(PadLength)>> = T, + IVLength = byte_size(IV), + CompressedLength = byte_size(T) - IVLength - PadLength - 1 - HashSize, + <<NextIV:IVLength/binary, Content:CompressedLength/binary, Mac:HashSize/binary, + Padding:PadLength/binary, ?BYTE(PadLength)>> = T, + #generic_block_cipher{content=Content, mac=Mac, + padding=Padding, padding_length=PadLength, + next_iv = NextIV}. generic_stream_cipher_from_bin(T, HashSz) -> Sz = byte_size(T), @@ -567,6 +690,10 @@ get_padding_aux(BlockSize, PadLength) -> N = BlockSize - PadLength, {N, list_to_binary(lists:duplicate(N, N))}. +random_iv(IV) -> + IVSz = byte_size(IV), + ssl:random_bytes(IVSz). + next_iv(Bin, IV) -> BinSz = byte_size(Bin), IVSz = byte_size(IV), @@ -578,16 +705,19 @@ rsa_signed_suites() -> dhe_rsa_suites() ++ rsa_suites(). dhe_rsa_suites() -> - [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, ?TLS_DHE_RSA_WITH_DES_CBC_SHA]. rsa_suites() -> - [?TLS_RSA_WITH_AES_256_CBC_SHA, + [?TLS_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_RSA_WITH_AES_256_CBC_SHA, ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_WITH_AES_128_CBC_SHA256, ?TLS_RSA_WITH_AES_128_CBC_SHA, - %%?TLS_RSA_WITH_IDEA_CBC_SHA, ?TLS_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_MD5, ?TLS_RSA_WITH_DES_CBC_SHA]. @@ -596,8 +726,10 @@ dsa_signed_suites() -> dhe_dss_suites(). dhe_dss_suites() -> - [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA]. diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 8bd68cc190..0f439f8ed5 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -28,8 +28,9 @@ -type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc' | aes_128_cbc | aes_256_cbc. --type hash() :: null | sha | md5. +-type hash() :: null | sha | md5 | sha256 | sha384 | sha512. -type erl_cipher_suite() :: {key_algo(), cipher(), hash()}. +-type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash()}. -type cipher_suite() :: binary(). -type cipher_enum() :: integer(). -type openssl_cipher_suite() :: string(). @@ -177,6 +178,47 @@ %% TLS_DH_anon_WITH_AES_256_CBC_SHA = { 0x00, 0x3A }; -define(TLS_DH_anon_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#3A)>>). +%%% TLS 1.2 Cipher Suites RFC 5246 + +%% TLS_RSA_WITH_NULL_SHA256 = { 0x00,0x3B }; +-define(TLS_RSA_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#3B)>>). + +%% TLS_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x3C }; +-define(TLS_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3C)>>). + +%% TLS_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x3D }; +-define(TLS_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3D)>>). + +%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x3E }; +-define(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3E)>>). + +%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x3F }; +-define(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3F)>>). + +%% TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x40 }; +-define(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#40)>>). + +%% TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x67 }; +-define(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#67)>>). + +%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = { 0x00,0x68 }; +-define(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#68)>>). + +%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x69 }; +-define(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#69)>>). + +%% TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = { 0x00,0x6A }; +-define(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6A)>>). + +%% TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x6B }; +-define(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6B)>>). + +%% TLS_DH_anon_WITH_AES_128_CBC_SHA256 = { 0x00,0x6C }; +-define(TLS_DH_anon_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6C)>>). + +%% TLS_DH_anon_WITH_AES_256_CBC_SHA256 = { 0x00,0x6D }; +-define(TLS_DH_anon_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6D)>>). + %%% Kerberos Cipher Suites %% TLS_KRB5_WITH_DES_CBC_SHA = { 0x00,0x1E }; diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 6c06baff98..ff2556c488 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -47,8 +47,8 @@ -export([start_link/7]). %% gen_fsm callbacks --export([init/1, hello/2, certify/2, cipher/2, connection/2, - abbreviated/2, handle_event/3, +-export([init/1, hello/2, certify/2, cipher/2, + abbreviated/2, connection/2, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -record(state, { @@ -67,8 +67,7 @@ tls_packets = [], % Not yet handled decode ssl/tls packets. tls_record_buffer, % binary() buffer of incomplete records tls_handshake_buffer, % binary() buffer of incomplete handshakes - %% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary()) - tls_handshake_hashes, % see above + tls_handshake_history, % tls_handshake_history() tls_cipher_texts, % list() received but not deciphered yet cert_db, % session, % #session{} from ssl_handshake.hrl @@ -78,18 +77,19 @@ supported_protocol_versions, % [atom()] client_certificate_requested = false, key_algorithm, % atom as defined by cipher_suite + hashsign_algorithm, % atom as defined by cipher_suite public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams} private_key, % PKIX: #'RSAPrivateKey'{} diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side diffie_hellman_keys, % {PublicKey, PrivateKey} premaster_secret, % - cert_db_ref, % ets_table() - from, % term(), where to reply + file_ref_db, % ets() + cert_db_ref, % ref() bytes_to_read, % integer(), # bytes to read in passive mode user_data_buffer, % binary() log_alert, % boolean() renegotiation, % {boolean(), From | internal | peer} - recv_from, % + start_or_recv_from, % "gen_fsm From" send_queue, % queue() terminated = false, % allow_renegotiate = true @@ -297,39 +297,31 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) -> %% does not return until Module:init/1 has returned. %%-------------------------------------------------------------------- start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> - gen_fsm:start_link(?MODULE, [Role, Host, Port, Socket, Options, - User, CbInfo], []). + {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}. -%%==================================================================== -%% gen_fsm callbacks -%%==================================================================== -%%-------------------------------------------------------------------- -%% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or -%% gen_fsm:start_link/3,4, this function is called by the new process to -%% initialize. -%%-------------------------------------------------------------------- -init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, - User, CbInfo]) -> +init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), - Hashes0 = ssl_handshake:init_hashes(), + Handshake = ssl_handshake:init_handshake_history(), TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), try ssl_init(SSLOpts0, Role) of - {ok, Ref, CertDbHandle, CacheHandle, OwnCert, Key, DHParams} -> + {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} -> Session = State0#state.session, - State = State0#state{tls_handshake_hashes = Hashes0, + State = State0#state{ + tls_handshake_history = Handshake, session = Session#session{own_certificate = OwnCert, time_stamp = TimeStamp}, + file_ref_db = FileRefHandle, cert_db_ref = Ref, cert_db = CertDbHandle, session_cache = CacheHandle, private_key = Key, diffie_hellman_params = DHParams}, - {ok, hello, State, get_timeout(State)} - catch + gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State)) + catch throw:Error -> - {stop, Error} + gen_fsm:enter_loop(?MODULE, [], error, {Error,State0}, get_timeout(State0)) end. - + %%-------------------------------------------------------------------- %% Description:There should be one instance of this function for each %% possible state name. Whenever a gen_fsm receives an event sent @@ -337,6 +329,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, %% same name as the current state name StateName is called to handle %% the event. It is also called if a timeout occurs. %% + %%-------------------------------------------------------------------- -spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(), #state{}) -> gen_fsm_state_return(). @@ -344,23 +337,23 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, hello(start, #state{host = Host, port = Port, role = client, ssl_options = SslOpts, session = #session{own_certificate = Cert} = Session0, + session_cache = Cache, session_cache_cb = CacheCb, transport_cb = Transport, socket = Socket, - connection_states = ConnectionStates, + connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}} = State0) -> - Hello = ssl_handshake:client_hello(Host, Port, - ConnectionStates, - SslOpts, Renegotiation, Cert), + Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, + Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, - Hashes0 = ssl_handshake:init_hashes(), - {BinMsg, CS2, Hashes1} = - encode_handshake(Hello, Version, ConnectionStates, Hashes0), + Handshake0 = ssl_handshake:init_handshake_history(), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Hello, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), - State1 = State0#state{connection_states = CS2, - negotiated_version = Version, %% Requested version + State1 = State0#state{connection_states = ConnectionStates, + negotiated_version = Version, %% Requested version session = Session0#session{session_id = Hello#client_hello.session_id}, - tls_handshake_hashes = Hashes1}, + tls_handshake_history = Handshake}, {Record, State} = next_record(State1), next_state(hello, hello, Record, State); @@ -382,19 +375,21 @@ hello(#server_hello{cipher_suite = CipherSuite, ssl_options = SslOptions} = State0) -> case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of {Version, NewId, ConnectionStates} -> - {KeyAlgorithm, _, _} = + {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite), PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), State = State0#state{key_algorithm = KeyAlgorithm, + hashsign_algorithm = default_hashsign(Version, KeyAlgorithm), negotiated_version = Version, connection_states = ConnectionStates, premaster_secret = PremasterSecret}, case ssl_session:is_new(OldId, NewId) of true -> - handle_new_session(NewId, CipherSuite, Compression, State); + handle_new_session(NewId, CipherSuite, Compression, + State#state{connection_states = ConnectionStates}); false -> handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) end; @@ -438,12 +433,13 @@ abbreviated(#hello_request{}, State0) -> abbreviated(#finished{verify_data = Data} = Finished, #state{role = server, negotiated_version = Version, - tls_handshake_hashes = Hashes, + tls_handshake_history = Handshake, session = #session{master_secret = MasterSecret}, connection_states = ConnectionStates0} = State) -> case ssl_handshake:verify_connection(Version, Finished, client, - MasterSecret, Hashes) of + get_current_connection_state_prf(ConnectionStates0, write), + MasterSecret, Handshake) of verified -> ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0), next_state_connection(abbreviated, @@ -454,18 +450,19 @@ abbreviated(#finished{verify_data = Data} = Finished, end; abbreviated(#finished{verify_data = Data} = Finished, - #state{role = client, tls_handshake_hashes = Hashes0, + #state{role = client, tls_handshake_history = Handshake0, session = #session{master_secret = MasterSecret}, negotiated_version = Version, connection_states = ConnectionStates0} = State) -> case ssl_handshake:verify_connection(Version, Finished, server, - MasterSecret, Hashes0) of + get_pending_connection_state_prf(ConnectionStates0, write), + MasterSecret, Handshake0) of verified -> ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0), - {ConnectionStates, Hashes} = + {ConnectionStates, Handshake} = finalize_handshake(State#state{connection_states = ConnectionStates1}, abbreviated), next_state_connection(abbreviated, - ack_connection(State#state{tls_handshake_hashes = Hashes, + ack_connection(State#state{tls_handshake_history = Handshake, connection_states = ConnectionStates})); #alert{} = Alert -> @@ -552,8 +549,8 @@ certify(#server_hello_done{}, role = client} = State0) -> case ssl_handshake:master_secret(Version, Session, ConnectionStates0, client) of - {MasterSecret, ConnectionStates1} -> - State = State0#state{connection_states = ConnectionStates1}, + {MasterSecret, ConnectionStates} -> + State = State0#state{connection_states = ConnectionStates}, client_certify_and_key_exchange(State); #alert{} = Alert -> handle_own_alert(Alert, Version, certify, State0), @@ -569,10 +566,10 @@ certify(#server_hello_done{}, role = client} = State0) -> case ssl_handshake:master_secret(Version, PremasterSecret, ConnectionStates0, client) of - {MasterSecret, ConnectionStates1} -> + {MasterSecret, ConnectionStates} -> Session = Session0#session{master_secret = MasterSecret}, - State = State0#state{connection_states = ConnectionStates1, - session = Session}, + State = State0#state{connection_states = ConnectionStates, + session = Session}, client_certify_and_key_exchange(State); #alert{} = Alert -> handle_own_alert(Alert, Version, certify, State0), @@ -643,15 +640,20 @@ cipher(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(cipher, hello, Record, State); -cipher(#certificate_verify{signature = Signature}, +cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashSign}, #state{role = server, public_key_info = PublicKeyInfo, negotiated_version = Version, session = #session{master_secret = MasterSecret}, - tls_handshake_hashes = Hashes + hashsign_algorithm = ConnectionHashSign, + tls_handshake_history = Handshake } = State0) -> + HashSign = case CertHashSign of + {_, _} -> CertHashSign; + _ -> ConnectionHashSign + end, case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, - Version, MasterSecret, Hashes) of + Version, HashSign, MasterSecret, Handshake) of valid -> {Record, State} = next_record(State0), next_state(cipher, cipher, Record, State); @@ -667,10 +669,12 @@ cipher(#finished{verify_data = Data} = Finished, role = Role, session = #session{master_secret = MasterSecret} = Session0, - tls_handshake_hashes = Hashes0} = State) -> + connection_states = ConnectionStates0, + tls_handshake_history = Handshake0} = State) -> case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), - MasterSecret, Hashes0) of + get_current_connection_state_prf(ConnectionStates0, read), + MasterSecret, Handshake0) of verified -> Session = register_session(Role, Host, Port, Session0), cipher_role(Role, Data, Session, State); @@ -691,22 +695,24 @@ cipher(Msg, State) -> %%-------------------------------------------------------------------- connection(#hello_request{}, #state{host = Host, port = Port, socket = Socket, - session = #session{own_certificate = Cert}, + session = #session{own_certificate = Cert} = Session0, + session_cache = Cache, session_cache_cb = CacheCb, ssl_options = SslOpts, negotiated_version = Version, transport_cb = Transport, connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}, - tls_handshake_hashes = Hashes0} = State0) -> - Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, - SslOpts, Renegotiation, Cert), + tls_handshake_history = Handshake0} = State0) -> + Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, + Cache, CacheCb, Renegotiation, Cert), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(Hello, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Hello, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), {Record, State} = next_record(State0#state{connection_states = - ConnectionStates1, - tls_handshake_hashes = Hashes1}), + ConnectionStates, + session = Session0#session{session_id = Hello#client_hello.session_id}, + tls_handshake_history = Handshake}), next_state(connection, hello, Record, State); connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) -> %% Mitigate Computational DoS attack @@ -732,6 +738,7 @@ connection(timeout, State) -> connection(Msg, State) -> handle_unexpected_message(Msg, connection, State). + %%-------------------------------------------------------------------- %% Description: Whenever a gen_fsm receives an event sent using %% gen_fsm:send_all_state_event/2, this function is called to handle @@ -761,8 +768,8 @@ handle_sync_event({application_data, Data}, From, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}, get_timeout(State)}; -handle_sync_event(start, From, hello, State) -> - hello(start, State#state{from = From}); +handle_sync_event(start, StartFrom, hello, State) -> + hello(start, State#state{start_or_recv_from = StartFrom}); %% The two clauses below could happen if a server upgrades a socket in %% active mode. Note that in this case we are lucky that @@ -774,8 +781,10 @@ handle_sync_event(start, From, hello, State) -> %% they upgrade a active socket. handle_sync_event(start, _, connection, State) -> {reply, connected, connection, State, get_timeout(State)}; -handle_sync_event(start, From, StateName, State) -> - {next_state, StateName, State#state{from = From}, get_timeout(State)}; +handle_sync_event(start, _From, error, {Error, State = #state{}}) -> + {stop, {shutdown, Error}, {error, Error}, State}; +handle_sync_event(start, StartFrom, StateName, State) -> + {next_state, StateName, State#state{start_or_recv_from = StartFrom}, get_timeout(State)}; handle_sync_event(close, _, StateName, State) -> %% Run terminate before returning @@ -806,13 +815,13 @@ handle_sync_event({shutdown, How0}, _, StateName, {stop, normal, Error, State} end; -handle_sync_event({recv, N}, From, connection = StateName, State0) -> - passive_receive(State0#state{bytes_to_read = N, recv_from = From}, StateName); +handle_sync_event({recv, N}, RecvFrom, connection = StateName, State0) -> + passive_receive(State0#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, StateName); %% Doing renegotiate wait with handling request until renegotiate is %% finished. Will be handled by next_state_is_connection/2. -handle_sync_event({recv, N}, From, StateName, State) -> - {next_state, StateName, State#state{bytes_to_read = N, recv_from = From}, +handle_sync_event({recv, N}, RecvFrom, StateName, State) -> + {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, get_timeout(State)}; handle_sync_event({new_user, User}, _From, StateName, @@ -910,14 +919,14 @@ handle_sync_event(info, _, StateName, session = #session{cipher_suite = Suite}} = State) -> AtomVersion = ssl_record:protocol_version(Version), - {reply, {ok, {AtomVersion, ssl_cipher:suite_definition(Suite)}}, + {reply, {ok, {AtomVersion, ssl:suite_definition(Suite)}}, StateName, State, get_timeout(State)}; handle_sync_event(session_info, _, StateName, #state{session = #session{session_id = Id, cipher_suite = Suite}} = State) -> {reply, [{session_id, Id}, - {cipher_suite, ssl_cipher:suite_definition(Suite)}], + {cipher_suite, ssl:suite_definition(Suite)}], StateName, State, get_timeout(State)}; handle_sync_event(peer_certificate, _, StateName, @@ -963,9 +972,9 @@ handle_info({CloseTag, Socket}, StateName, {stop, normal, State}; handle_info({ErrorTag, Socket, econnaborted}, StateName, - #state{socket = Socket, from = User, role = Role, + #state{socket = Socket, start_or_recv_from = StartFrom, role = Role, error_tag = ErrorTag} = State) when StateName =/= connection -> - alert_user(User, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role), + alert_user(StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role), {stop, normal, State}; handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket, @@ -1002,16 +1011,19 @@ terminate(Reason, connection, #state{negotiated_version = Version, connection_states = ConnectionStates, transport_cb = Transport, socket = Socket, send_queue = SendQueue, - renegotiation = Renegotiate}) -> + renegotiation = Renegotiate} = State) -> + handle_trusted_certs_db(State), notify_senders(SendQueue), notify_renegotiater(Renegotiate), BinAlert = terminate_alert(Reason, Version, ConnectionStates), Transport:send(Socket, BinAlert), workaround_transport_delivery_problems(Socket, Transport, Reason), Transport:close(Socket); + terminate(Reason, _StateName, #state{transport_cb = Transport, socket = Socket, send_queue = SendQueue, - renegotiation = Renegotiate}) -> + renegotiation = Renegotiate} = State) -> + handle_trusted_certs_db(State), notify_senders(SendQueue), notify_renegotiater(Renegotiate), workaround_transport_delivery_problems(Socket, Transport, Reason), @@ -1059,12 +1071,12 @@ ssl_init(SslOpts, Role) -> init_manager_name(SslOpts#ssl_options.erl_dist), - {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert} = init_certificates(SslOpts, Role), + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, OwnCert} = init_certificates(SslOpts, Role), PrivateKey = - init_private_key(CertDbHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, + init_private_key(PemCacheHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, SslOpts#ssl_options.password, Role), - DHParams = init_diffie_hellman(CertDbHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role), - {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert, PrivateKey, DHParams}. + DHParams = init_diffie_hellman(PemCacheHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role), + {ok, CertDbRef, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, PrivateKey, DHParams}. init_manager_name(false) -> put(ssl_manager, ssl_manager); @@ -1075,7 +1087,7 @@ init_certificates(#ssl_options{cacerts = CaCerts, cacertfile = CACertFile, certfile = CertFile, cert = Cert}, Role) -> - {ok, CertDbRef, CertDbHandle, CacheHandle} = + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle} = try Certs = case CaCerts of undefined -> @@ -1083,38 +1095,38 @@ init_certificates(#ssl_options{cacerts = CaCerts, _ -> {der, CaCerts} end, - {ok, _, _, _} = ssl_manager:connection_init(Certs, Role) + {ok, _, _, _, _, _} = ssl_manager:connection_init(Certs, Role) catch Error:Reason -> handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile, erlang:get_stacktrace()) end, - init_certificates(Cert, CertDbRef, CertDbHandle, CacheHandle, CertFile, Role). + init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, Role). -init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, "", _) -> - {ok, CertDbRef, CertDbHandle, CacheHandle, undefined}; +init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, <<>>, _) -> + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, undefined}; -init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, CertFile, client) -> +init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, client) -> try - [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle), - {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert} + [OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle), + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, OwnCert} catch _Error:_Reason -> - {ok, CertDbRef, CertDbHandle, CacheHandle, undefined} + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, undefined} end; -init_certificates(undefined, CertDbRef, CertDbHandle, CacheRef, CertFile, server) -> +init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CertFile, server) -> try - [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle), - {ok, CertDbRef, CertDbHandle, CacheRef, OwnCert} + [OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle), + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, OwnCert} catch Error:Reason -> handle_file_error(?LINE, Error, Reason, CertFile, ecertfile, erlang:get_stacktrace()) end; -init_certificates(Cert, CertDbRef, CertDbHandle, CacheRef, _, _) -> - {ok, CertDbRef, CertDbHandle, CacheRef, Cert}. +init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, _, _) -> + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, Cert}. -init_private_key(_, undefined, "", _Password, _Client) -> +init_private_key(_, undefined, <<>>, _Password, _Client) -> undefined; init_private_key(DbHandle, undefined, KeyFile, Password, _) -> try @@ -1223,13 +1235,13 @@ certify_client(#state{client_certificate_requested = true, role = client, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}, socket = Socket, - tls_handshake_hashes = Hashes0} = State) -> + tls_handshake_history = Handshake0} = State) -> Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client), - {BinCert, ConnectionStates1, Hashes1} = - encode_handshake(Certificate, Version, ConnectionStates0, Hashes0), + {BinCert, ConnectionStates, Handshake} = + encode_handshake(Certificate, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinCert), - State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}; + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; certify_client(#state{client_certificate_requested = false} = State) -> State. @@ -1241,17 +1253,19 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, private_key = PrivateKey, session = #session{master_secret = MasterSecret, own_certificate = OwnCert}, - tls_handshake_hashes = Hashes0} = State) -> + hashsign_algorithm = HashSign, + tls_handshake_history = Handshake0} = State) -> + %%TODO: for TLS 1.2 we can choose a different/stronger HashSign combination for this. case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, - Version, PrivateKey, Hashes0) of + Version, HashSign, PrivateKey, Handshake0) of #certificate_verify{} = Verified -> - {BinVerified, ConnectionStates1, Hashes1} = + {BinVerified, ConnectionStates, Handshake} = encode_handshake(Verified, Version, - ConnectionStates0, Hashes0), + ConnectionStates0, Handshake0), Transport:send(Socket, BinVerified), - State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}; + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; ignore -> State; #alert{} = Alert -> @@ -1261,7 +1275,7 @@ verify_client_cert(#state{client_certificate_requested = false} = State) -> State. do_server_hello(Type, #state{negotiated_version = Version, - session = #session{session_id = SessId} = Session, + session = #session{session_id = SessId}, connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}} = State0) when is_atom(Type) -> @@ -1269,29 +1283,13 @@ do_server_hello(Type, #state{negotiated_version = Version, ServerHello = ssl_handshake:server_hello(SessId, Version, ConnectionStates0, Renegotiation), - State1 = server_hello(ServerHello, State0), + State = server_hello(ServerHello, State0), case Type of new -> - new_server_hello(ServerHello, State1); + new_server_hello(ServerHello, State); resumed -> - ConnectionStates1 = State1#state.connection_states, - case ssl_handshake:master_secret(Version, Session, - ConnectionStates1, server) of - {_, ConnectionStates2} -> - State2 = State1#state{connection_states=ConnectionStates2, - session = Session}, - {ConnectionStates, Hashes} = - finalize_handshake(State2, abbreviated), - State3 = State2#state{connection_states = - ConnectionStates, - tls_handshake_hashes = Hashes}, - {Record, State} = next_record(State3), - next_state(hello, abbreviated, Record, State); - #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State1), - {stop, normal, State1} - end + resumed_server_hello(State) end. new_server_hello(#server_hello{cipher_suite = CipherSuite, @@ -1314,6 +1312,27 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite, {stop, normal, State0} end. +resumed_server_hello(#state{session = Session, + connection_states = ConnectionStates0, + negotiated_version = Version} = State0) -> + + case ssl_handshake:master_secret(Version, Session, + ConnectionStates0, server) of + {_, ConnectionStates1} -> + State1 = State0#state{connection_states = ConnectionStates1, + session = Session}, + {ConnectionStates, Handshake} = + finalize_handshake(State1, abbreviated), + State2 = State1#state{connection_states = + ConnectionStates, + tls_handshake_history = Handshake}, + {Record, State} = next_record(State2), + next_state(hello, abbreviated, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, hello, State0), + {stop, normal, State0} + end. + handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) -> Session = Session0#session{session_id = NewId, cipher_suite = CipherSuite, @@ -1329,10 +1348,10 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}), case ssl_handshake:master_secret(Version, Session, ConnectionStates0, client) of - {_, ConnectionStates1} -> + {_, ConnectionStates} -> {Record, State} = next_record(State0#state{ - connection_states = ConnectionStates1, + connection_states = ConnectionStates, session = Session}), next_state(hello, abbreviated, Record, State); #alert{} = Alert -> @@ -1345,11 +1364,11 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} = State0) -> try do_client_certify_and_key_exchange(State0) of State1 = #state{} -> - {ConnectionStates, Hashes} = finalize_handshake(State1, certify), + {ConnectionStates, Handshake} = finalize_handshake(State1, certify), State2 = State1#state{connection_states = ConnectionStates, %% Reinitialize client_certificate_requested = false, - tls_handshake_hashes = Hashes}, + tls_handshake_history = Handshake}, {Record, State} = next_record(State2), next_state(certify, cipher, Record, State) catch @@ -1372,29 +1391,30 @@ server_hello(ServerHello, #state{transport_cb = Transport, socket = Socket, negotiated_version = Version, connection_states = ConnectionStates0, - tls_handshake_hashes = Hashes0} = State) -> + tls_handshake_history = Handshake0} = State) -> CipherSuite = ServerHello#server_hello.cipher_suite, - {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(ServerHello, Version, ConnectionStates0, Hashes0), + {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite), + {BinMsg, ConnectionStates1, Handshake1} = + encode_handshake(ServerHello, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1, - key_algorithm = KeyAlgorithm}. + tls_handshake_history = Handshake1, + key_algorithm = KeyAlgorithm, + hashsign_algorithm = default_hashsign(Version, KeyAlgorithm)}. server_hello_done(#state{transport_cb = Transport, socket = Socket, negotiated_version = Version, - connection_states = ConnectionStates, - tls_handshake_hashes = Hashes} = State) -> + connection_states = ConnectionStates0, + tls_handshake_history = Handshake0} = State) -> HelloDone = ssl_handshake:server_hello_done(), - {BinHelloDone, NewConnectionStates, NewHashes} = - encode_handshake(HelloDone, Version, ConnectionStates, Hashes), + {BinHelloDone, ConnectionStates, Handshake} = + encode_handshake(HelloDone, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinHelloDone), - State#state{connection_states = NewConnectionStates, - tls_handshake_hashes = NewHashes}. + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}. certify_server(#state{key_algorithm = dh_anon} = State) -> State; @@ -1402,18 +1422,18 @@ certify_server(#state{key_algorithm = dh_anon} = State) -> certify_server(#state{transport_cb = Transport, socket = Socket, negotiated_version = Version, - connection_states = ConnectionStates, - tls_handshake_hashes = Hashes, + connection_states = ConnectionStates0, + tls_handshake_history = Handshake0, cert_db = CertDbHandle, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}} = State) -> case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of CertMsg = #certificate{} -> - {BinCertMsg, NewConnectionStates, NewHashes} = - encode_handshake(CertMsg, Version, ConnectionStates, Hashes), + {BinCertMsg, ConnectionStates, Handshake} = + encode_handshake(CertMsg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinCertMsg), - State#state{connection_states = NewConnectionStates, - tls_handshake_hashes = NewHashes + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake }; Alert = #alert{} -> throw(Alert) @@ -1422,11 +1442,12 @@ certify_server(#state{transport_cb = Transport, key_exchange(#state{role = server, key_algorithm = rsa} = State) -> State; key_exchange(#state{role = server, key_algorithm = Algo, + hashsign_algorithm = HashSignAlgo, diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params, private_key = PrivateKey, connection_states = ConnectionStates0, negotiated_version = Version, - tls_handshake_hashes = Hashes0, + tls_handshake_history = Handshake0, socket = Socket, transport_cb = Transport } = State) @@ -1439,16 +1460,16 @@ key_exchange(#state{role = server, key_algorithm = Algo, SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, {dh, Keys, Params, - Algo, ClientRandom, + Msg = ssl_handshake:key_exchange(server, Version, {dh, Keys, Params, + HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), - {BinMsg, ConnectionStates, Hashes1} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, diffie_hellman_keys = Keys, - tls_handshake_hashes = Hashes1}; + tls_handshake_history = Handshake}; key_exchange(#state{role = client, connection_states = ConnectionStates0, @@ -1457,56 +1478,61 @@ key_exchange(#state{role = client, negotiated_version = Version, premaster_secret = PremasterSecret, socket = Socket, transport_cb = Transport, - tls_handshake_hashes = Hashes0} = State) -> - Msg = rsa_key_exchange(PremasterSecret, PublicKeyInfo), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + tls_handshake_history = Handshake0} = State) -> + Msg = rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), - State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}; + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; key_exchange(#state{role = client, connection_states = ConnectionStates0, key_algorithm = Algorithm, negotiated_version = Version, diffie_hellman_keys = {DhPubKey, _}, socket = Socket, transport_cb = Transport, - tls_handshake_hashes = Hashes0} = State) + tls_handshake_history = Handshake0} = State) when Algorithm == dhe_dss; Algorithm == dhe_rsa; Algorithm == dh_anon -> - Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), - State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}. + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}. -rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) +rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) when Algorithm == ?rsaEncryption; Algorithm == ?md2WithRSAEncryption; Algorithm == ?md5WithRSAEncryption; - Algorithm == ?sha1WithRSAEncryption -> - ssl_handshake:key_exchange(client, + Algorithm == ?sha1WithRSAEncryption; + Algorithm == ?sha224WithRSAEncryption; + Algorithm == ?sha256WithRSAEncryption; + Algorithm == ?sha384WithRSAEncryption; + Algorithm == ?sha512WithRSAEncryption + -> + ssl_handshake:key_exchange(client, Version, {premaster_secret, PremasterSecret, PublicKeyInfo}); -rsa_key_exchange(_, _) -> +rsa_key_exchange(_, _, _) -> throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)). request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, connection_states = ConnectionStates0, cert_db = CertDbHandle, cert_db_ref = CertDbRef, - tls_handshake_hashes = Hashes0, + tls_handshake_history = Handshake0, negotiated_version = Version, socket = Socket, transport_cb = Transport} = State) -> Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{client_certificate_requested = true, - connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}; + connection_states = ConnectionStates, + tls_handshake_history = Handshake}; request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} = State) -> State. @@ -1532,14 +1558,16 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version, transport_cb = Transport, session = Session, connection_states = ConnectionStates0, - tls_handshake_hashes = Hashes0}, StateName) -> + tls_handshake_history = Handshake0}, StateName) -> MasterSecret = Session#session.master_secret, - Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes0), + Finished = ssl_handshake:finished(Version, Role, + get_current_connection_state_prf(ConnectionStates0, write), + MasterSecret, Handshake0), ConnectionStates1 = save_verify_data(Role, Finished, ConnectionStates0, StateName), - {BinFinished, ConnectionStates, Hashes} = - encode_handshake(Finished, Version, ConnectionStates1, Hashes0), + {BinFinished, ConnectionStates, Handshake} = + encode_handshake(Finished, Version, ConnectionStates1, Handshake0), Transport:send(Socket, BinFinished), - {ConnectionStates, Hashes}. + {ConnectionStates, Handshake}. save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, certify) -> ssl_record:set_client_verify_data(current_write, Data, ConnectionStates); @@ -1563,36 +1591,41 @@ handle_server_key( #server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey}, - signed_params = Signed}, - #state{public_key_info = PubKeyInfo, - key_algorithm = KeyAlgo, + signed_params = Signed, + hashsign = HashSign}, + #state{negotiated_version = Version, + public_key_info = PubKeyInfo, connection_states = ConnectionStates} = State) -> PLen = size(P), GLen = size(G), YLen = size(ServerPublicDhKey), + HashAlgo = connection_hash_algo(HashSign, State), ConnectionState = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Hash = ssl_handshake:server_key_exchange_hash(KeyAlgo, + Hash = ssl_handshake:server_key_exchange_hash(HashAlgo, <<ClientRandom/binary, ServerRandom/binary, ?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), ServerPublicDhKey/binary>>), - - case verify_dh_params(Signed, Hash, PubKeyInfo) of + + case verify_dh_params(Version, Signed, Hash, HashAlgo, PubKeyInfo) of true -> dh_master_secret(P, G, ServerPublicDhKey, undefined, State); false -> ?ALERT_REC(?FATAL, ?DECRYPT_ERROR) end. -verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) -> +verify_dh_params({3, Minor}, Signed, Hashes, HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) + when Minor >= 3 -> + public_key:verify({digest, Hashes}, HashAlgo, Signed, PubKey); +verify_dh_params(_Version, Signed, Hashes, _HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) -> case public_key:decrypt_public(Signed, PubKey, [{rsa_pad, rsa_pkcs1_padding}]) of Hashes -> @@ -1600,8 +1633,8 @@ verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) -> _ -> false end; -verify_dh_params(Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) -> - public_key:verify(Hash, none, Signed, {PublicKey, PublicKeyParams}). +verify_dh_params(_Version, Signed, Hash, HashAlgo, {?'id-dsa', PublicKey, PublicKeyParams}) -> + public_key:verify({digest, Hash}, HashAlgo, Signed, {PublicKey, PublicKeyParams}). dh_master_secret(Prime, Base, PublicDhKey, undefined, State) -> PMpint = mpint_binary(Prime), @@ -1635,26 +1668,26 @@ cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) -> ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), - {ConnectionStates, Hashes} = + {ConnectionStates, Handshake} = finalize_handshake(State#state{connection_states = ConnectionStates1, session = Session}, cipher), next_state_connection(cipher, ack_connection(State#state{connection_states = ConnectionStates, session = Session, - tls_handshake_hashes = - Hashes})). + tls_handshake_history = + Handshake})). encode_alert(#alert{} = Alert, Version, ConnectionStates) -> ssl_record:encode_alert_record(Alert, Version, ConnectionStates). encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> ssl_record:encode_change_cipher_spec(Version, ConnectionStates). -encode_handshake(HandshakeRec, Version, ConnectionStates0, Hashes0) -> +encode_handshake(HandshakeRec, Version, ConnectionStates0, Handshake0) -> Frag = ssl_handshake:encode_handshake(HandshakeRec, Version), - Hashes1 = ssl_handshake:update_hashes(Hashes0, Frag), + Handshake1 = ssl_handshake:update_handshake_history(Handshake0, Frag), {E, ConnectionStates1} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), - {E, ConnectionStates1, Hashes1}. + {E, ConnectionStates1, Handshake1}. encode_packet(Data, #socket_options{packet=Packet}) -> case Packet of @@ -1697,7 +1730,7 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> read_application_data(Data, #state{user_application = {_Mon, Pid}, socket_options = SOpts, bytes_to_read = BytesToRead, - recv_from = From, + start_or_recv_from = RecvFrom, user_data_buffer = Buffer0} = State0) -> Buffer1 = if Buffer0 =:= <<>> -> Data; @@ -1706,9 +1739,9 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, end, case get_data(SOpts, BytesToRead, Buffer1) of {ok, ClientData, Buffer} -> % Send data - SocketOpt = deliver_app_data(SOpts, ClientData, Pid, From), + SocketOpt = deliver_app_data(SOpts, ClientData, Pid, RecvFrom), State = State0#state{user_data_buffer = Buffer, - recv_from = undefined, + start_or_recv_from = undefined, bytes_to_read = 0, socket_options = SocketOpt }, @@ -1723,7 +1756,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, {more, Buffer} -> % no reply, we need more data next_record(State0#state{user_data_buffer = Buffer}); {error,_Reason} -> %% Invalid packet in packet mode - deliver_packet_error(SOpts, Buffer1, Pid, From), + deliver_packet_error(SOpts, Buffer1, Pid, RecvFrom), {stop, normal, State0} end. @@ -1843,14 +1876,15 @@ format_reply(binary, _, N, Data) when N > 0 -> % Header mode format_reply(binary, _, _, Data) -> Data; format_reply(list, Packet, _, Data) - when Packet == http; Packet == {http, headers}; Packet == http_bin; Packet == {http_bin, headers}; Packet == httph; - Packet == httph_bin-> + when Packet == http; Packet == {http, headers}; + Packet == http_bin; Packet == {http_bin, headers}; + Packet == httph; Packet == httph_bin -> Data; format_reply(list, _,_, Data) -> binary_to_list(Data). header(0, <<>>) -> - <<>>; + []; header(_, <<>>) -> []; header(0, Binary) -> @@ -1906,25 +1940,26 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) -> %% This message should not be included in handshake %% message hashes. Starts new handshake (renegotiation) - Hs0 = ssl_handshake:init_hashes(), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0, + Hs0 = ssl_handshake:init_handshake_history(), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0, renegotiation = {true, peer}}); ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> %% This message should not be included in handshake %% message hashes. Already in negotiation so it will be ignored! ?MODULE:SName(Packet, State); ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> - Hs0 = ssl_handshake:init_hashes(), - Hs1 = ssl_handshake:update_hashes(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1, + Version = Packet#client_hello.client_version, + Hs0 = ssl_handshake:init_handshake_history(), + Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1, renegotiation = {true, peer}}); - ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) -> - Hs1 = ssl_handshake:update_hashes(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1}); + ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) -> + Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1}); (_, StopState) -> StopState end, try - {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0), + {Packets, Buf} = ssl_handshake:get_tls_handshake(Version,Data,Buf0), State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf}, handle_tls_handshake(Handle, Next, State) catch throw:#alert{} = Alert -> @@ -2004,22 +2039,22 @@ next_state_connection(StateName, #state{send_queue = Queue0, next_state_is_connection(StateName, State) end. -%% In next_state_is_connection/1: clear tls_handshake_hashes, +%% In next_state_is_connection/1: clear tls_handshake, %% premaster_secret and public_key_info (only needed during handshake) %% to reduce memory foot print of a connection. next_state_is_connection(_, State = - #state{recv_from = From, + #state{start_or_recv_from = RecvFrom, socket_options = - #socket_options{active = false}}) when From =/= undefined -> + #socket_options{active = false}}) when RecvFrom =/= undefined -> passive_receive(State#state{premaster_secret = undefined, public_key_info = undefined, - tls_handshake_hashes = {<<>>, <<>>}}, connection); + tls_handshake_history = ssl_handshake:init_handshake_history()}, connection); next_state_is_connection(StateName, State0) -> {Record, State} = next_record_if_active(State0), next_state(StateName, connection, Record, State#state{premaster_secret = undefined, public_key_info = undefined, - tls_handshake_hashes = {<<>>, <<>>}}). + tls_handshake_history = ssl_handshake:init_handshake_history()}). register_session(client, Host, Port, #session{is_resumable = new} = Session0) -> Session = Session0#session{is_resumable = true}, @@ -2073,7 +2108,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, log_alert = true, session_cache_cb = SessionCacheCb, renegotiation = {false, first}, - recv_from = undefined, + start_or_recv_from = undefined, send_queue = queue:new() }. @@ -2177,7 +2212,7 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) -> handle_alerts(Alerts, handle_alert(Alert, StateName, State)). handle_alert(#alert{level = ?FATAL} = Alert, StateName, - #state{from = From, host = Host, port = Port, session = Session, + #state{start_or_recv_from = From, host = Host, port = Port, session = Session, user_application = {_Mon, Pid}, log_alert = Log, role = Role, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), @@ -2259,13 +2294,13 @@ handle_own_alert(Alert, Version, StateName, ok end. -handle_normal_shutdown(Alert, _, #state{from = User, role = Role, renegotiation = {false, first}}) -> - alert_user(User, Alert, Role); +handle_normal_shutdown(Alert, _, #state{start_or_recv_from = StartFrom, role = Role, renegotiation = {false, first}}) -> + alert_user(StartFrom, Alert, Role); handle_normal_shutdown(Alert, StateName, #state{socket_options = Opts, user_application = {_Mon, Pid}, - from = User, role = Role}) -> - alert_user(StateName, Opts, Pid, User, Alert, Role). + start_or_recv_from = RecvFrom, role = Role}) -> + alert_user(StateName, Opts, Pid, RecvFrom, Alert, Role). handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), @@ -2273,7 +2308,7 @@ handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = Stat {stop, normal, State}. make_premaster_secret({MajVer, MinVer}, rsa) -> - Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2), + Rand = ssl:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2), <<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>; make_premaster_secret(_, _) -> undefined. @@ -2291,17 +2326,17 @@ ack_connection(#state{renegotiation = {true, From}} = State) -> gen_fsm:reply(From, ok), State#state{renegotiation = undefined}; ack_connection(#state{renegotiation = {false, first}, - from = From} = State) when From =/= undefined -> - gen_fsm:reply(From, connected), - State#state{renegotiation = undefined}; + start_or_recv_from = StartFrom} = State) when StartFrom =/= undefined -> + gen_fsm:reply(StartFrom, connected), + State#state{renegotiation = undefined, start_or_recv_from = undefined}; ack_connection(State) -> State. renegotiate(#state{role = client} = State) -> %% Handle same way as if server requested %% the renegotiation - Hs0 = ssl_handshake:init_hashes(), - connection(#hello_request{}, State#state{tls_handshake_hashes = Hs0}); + Hs0 = ssl_handshake:init_handshake_history(), + connection(#hello_request{}, State#state{tls_handshake_history = Hs0}); renegotiate(#state{role = server, socket = Socket, transport_cb = Transport, @@ -2309,13 +2344,13 @@ renegotiate(#state{role = server, connection_states = ConnectionStates0} = State0) -> HelloRequest = ssl_handshake:hello_request(), Frag = ssl_handshake:encode_handshake(HelloRequest, Version), - Hs0 = ssl_handshake:init_hashes(), + Hs0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), Transport:send(Socket, BinMsg), {Record, State} = next_record(State0#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hs0}), + tls_handshake_history = Hs0}), next_state(connection, hello, Record, State#state{allow_renegotiate = true}). notify_senders(SendQueue) -> @@ -2359,7 +2394,74 @@ linux_workaround_transport_delivery_problems(#alert{level = ?FATAL}, Socket) -> linux_workaround_transport_delivery_problems(_, _) -> ok. -get_timeout(#state{ssl_options=#ssl_options{hibernate_after=undefined}}) -> +get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) -> infinity; -get_timeout(#state{ssl_options=#ssl_options{hibernate_after=HibernateAfter}}) -> +get_timeout(#state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}}) -> HibernateAfter. + +handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>}}) -> + %% No trusted certs specified + ok; +handle_trusted_certs_db(#state{cert_db_ref = Ref, + cert_db = CertDb, + ssl_options = #ssl_options{cacertfile = undefined}}) -> + %% Certs provided as DER directly can not be shared + %% with other connections and it is safe to delete them when the connection ends. + ssl_certificate_db:remove_trusted_certs(Ref, CertDb); +handle_trusted_certs_db(#state{file_ref_db = undefined}) -> + %% Something went wrong early (typically cacertfile does not exist) so there is nothing to handle + ok; +handle_trusted_certs_db(#state{cert_db_ref = Ref, + file_ref_db = RefDb, + ssl_options = #ssl_options{cacertfile = File}}) -> + case ssl_certificate_db:ref_count(Ref, RefDb, -1) of + 0 -> + ssl_manager:clean_cert_db(Ref, File); + _ -> + ok + end. + +get_current_connection_state_prf(CStates, Direction) -> + CS = ssl_record:current_connection_state(CStates, Direction), + CS#connection_state.security_parameters#security_parameters.prf_algorithm. +get_pending_connection_state_prf(CStates, Direction) -> + CS = ssl_record:pending_connection_state(CStates, Direction), + CS#connection_state.security_parameters#security_parameters.prf_algorithm. + +connection_hash_algo({HashAlgo, _}, _State) -> + HashAlgo; +connection_hash_algo(_, #state{hashsign_algorithm = {HashAlgo, _}}) -> + HashAlgo. + +%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms +%% If the client does not send the signature_algorithms extension, the +%% server MUST do the following: +%% +%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA, +%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had +%% sent the value {sha1,rsa}. +%% +%% - If the negotiated key exchange algorithm is one of (DHE_DSS, +%% DH_DSS), behave as if the client had sent the value {sha1,dsa}. +%% +%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA, +%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}. + +default_hashsign(_Version = {Major, Minor}, KeyExchange) + when Major == 3 andalso Minor >= 3 andalso + (KeyExchange == rsa orelse + KeyExchange == dhe_rsa orelse + KeyExchange == dh_rsa) -> + {sha, rsa}; +default_hashsign(_Version, KeyExchange) + when KeyExchange == rsa; + KeyExchange == dhe_rsa; + KeyExchange == dh_rsa -> + {md5sha, rsa}; +default_hashsign(_Version, KeyExchange) + when KeyExchange == dhe_dss; + KeyExchange == dh_dss -> + {sha, dsa}; +default_hashsign(_Version, KeyExchange) + when KeyExchange == dh_anon -> + {null, anon}. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 2e0a3de182..bb26302fff 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -30,13 +30,13 @@ -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([master_secret/4, client_hello/6, server_hello/4, hello/4, +-export([master_secret/4, client_hello/8, server_hello/4, hello/4, hello_request/0, certify/7, certificate/4, - client_certificate_verify/5, certificate_verify/5, - certificate_request/3, key_exchange/2, server_key_exchange_hash/2, - finished/4, verify_connection/5, get_tls_handshake/2, + client_certificate_verify/6, certificate_verify/6, + certificate_request/3, key_exchange/3, server_key_exchange_hash/2, + finished/5, verify_connection/6, get_tls_handshake/3, decode_client_key/3, server_hello_done/0, - encode_handshake/2, init_hashes/0, update_hashes/2, + encode_handshake/2, init_handshake_history/0, update_handshake_history/2, decrypt_premaster_secret/2, prf/5]). -export([dec_hello_extensions/2]). @@ -51,14 +51,17 @@ %%==================================================================== %%-------------------------------------------------------------------- -spec client_hello(host(), inet:port_number(), #connection_states{}, - #ssl_options{}, boolean(), der_cert()) -> #client_hello{}. + #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> + #client_hello{}. %% %% Description: Creates a client hello message. %%-------------------------------------------------------------------- -client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, - ciphers = UserSuites} - = SslOpts, Renegotiation, OwnCert) -> - +client_hello(Host, Port, ConnectionStates, + #ssl_options{versions = Versions, + ciphers = UserSuites + } = SslOpts, + Cache, CacheCb, Renegotiation, OwnCert) -> + Fun = fun(Version) -> ssl_record:protocol_version(Version) end, @@ -67,15 +70,16 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, SecParams = Pending#connection_state.security_parameters, Ciphers = available_suites(UserSuites, Version), - Id = ssl_manager:client_session_id(Host, Port, SslOpts, OwnCert), + Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), - #client_hello{session_id = Id, + #client_hello{session_id = Id, client_version = Version, cipher_suites = cipher_suites(Ciphers, Renegotiation), compression_methods = ssl_record:compressions(), random = SecParams#security_parameters.client_random, - renegotiation_info = - renegotiation_info(client, ConnectionStates, Renegotiation) + renegotiation_info = + renegotiation_info(client, ConnectionStates, Renegotiation), + hash_signs = default_hash_signs() }. %%-------------------------------------------------------------------- @@ -118,17 +122,18 @@ hello_request() -> %%-------------------------------------------------------------------- hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, compression_method = Compression, random = Random, - session_id = SessionId, renegotiation_info = Info}, + session_id = SessionId, renegotiation_info = Info, + hash_signs = _HashSigns}, #ssl_options{secure_renegotiate = SecureRenegotation}, ConnectionStates0, Renegotiation) -> - +%%TODO: select hash and signature algorigthm case ssl_record:is_acceptable_version(Version) of true -> case handle_renegotiation_info(client, Info, ConnectionStates0, Renegotiation, SecureRenegotation, []) of {ok, ConnectionStates1} -> ConnectionStates = - hello_pending_connection_states(client, CipherSuite, Random, + hello_pending_connection_states(client, Version, CipherSuite, Random, Compression, ConnectionStates1), {Version, SessionId, ConnectionStates}; #alert{} = Alert -> @@ -140,10 +145,12 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, hello(#client_hello{client_version = ClientVersion, random = Random, cipher_suites = CipherSuites, - renegotiation_info = Info} = Hello, + renegotiation_info = Info, + hash_signs = _HashSigns} = Hello, #ssl_options{versions = Versions, secure_renegotiate = SecureRenegotation} = SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> +%% TODO: select hash and signature algorithm Version = select_version(ClientVersion, Versions), case ssl_record:is_acceptable_version(Version) of true -> @@ -161,6 +168,7 @@ hello(#client_hello{client_version = ClientVersion, random = Random, {ok, ConnectionStates1} -> ConnectionStates = hello_pending_connection_states(server, + Version, CipherSuite, Random, Compression, @@ -212,18 +220,23 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef, end, {Role, UserState0}} end, - {TrustedErlCert, CertPath} = - ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef), - - case public_key:pkix_path_validation(TrustedErlCert, - CertPath, - [{max_path_length, - MaxPathLen}, - {verify_fun, ValidationFunAndState}]) of - {ok, {PublicKeyInfo,_}} -> - {PeerCert, PublicKeyInfo}; - {error, Reason} -> - path_validation_alert(Reason) + try + {TrustedErlCert, CertPath} = + ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef), + case public_key:pkix_path_validation(TrustedErlCert, + CertPath, + [{max_path_length, + MaxPathLen}, + {verify_fun, ValidationFunAndState}]) of + {ok, {PublicKeyInfo,_}} -> + {PeerCert, PublicKeyInfo}; + {error, Reason} -> + path_validation_alert(Reason) + end + catch + error:_ -> + %% ASN-1 decode of certificate somehow failed + ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN) end. %%-------------------------------------------------------------------- @@ -254,54 +267,51 @@ certificate(OwnCert, CertDbHandle, CertDbRef, server) -> %%-------------------------------------------------------------------- -spec client_certificate_verify(undefined | der_cert(), binary(), - tls_version(), private_key(), - {{binary(), binary()},{binary(), binary()}}) -> + tls_version(), term(), private_key(), + tls_handshake_history()) -> #certificate_verify{} | ignore | #alert{}. %% %% Description: Creates a certificate_verify message, called by the client. %%-------------------------------------------------------------------- -client_certificate_verify(undefined, _, _, _, _) -> +client_certificate_verify(undefined, _, _, _, _, _) -> ignore; -client_certificate_verify(_, _, _, undefined, _) -> +client_certificate_verify(_, _, _, _, undefined, _) -> ignore; client_certificate_verify(OwnCert, MasterSecret, Version, - PrivateKey, {Hashes0, _}) -> + {HashAlgo, SignAlgo}, + PrivateKey, {Handshake, _}) -> case public_key:pkix_is_fixed_dh_cert(OwnCert) of true -> ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE); - false -> - Hashes = - calc_certificate_verify(Version, MasterSecret, - alg_oid(PrivateKey), Hashes0), - Signed = digitally_signed(Hashes, PrivateKey), - #certificate_verify{signature = Signed} + false -> + Hashes = + calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), + Signed = digitally_signed(Version, Hashes, HashAlgo, PrivateKey), + #certificate_verify{signature = Signed, hashsign_algorithm = {HashAlgo, SignAlgo}} end. %%-------------------------------------------------------------------- --spec certificate_verify(binary(), public_key_info(), tls_version(), - binary(), {_, {binary(), binary()}}) -> valid | #alert{}. +-spec certificate_verify(binary(), public_key_info(), tls_version(), term(), + binary(), tls_handshake_history()) -> valid | #alert{}. %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- -certificate_verify(Signature, {?'rsaEncryption'= Algorithm, PublicKey, _}, Version, - MasterSecret, {_, Hashes0}) -> - Hashes = calc_certificate_verify(Version, MasterSecret, - Algorithm, Hashes0), - case public_key:decrypt_public(Signature, PublicKey, - [{rsa_pad, rsa_pkcs1_padding}]) of - Hashes -> +certificate_verify(Signature, {?'rsaEncryption', PublicKey, _}, Version, + {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) -> + Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), + case certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, Version) of + true -> valid; _ -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end; -certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams}, Version, - MasterSecret, {_, Hashes0}) -> - Hashes = calc_certificate_verify(Version, MasterSecret, - Algorithm, Hashes0), - case public_key:verify(Hashes, none, Signature, {PublicKey, PublicKeyParams}) of - true -> - valid; - false -> +certificate_verify(Signature, {?'id-dsa', PublicKey, PublicKeyParams}, Version, + {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) -> + Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), + case public_key:verify({digest, Hashes}, sha, Signature, {PublicKey, PublicKeyParams}) of + true -> + valid; + false -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end. @@ -317,36 +327,38 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) -> #security_parameters{cipher_suite = CipherSuite}} = ssl_record:pending_connection_state(ConnectionStates, read), Types = certificate_types(CipherSuite), + HashSigns = default_hash_signs(), Authorities = certificate_authorities(CertDbHandle, CertDbRef), #certificate_request{ certificate_types = Types, + hashsign_algorithms = HashSigns, certificate_authorities = Authorities }. %%-------------------------------------------------------------------- --spec key_exchange(client | server, +-spec key_exchange(client | server, tls_version(), {premaster_secret, binary(), public_key_info()} | {dh, binary()} | - {dh, {binary(), binary()}, #'DHParameter'{}, key_algo(), + {dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()}, binary(), binary(), private_key()}) -> #client_key_exchange{} | #server_key_exchange{}. %% %% Description: Creates a keyexchange message. %%-------------------------------------------------------------------- -key_exchange(client, {premaster_secret, Secret, {_, PublicKey, _}}) -> +key_exchange(client, _Version, {premaster_secret, Secret, {_, PublicKey, _}}) -> EncPremasterSecret = encrypted_premaster_secret(Secret, PublicKey), #client_key_exchange{exchange_keys = EncPremasterSecret}; -key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) -> +key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) -> #client_key_exchange{ exchange_keys = #client_diffie_hellman_public{ dh_public = PublicKey} }; -key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, +key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, #'DHParameter'{prime = P, base = G}, - KeyAlgo, ClientRandom, ServerRandom, PrivateKey}) -> + {HashAlgo, SignAlgo}, ClientRandom, ServerRandom, PrivateKey}) -> <<?UINT32(_), PBin/binary>> = crypto:mpint(P), <<?UINT32(_), GBin/binary>> = crypto:mpint(G), PLen = byte_size(PBin), @@ -355,20 +367,22 @@ key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, ServerDHParams = #server_dh_params{dh_p = PBin, dh_g = GBin, dh_y = PublicKey}, - case KeyAlgo of - dh_anon -> + case HashAlgo of + null -> #server_key_exchange{params = ServerDHParams, - signed_params = <<>>}; + signed_params = <<>>, + hashsign = {null, anon}}; _ -> Hash = - server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary, + server_key_exchange_hash(HashAlgo, <<ClientRandom/binary, ServerRandom/binary, ?UINT16(PLen), PBin/binary, ?UINT16(GLen), GBin/binary, ?UINT16(YLen), PublicKey/binary>>), - Signed = digitally_signed(Hash, PrivateKey), + Signed = digitally_signed(Version, Hash, HashAlgo, PrivateKey), #server_key_exchange{params = ServerDHParams, - signed_params = Signed} + signed_params = Signed, + hashsign = {HashAlgo, SignAlgo}} end. %%-------------------------------------------------------------------- @@ -398,10 +412,11 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> ConnectionState = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, - #security_parameters{client_random = ClientRandom, + #security_parameters{prf_algorithm = PrfAlgo, + client_random = ClientRandom, server_random = ServerRandom} = SecParams, try master_secret(Version, - calc_master_secret(Version,PremasterSecret, + calc_master_secret(Version,PrfAlgo,PremasterSecret, ClientRandom, ServerRandom), SecParams, ConnectionStates, Role) catch @@ -413,26 +428,26 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> end. %%-------------------------------------------------------------------- --spec finished(tls_version(), client | server, binary(), {{binary(), binary()},_}) -> +-spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) -> #finished{}. %% %% Description: Creates a handshake finished message %%------------------------------------------------------------------- -finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes +finished(Version, Role, PrfAlgo, MasterSecret, {Handshake, _}) -> % use the current handshake #finished{verify_data = - calc_finished(Version, Role, MasterSecret, Hashes)}. + calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake)}. %%-------------------------------------------------------------------- --spec verify_connection(tls_version(), #finished{}, client | server, binary(), - {_, {binary(), binary()}}) -> verified | #alert{}. +-spec verify_connection(tls_version(), #finished{}, client | server, integer(), binary(), + tls_handshake_history()) -> verified | #alert{}. %% %% Description: Checks the ssl handshake finished message to verify %% the connection. %%------------------------------------------------------------------- verify_connection(Version, #finished{verify_data = Data}, - Role, MasterSecret, {_, {MD5, SHA}}) -> + Role, PrfAlgo, MasterSecret, {_, Handshake}) -> %% use the previous hashes - case calc_finished(Version, Role, MasterSecret, {MD5, SHA}) of + case calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake) of Data -> verified; _ -> @@ -457,17 +472,17 @@ encode_handshake(Package, Version) -> [MsgType, ?uint24(Len), Bin]. %%-------------------------------------------------------------------- --spec get_tls_handshake(binary(), binary() | iolist()) -> +-spec get_tls_handshake(tls_version(), binary(), binary() | iolist()) -> {[tls_handshake()], binary()}. %% %% Description: Given buffered and new data from ssl_record, collects %% and returns it as a list of handshake messages, also returns leftover %% data. %%-------------------------------------------------------------------- -get_tls_handshake(Data, <<>>) -> - get_tls_handshake_aux(Data, []); -get_tls_handshake(Data, Buffer) -> - get_tls_handshake_aux(list_to_binary([Buffer, Data]), []). +get_tls_handshake(Version, Data, <<>>) -> + get_tls_handshake_aux(Version, Data, []); +get_tls_handshake(Version, Data, Buffer) -> + get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), []). %%-------------------------------------------------------------------- -spec decode_client_key(binary(), key_algo(), tls_version()) -> @@ -479,39 +494,34 @@ decode_client_key(ClientKey, Type, Version) -> dec_client_key(ClientKey, key_exchange_alg(Type), Version). %%-------------------------------------------------------------------- --spec init_hashes() ->{{binary(), binary()}, {binary(), binary()}}. +-spec init_handshake_history() -> tls_handshake_history(). %% -%% Description: Calls crypto hash (md5 and sha) init functions to -%% initalize the hash context. +%% Description: Initialize the empty handshake history buffer. %%-------------------------------------------------------------------- -init_hashes() -> - T = {crypto:md5_init(), crypto:sha_init()}, - {T, T}. +init_handshake_history() -> + {[], []}. %%-------------------------------------------------------------------- --spec update_hashes({{binary(), binary()}, {binary(), binary()}}, Data ::term()) -> - {{binary(), binary()}, {binary(), binary()}}. +-spec update_handshake_history(tls_handshake_history(), Data ::term()) -> + tls_handshake_history(). %% -%% Description: Calls crypto hash (md5 and sha) update functions to -%% update the hash context with Data. +%% Description: Update the handshake history buffer with Data. %%-------------------------------------------------------------------- -update_hashes(Hashes, % special-case SSL2 client hello - <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor), - ?UINT16(CSLength), ?UINT16(0), - ?UINT16(CDLength), - CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>) -> - update_hashes(Hashes, - <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor), - ?UINT16(CSLength), ?UINT16(0), - ?UINT16(CDLength), - CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>); -update_hashes({{MD50, SHA0}, _Prev}, Data) -> - {MD51, SHA1} = {crypto:md5_update(MD50, Data), - crypto:sha_update(SHA0, Data)}, - {{MD51, SHA1}, {MD50, SHA0}}. +update_handshake_history(Handshake, % special-case SSL2 client hello + <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor), + ?UINT16(CSLength), ?UINT16(0), + ?UINT16(CDLength), + CipherSuites:CSLength/binary, + ChallengeData:CDLength/binary>>) -> + update_handshake_history(Handshake, + <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor), + ?UINT16(CSLength), ?UINT16(0), + ?UINT16(CDLength), + CipherSuites:CSLength/binary, + ChallengeData:CDLength/binary>>); +update_handshake_history({Handshake0, _Prev}, Data) -> + {[Data|Handshake0], Handshake0}. %%-------------------------------------------------------------------- -spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary(). @@ -524,23 +534,22 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) -> [{rsa_pad, rsa_pkcs1_padding}]) catch _:_ -> + io:format("decrypt_premaster_secret error"), throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR)) end. %%-------------------------------------------------------------------- --spec server_key_exchange_hash(rsa | dhe_rsa| dhe_dss | dh_anon, binary()) -> binary(). - +-spec server_key_exchange_hash(md5sha | md5 | sha | sha224 |sha256 | sha384 | sha512, binary()) -> binary(). %% %% Description: Calculate server key exchange hash %%-------------------------------------------------------------------- -server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa; - Algorithm == dhe_rsa -> +server_key_exchange_hash(md5sha, Value) -> MD5 = crypto:md5(Value), - SHA = crypto:sha(Value), + SHA = crypto:sha(Value), <<MD5/binary, SHA/binary>>; -server_key_exchange_hash(dhe_dss, Value) -> - crypto:sha(Value). +server_key_exchange_hash(Hash, Value) -> + crypto:hash(Hash, Value). %%-------------------------------------------------------------------- -spec prf(tls_version(), binary(), binary(), [binary()], non_neg_integer()) -> @@ -550,19 +559,20 @@ server_key_exchange_hash(dhe_dss, Value) -> %%-------------------------------------------------------------------- prf({3,0}, _, _, _, _) -> {error, undefined}; -prf({3,N}, Secret, Label, Seed, WantedLength) - when N == 1; N == 2 -> - {ok, ssl_tls1:prf(Secret, Label, Seed, WantedLength)}. +prf({3,1}, Secret, Label, Seed, WantedLength) -> + {ok, ssl_tls1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)}; +prf({3,_N}, Secret, Label, Seed, WantedLength) -> + {ok, ssl_tls1:prf(?SHA256, Secret, Label, Seed, WantedLength)}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), +get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>, Acc) -> Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, - H = dec_hs(Type, Body), - get_tls_handshake_aux(Rest, [{H,Raw} | Acc]); -get_tls_handshake_aux(Data, Acc) -> + H = dec_hs(Version, Type, Body), + get_tls_handshake_aux(Version, Rest, [{H,Raw} | Acc]); +get_tls_handshake_aux(_Version, Data, Acc) -> {lists:reverse(Acc), Data}. path_validation_alert({bad_cert, cert_expired}) -> @@ -584,24 +594,23 @@ path_validation_alert({bad_cert, unknown_ca}) -> path_validation_alert(_) -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE). -select_session(Hello, Port, Session, Version, +select_session(Hello, Port, Session, Version, #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) -> SuggestedSessionId = Hello#client_hello.session_id, - SessionId = ssl_manager:server_session_id(Port, SuggestedSessionId, - SslOpts, Cert), - - Suites = available_suites(Cert, UserSuites, Version), - case ssl_session:is_new(SuggestedSessionId, SessionId) of - true -> - CipherSuite = - select_cipher_suite(Hello#client_hello.cipher_suites, Suites), + {SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId, + SslOpts, Cert, + Cache, CacheCb), + Suites = available_suites(Cert, UserSuites, Version), + case Resumed of + undefined -> + CipherSuite = select_cipher_suite(Hello#client_hello.cipher_suites, Suites), Compressions = Hello#client_hello.compression_methods, Compression = select_compression(Compressions), {new, Session#session{session_id = SessionId, cipher_suite = CipherSuite, compression_method = Compression}}; - false -> - {resumed, CacheCb:lookup(Cache, {Port, SessionId})} + _ -> + {resumed, Resumed} end. available_suites(UserSuites, Version) -> @@ -720,7 +729,7 @@ handle_renegotiation_info(ConnectionStates, SecureRenegotation) -> %% hello messages %% NOTE : Role is the role of the receiver of the hello message %% currently being processed. -hello_pending_connection_states(Role, CipherSuite, Random, Compression, +hello_pending_connection_states(Role, Version, CipherSuite, Random, Compression, ConnectionStates) -> ReadState = ssl_record:pending_connection_state(ConnectionStates, read), @@ -728,30 +737,30 @@ hello_pending_connection_states(Role, CipherSuite, Random, Compression, ssl_record:pending_connection_state(ConnectionStates, write), NewReadSecParams = - hello_security_parameters(Role, ReadState, CipherSuite, + hello_security_parameters(Role, Version, ReadState, CipherSuite, Random, Compression), NewWriteSecParams = - hello_security_parameters(Role, WriteState, CipherSuite, + hello_security_parameters(Role, Version, WriteState, CipherSuite, Random, Compression), ssl_record:update_security_params(NewReadSecParams, NewWriteSecParams, ConnectionStates). -hello_security_parameters(client, ConnectionState, CipherSuite, Random, +hello_security_parameters(client, Version, ConnectionState, CipherSuite, Random, Compression) -> SecParams = ConnectionState#connection_state.security_parameters, - NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams), + NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams), NewSecParams#security_parameters{ server_random = Random, compression_algorithm = Compression }; -hello_security_parameters(server, ConnectionState, CipherSuite, Random, +hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random, Compression) -> SecParams = ConnectionState#connection_state.security_parameters, - NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams), + NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams), NewSecParams#security_parameters{ client_random = Random, compression_algorithm = Compression @@ -785,13 +794,14 @@ master_secret(Version, MasterSecret, #security_parameters{ client_random = ClientRandom, server_random = ServerRandom, hash_size = HashSize, + prf_algorithm = PrfAlgo, key_material_length = KML, expanded_key_material_length = EKML, iv_size = IVS}, ConnectionStates, Role) -> {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, ServerWriteKey, ClientIV, ServerIV} = - setup_keys(Version, MasterSecret, ServerRandom, + setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, EKML, IVS), ConnStates1 = ssl_record:set_master_secret(MasterSecret, ConnectionStates), @@ -806,13 +816,13 @@ master_secret(Version, MasterSecret, #security_parameters{ ServerCipherState, Role)}. -dec_hs(?HELLO_REQUEST, <<>>) -> +dec_hs(_Version, ?HELLO_REQUEST, <<>>) -> #hello_request{}; %% Client hello v2. %% The server must be able to receive such messages, from clients that %% are willing to use ssl v3 or higher, but have ssl v2 compatibility. -dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), +dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), ?UINT16(CSLength), ?UINT16(0), ?UINT16(CDLength), CipherSuites:CSLength/binary, @@ -824,24 +834,27 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), compression_methods = [?NULL], renegotiation_info = undefined }; -dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, +dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, ?UINT16(Cs_length), CipherSuites:Cs_length/binary, ?BYTE(Cm_length), Comp_methods:Cm_length/binary, Extensions/binary>>) -> - - RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions), - undefined), + HelloExtensions = dec_hello_extensions(Extensions), + RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions, + undefined), + HashSigns = proplists:get_value(hash_signs, HelloExtensions, + undefined), #client_hello{ client_version = {Major,Minor}, random = Random, session_id = Session_ID, cipher_suites = from_2bytes(CipherSuites), compression_methods = Comp_methods, - renegotiation_info = RenegotiationInfo + renegotiation_info = RenegotiationInfo, + hash_signs = HashSigns }; -dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, +dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, Cipher_suite:2/binary, ?BYTE(Comp_method)>>) -> #server_hello{ @@ -850,53 +863,81 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, session_id = Session_ID, cipher_suite = Cipher_suite, compression_method = Comp_method, - renegotiation_info = undefined}; + renegotiation_info = undefined, + hash_signs = undefined}; -dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, +dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, Cipher_suite:2/binary, ?BYTE(Comp_method), ?UINT16(ExtLen), Extensions:ExtLen/binary>>) -> - RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions, []), - undefined), + HelloExtensions = dec_hello_extensions(Extensions, []), + RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions, + undefined), + HashSigns = proplists:get_value(hash_signs, HelloExtensions, + undefined), #server_hello{ server_version = {Major,Minor}, random = Random, session_id = Session_ID, cipher_suite = Cipher_suite, compression_method = Comp_method, - renegotiation_info = RenegotiationInfo}; -dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) -> + renegotiation_info = RenegotiationInfo, + hash_signs = HashSigns}; +dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) -> #certificate{asn1_certificates = certs_to_list(ASN1Certs)}; -dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, +dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, ?UINT16(GLen), G:GLen/binary, ?UINT16(YLen), Y:YLen/binary, ?UINT16(0)>>) -> %% May happen if key_algorithm is dh_anon #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y}, - signed_params = <<>>}; -dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, + signed_params = <<>>, hashsign = {null, anon}}; +dec_hs({Major, Minor}, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, + ?UINT16(GLen), G:GLen/binary, + ?UINT16(YLen), Y:YLen/binary, + ?BYTE(HashAlgo), ?BYTE(SignAlgo), + ?UINT16(Len), Sig:Len/binary>>) + when Major == 3, Minor >= 3 -> + #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, + dh_y = Y}, + signed_params = Sig, + hashsign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}}; +dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, ?UINT16(GLen), G:GLen/binary, ?UINT16(YLen), Y:YLen/binary, ?UINT16(Len), Sig:Len/binary>>) -> #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y}, - signed_params = Sig}; -dec_hs(?CERTIFICATE_REQUEST, + signed_params = Sig, hashsign = undefined}; +dec_hs({Major, Minor}, ?CERTIFICATE_REQUEST, + <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary, + ?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary, + ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) + when Major == 3, Minor >= 3 -> + HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} || + <<?BYTE(Hash), ?BYTE(Sign)>> <= HashSigns], + #certificate_request{certificate_types = CertTypes, + hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos}, + certificate_authorities = CertAuths}; +dec_hs(_Version, ?CERTIFICATE_REQUEST, <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary, ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) -> #certificate_request{certificate_types = CertTypes, certificate_authorities = CertAuths}; -dec_hs(?SERVER_HELLO_DONE, <<>>) -> +dec_hs(_Version, ?SERVER_HELLO_DONE, <<>>) -> #server_hello_done{}; -dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>)-> +dec_hs({Major, Minor}, ?CERTIFICATE_VERIFY,<<HashSign:2/binary, ?UINT16(SignLen), Signature:SignLen/binary>>) + when Major == 3, Minor >= 3 -> + #certificate_verify{hashsign_algorithm = hashsign_dec(HashSign), signature = Signature}; +dec_hs(_Version, ?CERTIFICATE_VERIFY,<<?UINT16(SignLen), Signature:SignLen/binary>>)-> #certificate_verify{signature = Signature}; -dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS) -> +dec_hs(_Version, ?CLIENT_KEY_EXCHANGE, PKEPMS) -> #client_key_exchange{exchange_keys = PKEPMS}; -dec_hs(?FINISHED, VerifyData) -> +dec_hs(_Version, ?FINISHED, VerifyData) -> #finished{verify_data = VerifyData}; -dec_hs(_, _) -> +dec_hs(_, _, _) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)). dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) -> @@ -930,6 +971,15 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar dec_hello_extensions(Rest, [{renegotiation_info, #renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]); +dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Acc) -> + SignAlgoListLen = Len - 2, + <<?UINT16(SignAlgoListLen), SignAlgoList/binary>> = ExtData, + HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} || + <<?BYTE(Hash), ?BYTE(Sign)>> <= SignAlgoList], + dec_hello_extensions(Rest, [{hash_signs, + #hash_sign_algos{hash_sign_algos = HashSignAlgos}} | Acc]); + %% Ignore data following the ClientHello (i.e., %% extensions) if not understood. dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) -> @@ -971,14 +1021,19 @@ enc_hs(#client_hello{client_version = {Major, Minor}, session_id = SessionID, cipher_suites = CipherSuites, compression_methods = CompMethods, - renegotiation_info = RenegotiationInfo}, _Version) -> + renegotiation_info = RenegotiationInfo, + hash_signs = HashSigns}, _Version) -> SIDLength = byte_size(SessionID), BinCompMethods = list_to_binary(CompMethods), CmLength = byte_size(BinCompMethods), BinCipherSuites = list_to_binary(CipherSuites), CsLength = byte_size(BinCipherSuites), - Extensions = hello_extensions(RenegotiationInfo), - ExtensionsBin = enc_hello_extensions(Extensions), + Extensions0 = hello_extensions(RenegotiationInfo), + Extensions1 = if + Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns); + true -> Extensions0 + end, + ExtensionsBin = enc_hello_extensions(Extensions1), {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SIDLength), SessionID/binary, ?UINT16(CsLength), BinCipherSuites/binary, @@ -1002,15 +1057,30 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) -> {?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>}; enc_hs(#server_key_exchange{params = #server_dh_params{ dh_p = P, dh_g = G, dh_y = Y}, - signed_params = SignedParams}, _Version) -> + signed_params = SignedParams, hashsign = HashSign}, Version) -> PLen = byte_size(P), GLen = byte_size(G), YLen = byte_size(Y), - SignedLen = byte_size(SignedParams), + Signature = enc_sign(HashSign, SignedParams, Version), {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary, - ?UINT16(SignedLen), SignedParams/binary>> + Signature/binary>> + }; +enc_hs(#certificate_request{certificate_types = CertTypes, + hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos}, + certificate_authorities = CertAuths}, + {Major, Minor}) + when Major == 3, Minor >= 3 -> + HashSigns= << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> || + {Hash, Sign} <- HashSignAlgos >>, + CertTypesLen = byte_size(CertTypes), + HashSignsLen = byte_size(HashSigns), + CertAuthsLen = byte_size(CertAuths), + {?CERTIFICATE_REQUEST, + <<?BYTE(CertTypesLen), CertTypes/binary, + ?UINT16(HashSignsLen), HashSigns/binary, + ?UINT16(CertAuthsLen), CertAuths/binary>> }; enc_hs(#certificate_request{certificate_types = CertTypes, certificate_authorities = CertAuths}, @@ -1025,8 +1095,8 @@ enc_hs(#server_hello_done{}, _Version) -> {?SERVER_HELLO_DONE, <<>>}; enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) -> {?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)}; -enc_hs(#certificate_verify{signature = BinSig}, _) -> - EncSig = enc_bin_sig(BinSig), +enc_hs(#certificate_verify{signature = BinSig, hashsign_algorithm = HashSign}, Version) -> + EncSig = enc_sign(HashSign, BinSig, Version), {?CERTIFICATE_VERIFY, EncSig}; enc_hs(#finished{verify_data = VerifyData}, _Version) -> {?FINISHED, VerifyData}. @@ -1040,14 +1110,23 @@ enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) -> Len = byte_size(DHPublic), <<?UINT16(Len), DHPublic/binary>>. -enc_bin_sig(BinSig) -> - Size = byte_size(BinSig), - <<?UINT16(Size), BinSig/binary>>. +enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor}) + when Major == 3, Minor >= 3-> + SignLen = byte_size(Signature), + HashSign = hashsign_enc(HashAlg, SignAlg), + <<HashSign/binary, ?UINT16(SignLen), Signature/binary>>; +enc_sign(_HashSign, Sign, _Version) -> + SignLen = byte_size(Sign), + <<?UINT16(SignLen), Sign/binary>>. -%% Renegotiation info, only current extension +hello_extensions(undefined) -> + []; +%% Renegotiation info hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) -> []; hello_extensions(#renegotiation_info{} = Info) -> + [Info]; +hello_extensions(#hash_sign_algos{} = Info) -> [Info]. enc_hello_extensions(Extensions) -> @@ -1065,7 +1144,14 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = I enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) -> InfoLen = byte_size(Info), Len = InfoLen +1, - enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>). + enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>); + +enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> + SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> || + {Hash, Sign} <- HashSignAlgos >>, + ListLen = byte_size(SignAlgoList), + Len = ListLen + 2, + enc_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>). from_3bytes(Bin3) -> @@ -1093,6 +1179,14 @@ certificate_types({KeyExchange, _, _, _}) certificate_types(_) -> <<?BYTE(?RSA_SIGN)>>. +hashsign_dec(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) -> + {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}. + +hashsign_enc(HashAlgo, SignAlgo) -> + Hash = ssl_cipher:hash_algorithm(HashAlgo), + Sign = ssl_cipher:sign_algorithm(SignAlgo), + <<?BYTE(Hash), ?BYTE(Sign)>>. + certificate_authorities(CertDbHandle, CertDbRef) -> Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef), Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) -> @@ -1111,43 +1205,43 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) -> [Cert | Acc]; (_, Acc) -> Acc - end, + end, ssl_certificate_db:foldl(ConnectionCerts, [], CertDbHandle). -digitally_signed(Hash, #'RSAPrivateKey'{} = Key) -> + +digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 -> + public_key:sign({digest, Hash}, HashAlgo, Key); +digitally_signed(_Version, Hash, _HashAlgo, #'DSAPrivateKey'{} = Key) -> + public_key:sign({digest, Hash}, sha, Key); +digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) -> public_key:encrypt_private(Hash, Key, - [{rsa_pad, rsa_pkcs1_padding}]); -digitally_signed(Hash, #'DSAPrivateKey'{} = Key) -> - public_key:sign(Hash, none, Key). - -calc_master_secret({3,0}, PremasterSecret, ClientRandom, ServerRandom) -> + [{rsa_pad, rsa_pkcs1_padding}]). + +calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) -> ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom); -calc_master_secret({3,N},PremasterSecret, ClientRandom, ServerRandom) - when N == 1; N == 2 -> - ssl_tls1:master_secret(PremasterSecret, ClientRandom, ServerRandom). +calc_master_secret({3,_}, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) -> + ssl_tls1:master_secret(PrfAlgo, PremasterSecret, ClientRandom, ServerRandom). -setup_keys({3,0}, MasterSecret, +setup_keys({3,0}, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) -> - ssl_ssl3:setup_keys(MasterSecret, ServerRandom, + ssl_ssl3:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KML, EKML, IVS); -setup_keys({3,1}, MasterSecret, +setup_keys({3,N}, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) -> - ssl_tls1:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, + ssl_tls1:setup_keys(N, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, IVS). -calc_finished({3, 0}, Role, MasterSecret, Hashes) -> - ssl_ssl3:finished(Role, MasterSecret, Hashes); -calc_finished({3, N}, Role, MasterSecret, Hashes) - when N == 1; N == 2 -> - ssl_tls1:finished(Role, MasterSecret, Hashes). +calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) -> + ssl_ssl3:finished(Role, MasterSecret, lists:reverse(Handshake)); +calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) -> + ssl_tls1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)). -calc_certificate_verify({3, 0}, MasterSecret, Algorithm, Hashes) -> - ssl_ssl3:certificate_verify(Algorithm, MasterSecret, Hashes); -calc_certificate_verify({3, N}, _, Algorithm, Hashes) - when N == 1; N == 2 -> - ssl_tls1:certificate_verify(Algorithm, Hashes). +calc_certificate_verify({3, 0}, HashAlgo, MasterSecret, Handshake) -> + ssl_ssl3:certificate_verify(HashAlgo, MasterSecret, lists:reverse(Handshake)); +calc_certificate_verify({3, N}, HashAlgo, _MasterSecret, Handshake) -> + ssl_tls1:certificate_verify(HashAlgo, N, lists:reverse(Handshake)). key_exchange_alg(rsa) -> ?KEY_EXCHANGE_RSA; @@ -1167,7 +1261,29 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) -> {unknown, {SslState, UserState}} end. -alg_oid(#'RSAPrivateKey'{}) -> - ?'rsaEncryption'; -alg_oid(#'DSAPrivateKey'{}) -> - ?'id-dsa'. +certificate_verify_rsa(Hashes, sha, Signature, PublicKey, {Major, Minor}) + when Major == 3, Minor >= 3 -> + public_key:verify({digest, Hashes}, sha, Signature, PublicKey); +certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, {Major, Minor}) + when Major == 3, Minor >= 3 -> + public_key:verify({digest, Hashes}, HashAlgo, Signature, PublicKey); +certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) -> + case public_key:decrypt_public(Signature, PublicKey, + [{rsa_pad, rsa_pkcs1_padding}]) of + Hashes -> true; + _ -> false + end. + +-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}). +-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}). + +-define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_RSA(MD)). + +default_hash_signs() -> + #hash_sign_algos{hash_sign_algos = + [?TLSEXT_SIGALG(sha512), + ?TLSEXT_SIGALG(sha384), + ?TLSEXT_SIGALG(sha256), + ?TLSEXT_SIGALG(sha), + ?TLSEXT_SIGALG_DSA(sha), + ?TLSEXT_SIGALG_RSA(md5)]}. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index fb0ebac7d1..cc17dc2975 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -31,6 +31,13 @@ -type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'. -type public_key_params() :: #'Dss-Parms'{} | term(). -type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}. +-type tls_handshake_history() :: {[binary()], [binary()]}. + +%% Signature algorithms +-define(ANON, 0). +-define(RSA, 1). +-define(DSA, 2). +-define(ECDSA, 3). -record(session, { session_id, @@ -89,7 +96,8 @@ session_id, % opaque SessionID<0..32> cipher_suites, % cipher_suites<2..2^16-1> compression_methods, % compression_methods<1..2^8-1>, - renegotiation_info + renegotiation_info, + hash_signs % supported combinations of hashes/signature algos }). -record(server_hello, { @@ -98,7 +106,8 @@ session_id, % opaque SessionID<0..32> cipher_suite, % cipher_suites compression_method, % compression_method - renegotiation_info + renegotiation_info, + hash_signs % supported combinations of hashes/signature algos }). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -129,7 +138,8 @@ -record(server_key_exchange, { params, %% #server_rsa_params{} | #server_dh_params{} - signed_params %% #signature{} + signed_params, %% #signature{} + hashsign %% term(atom(), atom()) }). %% enum { anonymous, rsa, dsa } SignatureAlgorithm; @@ -159,6 +169,7 @@ -record(certificate_request, { certificate_types, %ClientCertificateType <1..2^8-1> + hashsign_algorithms, %%SignatureAndHashAlgorithm <2^16-1>; certificate_authorities %DistinguishedName <0..2^16-1> }). @@ -193,6 +204,7 @@ %%% Certificate verify - RFC 4346 section 7.4.8 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -record(certificate_verify, { + hashsign_algorithm, signature % binary() }). @@ -213,6 +225,15 @@ renegotiated_connection }). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Signature Algorithms RFC 5746 section 7.4.1.4.1. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-define(SIGNATURE_ALGORITHMS_EXT, 13). + +-record(hash_sign_algos, { + hash_sign_algos + }). + -endif. % -ifdef(ssl_handshake). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 18cfcdcd68..b8f2ae3b51 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -34,7 +34,7 @@ -type host() :: inet:ip_address() | inet:hostname(). -type session_id() :: 0 | binary(). -type tls_version() :: {integer(), integer()}. --type tls_atom_version() :: sslv3 | tlsv1. +-type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'. -type certdb_ref() :: reference(). -type db_handle() :: term(). -type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon. @@ -69,11 +69,11 @@ -define(TRUE, 0). -define(FALSE, 1). --define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). % TODO: This is temporary -%-define(DEFAULT_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]). +-define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). %% Add 'tlsv1.1' in R16 +-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]). -record(ssl_options, { - versions, % 'tlsv1.1' | tlsv1 | sslv3 + versions, % 'tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3 verify, % verify_none | verify_peer verify_fun, % fun(CertVerifyErrors) -> boolean() fail_if_no_peer_cert, % boolean() diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 6389ff03f5..af2bfa394d 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -30,9 +30,9 @@ -export([start_link/1, start_link_dist/1, connection_init/2, cache_pem_file/2, lookup_trusted_cert/4, - client_session_id/4, server_session_id/4, + new_session_id/1, clean_cert_db/2, register_session/2, register_session/3, invalidate_session/2, - invalidate_session/3]). + invalidate_session/3, clear_pem_cache/0]). % Spawn export -export([init_session_validator/1]). @@ -56,9 +56,12 @@ -define('24H_in_msec', 8640000). -define('24H_in_sec', 8640). +-define(GEN_UNIQUE_ID_MAX_TRIES, 10). -define(SESSION_VALIDATION_INTERVAL, 60000). --define(CERTIFICATE_CACHE_CLEANUP, 30000). +-define(CLEAR_PEM_CACHE, 120000). -define(CLEAN_SESSION_DB, 60000). +-define(CLEAN_CERT_DB, 500). +-define(NOT_TO_BIG, 10). %%==================================================================== %% API @@ -82,26 +85,46 @@ start_link_dist(Opts) -> gen_server:start_link({local, ssl_manager_dist}, ?MODULE, [ssl_manager_dist, Opts], []). %%-------------------------------------------------------------------- --spec connection_init(string()| {der, list()}, client | server) -> - {ok, certdb_ref(), db_handle(), db_handle()}. +-spec connection_init(binary()| {der, list()}, client | server) -> + {ok, certdb_ref(), db_handle(), db_handle(), db_handle(), db_handle()}. %% %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- +connection_init({der, _} = Trustedcerts, Role) -> + call({connection_init, Trustedcerts, Role}); + +connection_init(<<>> = Trustedcerts, Role) -> + call({connection_init, Trustedcerts, Role}); + connection_init(Trustedcerts, Role) -> call({connection_init, Trustedcerts, Role}). + %%-------------------------------------------------------------------- --spec cache_pem_file(string(), term()) -> {ok, term()} | {error, reason()}. +-spec cache_pem_file(binary(), term()) -> {ok, term()} | {error, reason()}. %% %% Description: Cach a pem file and return its content. %%-------------------------------------------------------------------- cache_pem_file(File, DbHandle) -> - try file:read_file_info(File) of - {ok, #file_info{mtime = LastWrite}} -> - cache_pem_file(File, LastWrite, DbHandle) - catch - _:Reason -> - {error, Reason} + MD5 = crypto:md5(File), + case ssl_certificate_db:lookup_cached_pem(DbHandle, MD5) of + [{Content,_}] -> + {ok, Content}; + [Content] -> + {ok, Content}; + undefined -> + call({cache_pem, {MD5, File}}) end. + +%%-------------------------------------------------------------------- +-spec clear_pem_cache() -> ok. +%% +%% Description: Clear the PEM cache +%%-------------------------------------------------------------------- +clear_pem_cache() -> + %% Not supported for distribution at the moement, should it be? + put(ssl_manager, ssl_manager), + call(unconditionally_clear_pem_cache). + %%-------------------------------------------------------------------- -spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) -> undefined | @@ -114,22 +137,15 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> ssl_certificate_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer). %%-------------------------------------------------------------------- --spec client_session_id(host(), inet:port_number(), #ssl_options{}, - der_cert() | undefined) -> session_id(). +-spec new_session_id(integer()) -> session_id(). %% -%% Description: Select a session id for the client. +%% Description: Creates a session id for the server. %%-------------------------------------------------------------------- -client_session_id(Host, Port, SslOpts, OwnCert) -> - call({client_session_id, Host, Port, SslOpts, OwnCert}). +new_session_id(Port) -> + call({new_session_id, Port}). -%%-------------------------------------------------------------------- --spec server_session_id(host(), inet:port_number(), #ssl_options{}, - der_cert()) -> session_id(). -%% -%% Description: Select a session id for the server. -%%-------------------------------------------------------------------- -server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) -> - call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}). +clean_cert_db(Ref, File) -> + erlang:send_after(?CLEAN_CERT_DB, self(), {clean_cert_db, Ref, File}). %%-------------------------------------------------------------------- -spec register_session(inet:port_number(), #session{}) -> ok. @@ -177,6 +193,7 @@ init([Name, Opts]) -> SessionCache = CacheCb:init(proplists:get_value(session_cb_init_args, Opts, [])), Timer = erlang:send_after(SessionLifeTime * 1000, self(), validate_sessions), + erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache), {ok, #state{certificate_db = CertDb, session_cache = SessionCache, session_cache_cb = CacheCb, @@ -194,55 +211,44 @@ init([Name, Opts]) -> %% %% Description: Handling call messages %%-------------------------------------------------------------------- -handle_call({{connection_init, "", _Role}, Pid}, _From, - #state{certificate_db = [CertDb |_], +handle_call({{connection_init, <<>>, _Role}, _Pid}, _From, + #state{certificate_db = [CertDb, FileRefDb, PemChace], session_cache = Cache} = State) -> - erlang:monitor(process, Pid), - Result = {ok, make_ref(),CertDb, Cache}, + Result = {ok, make_ref(),CertDb, FileRefDb, PemChace, Cache}, {reply, Result, State}; handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From, - #state{certificate_db = [CertDb|_] =Db, + #state{certificate_db = [CertDb, FileRefDb, PemChace] = Db, session_cache = Cache} = State) -> - erlang:monitor(process, Pid), Result = try {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db), - {ok, Ref, CertDb, Cache} + {ok, Ref, CertDb, FileRefDb, PemChace, Cache} catch _:Reason -> {error, Reason} end, {reply, Result, State}; -handle_call({{client_session_id, Host, Port, SslOpts, OwnCert}, _}, _, - #state{session_cache = Cache, - session_cache_cb = CacheCb} = State) -> - Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), - {reply, Id, State}; - -handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}, _}, +handle_call({{new_session_id,Port}, _}, _, #state{session_cache_cb = CacheCb, - session_cache = Cache, - session_lifetime = LifeTime} = State) -> - Id = ssl_session:id(Port, SuggestedSessionId, SslOpts, - Cache, CacheCb, LifeTime, OwnCert), + session_cache = Cache} = State) -> + Id = new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb), {reply, Id, State}; -handle_call({{cache_pem, File, LastWrite}, Pid}, _, + +handle_call({{cache_pem, File}, _Pid}, _, #state{certificate_db = Db} = State) -> - try ssl_certificate_db:cache_pem_file(Pid, File, LastWrite, Db) of + try ssl_certificate_db:cache_pem_file(File, Db) of Result -> {reply, Result, State} catch _:Reason -> {reply, {error, Reason}, State} end; -handle_call({{recache_pem, File, LastWrite}, Pid}, From, - #state{certificate_db = Db} = State) -> - ssl_certificate_db:uncache_pem_file(File, Db), - cast({recache_pem, File, LastWrite, Pid, From}), - {noreply, State}. +handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_,PemChace]} = State) -> + ssl_certificate_db:clear(PemChace), + {reply, ok, State}. %%-------------------------------------------------------------------- -spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. @@ -278,22 +284,7 @@ handle_cast({invalidate_session, Host, Port, handle_cast({invalidate_session, Port, #session{session_id = ID} = Session}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> - invalidate_session(Cache, CacheCb, {Port, ID}, Session, State); - -handle_cast({recache_pem, File, LastWrite, Pid, From}, - #state{certificate_db = [_, FileToRefDb, _]} = State0) -> - case ssl_certificate_db:lookup(File, FileToRefDb) of - undefined -> - {reply, Msg, State} = - handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0), - gen_server:reply(From, Msg), - {noreply, State}; - _ -> %% Send message to self letting cleanup messages be handled - %% first so that no reference to the old version of file - %% exists when we cache the new one. - cast({recache_pem, File, LastWrite, Pid, From}), - {noreply, State0} - end. + invalidate_session(Cache, CacheCb, {Port, ID}, Session, State). %%-------------------------------------------------------------------- -spec handle_info(msg(), #state{}) -> {noreply, #state{}}. @@ -318,23 +309,38 @@ handle_info({delayed_clean_session, Key}, #state{session_cache = Cache, CacheCb:delete(Cache, Key), {noreply, State}; -handle_info({'EXIT', _, _}, State) -> - %% Session validator died!! Do we need to take any action? - %% maybe error log +handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace]} = State) -> + case ssl_certificate_db:db_size(PemChace) of + N when N < ?NOT_TO_BIG -> + ok; + _ -> + ssl_certificate_db:clear(PemChace) + end, + erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache), {noreply, State}; -handle_info({'DOWN', _Ref, _Type, _Pid, ecacertfile}, State) -> - {noreply, State}; -handle_info({'DOWN', _Ref, _Type, Pid, shutdown}, State) -> - handle_info({remove_trusted_certs, Pid}, State); -handle_info({'DOWN', _Ref, _Type, Pid, _Reason}, State) -> - erlang:send_after(?CERTIFICATE_CACHE_CLEANUP, self(), - {remove_trusted_certs, Pid}), +handle_info({clean_cert_db, Ref, File}, + #state{certificate_db = [CertDb,RefDb, PemCache]} = State) -> + case ssl_certificate_db:ref_count(Ref, RefDb, 0) of + 0 -> + MD5 = crypto:md5(File), + case ssl_certificate_db:lookup_cached_pem(PemCache, MD5) of + [{Content, Ref}] -> + ssl_certificate_db:insert(MD5, Content, PemCache); + undefined -> + ok + end, + ssl_certificate_db:remove(Ref, RefDb), + ssl_certificate_db:remove_trusted_certs(Ref, CertDb); + _ -> + ok + end, {noreply, State}; -handle_info({remove_trusted_certs, Pid}, - #state{certificate_db = Db} = State) -> - ssl_certificate_db:remove_trusted_certs(Pid, Db), + +handle_info({'EXIT', _, _}, State) -> + %% Session validator died!! Do we need to take any action? + %% maybe error log {noreply, State}; handle_info(_Info, State) -> @@ -406,19 +412,6 @@ session_validation({{Port, _}, Session}, LifeTime) -> validate_session(Port, Session, LifeTime), LifeTime. -cache_pem_file(File, LastWrite, DbHandle) -> - case ssl_certificate_db:lookup_cached_certs(DbHandle,File) of - [{_, {Mtime, Content}}] -> - case LastWrite of - Mtime -> - {ok, Content}; - _ -> - call({recache_pem, File, LastWrite}) - end; - [] -> - call({cache_pem, File, LastWrite}) - end. - delay_time() -> case application:get_env(ssl, session_delay_cleanup_time) of {ok, Time} when is_integer(Time) -> @@ -448,3 +441,28 @@ last_delay_timer({{_,_},_}, TRef, {LastServer, _}) -> {LastServer, TRef}; last_delay_timer({_,_}, TRef, {_, LastClient}) -> {TRef, LastClient}. + +%% If we can not generate a not allready in use session ID in +%% ?GEN_UNIQUE_ID_MAX_TRIES we make the new session uncacheable The +%% value of ?GEN_UNIQUE_ID_MAX_TRIES is stolen from open SSL which +%% states : "If we can not find a session id in +%% ?GEN_UNIQUE_ID_MAX_TRIES either the RAND code is broken or someone +%% is trying to open roughly very close to 2^128 (or 2^256) SSL +%% sessions to our server" +new_id(_, 0, _, _) -> + <<>>; +new_id(Port, Tries, Cache, CacheCb) -> + Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES), + case CacheCb:lookup(Cache, {Port, Id}) of + undefined -> + Now = calendar:datetime_to_gregorian_seconds({date(), time()}), + %% New sessions can not be set to resumable + %% until handshake is compleate and the + %% other session values are set. + CacheCb:update(Cache, {Port, Id}, #session{session_id = Id, + is_resumable = false, + time_stamp = Now}), + Id; + _ -> + new_id(Port, Tries - 1, Cache, CacheCb) + end. diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 830026c825..8e93ce4634 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -383,6 +383,8 @@ get_tls_records_aux(Data, Acc) -> %% Description: Creates a protocol version record from a version atom %% or vice versa. %%-------------------------------------------------------------------- +protocol_version('tlsv1.2') -> + {3, 3}; protocol_version('tlsv1.1') -> {3, 2}; protocol_version(tlsv1) -> @@ -391,6 +393,8 @@ protocol_version(sslv3) -> {3, 0}; protocol_version(sslv2) -> %% Backwards compatibility {2, 0}; +protocol_version({3, 3}) -> + 'tlsv1.2'; protocol_version({3, 2}) -> 'tlsv1.1'; protocol_version({3, 1}) -> @@ -445,9 +449,9 @@ supported_protocol_versions() -> end, case application:get_env(ssl, protocol_version) of undefined -> - lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS); + lists:map(Fun, supported_protocol_versions([])); {ok, []} -> - lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS); + lists:map(Fun, supported_protocol_versions([])); {ok, Vsns} when is_list(Vsns) -> Versions = lists:filter(fun is_acceptable_version/1, lists:map(Fun, Vsns)), supported_protocol_versions(Versions); @@ -457,7 +461,16 @@ supported_protocol_versions() -> end. supported_protocol_versions([]) -> - ?DEFAULT_SUPPORTED_VERSIONS; + Vsns = case sufficient_tlsv1_2_crypto_support() of + true -> + %%?ALL_SUPPORTED_VERSIONS; %% Add TlS-1.2 as default in R16 + ?DEFAULT_SUPPORTED_VERSIONS; + false -> + ?DEFAULT_SUPPORTED_VERSIONS + end, + application:set_env(ssl, protocol_version, Vsns), + Vsns; + supported_protocol_versions([_|_] = Vsns) -> Vsns. @@ -561,14 +574,14 @@ highest_protocol_version() -> initial_connection_state(ConnectionEnd) -> #connection_state{security_parameters = - initial_security_params(ConnectionEnd), + initial_security_params(ConnectionEnd), sequence_number = 0 }. initial_security_params(ConnectionEnd) -> SecParams = #security_parameters{connection_end = ConnectionEnd, compression_algorithm = ?NULL}, - ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL, + ssl_cipher:security_parameters(highest_protocol_version(), ?TLS_NULL_WITH_NULL_NULL, SecParams). empty_connection_state(ConnectionEnd) -> @@ -633,7 +646,7 @@ cipher(Type, Version, Fragment, CS0) -> BCA} }} = hash_and_bump_seqno(CS0, Type, Version, Length, Fragment), - {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment), + {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment, Version), CS2 = CS1#connection_state{cipher_state=CipherS1}, {Ciphered, CS2}. @@ -687,6 +700,17 @@ mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type, mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) -> ssl_ssl3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment); mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) - when N =:= 1; N =:= 2 -> + when N =:= 1; N =:= 2; N =:= 3 -> ssl_tls1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version, Length, Fragment). + +sufficient_tlsv1_2_crypto_support() -> + Data = "Sampl", + Data2 = "e #1", + Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39, + 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>, + try + crypto:sha256_mac(Key, lists:flatten([Data, Data2])), + true + catch _:_ -> false + end. diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index 282d642138..f73da92a52 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -47,6 +47,7 @@ key_material_length, % unit 8 expanded_key_material_length, % unit 8 mac_algorithm, % unit 8 + prf_algorithm, % unit 8 hash_size, % unit 8 compression_algorithm, % unit 8 master_secret, % opaque 48 @@ -97,10 +98,15 @@ %-define(TRUE, 0). %% Already defined by ssl_internal.hrl %-define(FALSE, 1). %% Already defined by ssl_internal.hrl -%% MACAlgorithm +%% MAC and PRF Algorithms %-define(NULL, 0). %% Already defined by ssl_internal.hrl -define(MD5, 1). -define(SHA, 2). +-define(MD5SHA, 4711). %% Not defined in protocol used to represent old prf +-define(SHA224, 3). +-define(SHA256, 4). +-define(SHA384, 5). +-define(SHA512, 6). %% CompressionMethod % -define(NULL, 0). %% Already defined by ssl_internal.hrl @@ -176,7 +182,8 @@ content, % opaque content[TLSCompressed.length]; mac, % opaque MAC[CipherSpec.hash_size]; padding, % unit 8 padding[GenericBlockCipher.padding_length]; - padding_length % uint8 padding_length; + padding_length, % uint8 padding_length; + next_iv % opaque IV[SecurityParameters.record_iv_length]; }). -endif. % -ifdef(ssl_record). diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index df5d7e0146..2ad422fc03 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -28,9 +28,9 @@ -include("ssl_internal.hrl"). %% Internal application API --export([is_new/2, id/4, id/7, valid_session/2]). +-export([is_new/2, client_id/4, server_id/6, valid_session/2]). --define(GEN_UNIQUE_ID_MAX_TRIES, 10). +-define('24H_in_sec', 8640). -type seconds() :: integer(). @@ -48,13 +48,13 @@ is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- --spec id({host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(), +-spec client_id({host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(), undefined | binary()) -> binary(). %% -%% Description: Should be called by the client side to get an id +%% Description: Should be called by the client side to get an id %% for the client hello message. %%-------------------------------------------------------------------- -id(ClientInfo, Cache, CacheCb, OwnCert) -> +client_id(ClientInfo, Cache, CacheCb, OwnCert) -> case select_session(ClientInfo, Cache, CacheCb, OwnCert) of no_session -> <<>>; @@ -62,27 +62,6 @@ id(ClientInfo, Cache, CacheCb, OwnCert) -> SessionId end. -%%-------------------------------------------------------------------- --spec id(inet:port_number(), binary(), #ssl_options{}, db_handle(), - atom(), seconds(), binary()) -> binary(). -%% -%% Description: Should be called by the server side to get an id -%% for the server hello message. -%%-------------------------------------------------------------------- -id(Port, <<>>, _, Cache, CacheCb, _, _) -> - new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb); - -id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled, - reuse_session = ReuseFun}, - Cache, CacheCb, SecondLifeTime, OwnCert) -> - case is_resumable(SuggestedSessionId, Port, ReuseEnabled, - ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) of - true -> - SuggestedSessionId; - false -> - new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb) - end. -%%-------------------------------------------------------------------- -spec valid_session(#session{}, seconds()) -> boolean(). %% %% Description: Check that the session has not expired @@ -91,57 +70,51 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) -> Now = calendar:datetime_to_gregorian_seconds({date(), time()}), Now - TimeStamp < LifeTime. +server_id(Port, <<>>, _SslOpts, _Cert, _, _) -> + {ssl_manager:new_session_id(Port), undefined}; +server_id(Port, SuggestedId, + #ssl_options{reuse_sessions = ReuseEnabled, + reuse_session = ReuseFun}, + Cert, Cache, CacheCb) -> + LifeTime = case application:get_env(ssl, session_lifetime) of + {ok, Time} when is_integer(Time) -> Time; + _ -> ?'24H_in_sec' + end, + case is_resumable(SuggestedId, Port, ReuseEnabled,ReuseFun, + Cache, CacheCb, LifeTime, Cert) + of + {true, Resumed} -> + {SuggestedId, Resumed}; + {false, undefined} -> + {ssl_manager:new_session_id(Port), undefined} + end. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +select_session({_, _, #ssl_options{reuse_sessions=false}}, _Cache, _CacheCb, _OwnCert) -> + no_session; select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) -> Sessions = CacheCb:select_session(Cache, {HostIP, Port}), select_session(Sessions, SslOpts, OwnCert). select_session([], _, _) -> no_session; - -select_session(Sessions, #ssl_options{ciphers = Ciphers, - reuse_sessions = ReuseSession}, OwnCert) -> - IsResumable = - fun(Session) -> - ReuseSession andalso resumable(Session#session.is_resumable) andalso - lists:member(Session#session.cipher_suite, Ciphers) - andalso (OwnCert == Session#session.own_certificate) +select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) -> + IsNotResumable = + fun([_Id, Session]) -> + not (resumable(Session#session.is_resumable) andalso + lists:member(Session#session.cipher_suite, Ciphers) + andalso (OwnCert == Session#session.own_certificate)) end, - case [Id || [Id, Session] <- Sessions, IsResumable(Session)] of - [] -> - no_session; - List -> - hd(List) - end. - -%% If we can not generate a not allready in use session ID in -%% ?GEN_UNIQUE_ID_MAX_TRIES we make the new session uncacheable The -%% value of ?GEN_UNIQUE_ID_MAX_TRIES is stolen from open SSL which -%% states : "If we can not find a session id in -%% ?GEN_UNIQUE_ID_MAX_TRIES either the RAND code is broken or someone -%% is trying to open roughly very close to 2^128 (or 2^256) SSL -%% sessions to our server" -new_id(_, 0, _, _) -> - <<>>; -new_id(Port, Tries, Cache, CacheCb) -> - Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES), - case CacheCb:lookup(Cache, {Port, Id}) of - undefined -> - Now = calendar:datetime_to_gregorian_seconds({date(), time()}), - %% New sessions can not be set to resumable - %% until handshake is compleate and the - %% other session values are set. - CacheCb:update(Cache, {Port, Id}, #session{session_id = Id, - is_resumable = false, - time_stamp = Now}), - Id; - _ -> - new_id(Port, Tries - 1, Cache, CacheCb) + case lists:dropwhile(IsNotResumable, Sessions) of + [] -> no_session; + [[Id, _]|_] -> Id end. -is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache, +is_resumable(_, _, false, _, _, _, _, _) -> + {false, undefined}; +is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) -> case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of #session{cipher_suite = CipherSuite, @@ -149,14 +122,17 @@ is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache, compression_method = Compression, is_resumable = IsResumable, peer_certificate = PeerCert} = Session -> - ReuseEnabled - andalso resumable(IsResumable) + case resumable(IsResumable) andalso (OwnCert == SessionOwnCert) - andalso valid_session(Session, SecondLifeTime) - andalso ReuseFun(SuggestedSessionId, PeerCert, - Compression, CipherSuite); + andalso valid_session(Session, SecondLifeTime) + andalso ReuseFun(SuggestedSessionId, PeerCert, + Compression, CipherSuite) + of + true -> {true, Session}; + false -> {false, undefined} + end; undefined -> - false + {false, undefined} end. resumable(new) -> diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index f9bbf905e1..5c6ee3c54c 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -32,7 +32,7 @@ %% Description: Return table reference. Called by ssl_manager process. %%-------------------------------------------------------------------- init(_) -> - ets:new(cache_name(), [set, protected]). + ets:new(cache_name(), [ordered_set, protected]). %%-------------------------------------------------------------------- %% Description: Handles cache table at termination of ssl manager. diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index f2926b2d2f..a11c5b8c0c 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -54,9 +54,9 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) -> Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48), Block. --spec finished(client | server, binary(), {binary(), binary()}) -> binary(). +-spec finished(client | server, binary(), [binary()]) -> binary(). -finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> +finished(Role, MasterSecret, Handshake) -> %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished %% struct { %% opaque md5_hash[16]; @@ -70,13 +70,13 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% SHA(handshake_messages + Sender + %% master_secret + pad1)); Sender = get_sender(Role), - MD5 = handshake_hash(?MD5, MasterSecret, Sender, MD5Hash), - SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash), + MD5 = handshake_hash(?MD5, MasterSecret, Sender, Handshake), + SHA = handshake_hash(?SHA, MasterSecret, Sender, Handshake), <<MD5/binary, SHA/binary>>. --spec certificate_verify(OID::tuple(), binary(), {binary(), binary()}) -> binary(). +-spec certificate_verify(md5sha | sha, binary(), [binary()]) -> binary(). -certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) -> +certificate_verify(md5sha, MasterSecret, Handshake) -> %% md5_hash %% MD5(master_secret + pad_2 + %% MD5(handshake_messages + master_secret + pad_1)); @@ -84,15 +84,16 @@ certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) -> %% SHA(master_secret + pad_2 + %% SHA(handshake_messages + master_secret + pad_1)); - MD5 = handshake_hash(?MD5, MasterSecret, undefined, MD5Hash), - SHA = handshake_hash(?SHA, MasterSecret, undefined, SHAHash), + MD5 = handshake_hash(?MD5, MasterSecret, undefined, Handshake), + SHA = handshake_hash(?SHA, MasterSecret, undefined, Handshake), <<MD5/binary, SHA/binary>>; -certificate_verify(?'id-dsa', MasterSecret, {_, SHAHash}) -> +certificate_verify(sha, MasterSecret, Handshake) -> %% sha_hash %% SHA(master_secret + pad_2 + %% SHA(handshake_messages + master_secret + pad_1)); - handshake_hash(?SHA, MasterSecret, undefined, SHAHash). + + handshake_hash(?SHA, MasterSecret, undefined, Handshake). -spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary(). @@ -152,28 +153,17 @@ suites() -> %%% Internal functions %%-------------------------------------------------------------------- -hash(?MD5, Data) -> +hash(?MD5, Data) -> crypto:md5(Data); -hash(?SHA, Data) -> +hash(?SHA, Data) -> crypto:sha(Data). -hash_update(?MD5, Context, Data) -> - crypto:md5_update(Context, Data); -hash_update(?SHA, Context, Data) -> - crypto:sha_update(Context, Data). - -hash_final(?MD5, Context) -> - crypto:md5_final(Context); -hash_final(?SHA, Context) -> - crypto:sha_final(Context). - %%pad_1(?NULL) -> %% ""; pad_1(?MD5) -> <<"666666666666666666666666666666666666666666666666">>; pad_1(?SHA) -> <<"6666666666666666666666666666666666666666">>. - %%pad_2(?NULL) -> %% ""; pad_2(?MD5) -> @@ -189,19 +179,11 @@ mac_hash(Method, Secret, Data) -> InnerHash = hash(Method, [Secret, pad_1(Method), Data]), hash(Method, [Secret, pad_2(Method), InnerHash]). -handshake_hash(Method, HandshakeHash, Extra) -> - HSH = hash_update(Method, HandshakeHash, Extra), - hash_final(Method, HSH). - -handshake_hash(Method, MasterSecret, undefined, HandshakeHash) -> - InnerHash = - handshake_hash(Method, HandshakeHash, - [MasterSecret, pad_1(Method)]), +handshake_hash(Method, MasterSecret, undefined, Handshake) -> + InnerHash = hash(Method, [Handshake, MasterSecret, pad_1(Method)]), hash(Method, [MasterSecret, pad_2(Method), InnerHash]); -handshake_hash(Method, MasterSecret, Sender, HandshakeHash) -> - InnerHash = - handshake_hash(Method, HandshakeHash, - [Sender, MasterSecret, pad_1(Method)]), +handshake_hash(Method, MasterSecret, Sender, Handshake) -> + InnerHash = hash(Method, [Handshake, Sender, MasterSecret, pad_1(Method)]), hash(Method, [MasterSecret, pad_2(Method), InnerHash]). get_sender(client) -> "CLNT"; diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index c8aae34892..41dc1bf0dc 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -26,27 +26,29 @@ -include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). --include("ssl_record.hrl"). +-include("ssl_record.hrl"). --export([master_secret/3, finished/3, certificate_verify/2, mac_hash/7, - setup_keys/6, suites/0, prf/4]). +-export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7, + setup_keys/8, suites/1, prf/5]). %%==================================================================== %% Internal application API %%==================================================================== --spec master_secret(binary(), binary(), binary()) -> binary(). +-spec master_secret(integer(), binary(), binary(), binary()) -> binary(). -master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> - %% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret, - %% "master secret", ClientHello.random + - %% ServerHello.random)[0..47]; - prf(PreMasterSecret, <<"master secret">>, +master_secret(PrfAlgo, PreMasterSecret, ClientRandom, ServerRandom) -> + %% RFC 2246 & 4346 && RFC 5246 - 8.1 %% master_secret = PRF(pre_master_secret, + %% "master secret", ClientHello.random + + %% ServerHello.random)[0..47]; + + prf(PrfAlgo, PreMasterSecret, <<"master secret">>, [ClientRandom, ServerRandom], 48). --spec finished(client | server, binary(), {binary(), binary()}) -> binary(). +-spec finished(client | server, integer(), integer(), binary(), [binary()]) -> binary(). -finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> +finished(Role, Version, PrfAlgo, MasterSecret, Handshake) + when Version == 1; Version == 2; PrfAlgo == ?MD5SHA -> %% RFC 2246 & 4346 - 7.4.9. Finished %% struct { %% opaque verify_data[12]; @@ -55,26 +57,39 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% verify_data %% PRF(master_secret, finished_label, MD5(handshake_messages) + %% SHA-1(handshake_messages)) [0..11]; - MD5 = hash_final(?MD5, MD5Hash), - SHA = hash_final(?SHA, SHAHash), - prf(MasterSecret, finished_label(Role), [MD5, SHA], 12). + MD5 = crypto:md5(Handshake), + SHA = crypto:sha(Handshake), + prf(?MD5SHA, MasterSecret, finished_label(Role), [MD5, SHA], 12); + +finished(Role, Version, PrfAlgo, MasterSecret, Handshake) + when Version == 3 -> + %% RFC 5246 - 7.4.9. Finished + %% struct { + %% opaque verify_data[12]; + %% } Finished; + %% + %% verify_data + %% PRF(master_secret, finished_label, Hash(handshake_messages)) [0..11]; + Hash = crypto:hash(mac_algo(PrfAlgo), Handshake), + prf(PrfAlgo, MasterSecret, finished_label(Role), Hash, 12). --spec certificate_verify(OID::tuple(), {binary(), binary()}) -> binary(). +-spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary(). -certificate_verify(?'rsaEncryption', {MD5Hash, SHAHash}) -> - MD5 = hash_final(?MD5, MD5Hash), - SHA = hash_final(?SHA, SHAHash), +certificate_verify(md5sha, _Version, Handshake) -> + MD5 = crypto:md5(Handshake), + SHA = crypto:sha(Handshake), <<MD5/binary, SHA/binary>>; -certificate_verify(?'id-dsa', {_, SHAHash}) -> - hash_final(?SHA, SHAHash). +certificate_verify(HashAlgo, _Version, Handshake) -> + crypto:hash(HashAlgo, Handshake). --spec setup_keys(binary(), binary(), binary(), integer(), - integer(), integer()) -> {binary(), binary(), binary(), +-spec setup_keys(integer(), integer(), binary(), binary(), binary(), integer(), + integer(), integer()) -> {binary(), binary(), binary(), binary(), binary(), binary()}. -setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, - KeyMatLen, IVSize) -> +setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, + KeyMatLen, IVSize) + when Version == 1 -> %% RFC 2246 - 6.3. Key calculation %% key_block = PRF(SecurityParameters.master_secret, %% "key expansion", @@ -88,36 +103,67 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, %% client_write_IV[SecurityParameters.IV_size] %% server_write_IV[SecurityParameters.IV_size] WantedLength = 2 * (HashSize + KeyMatLen + IVSize), - KeyBlock = prf(MasterSecret, "key expansion", + KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion", [ServerRandom, ClientRandom], WantedLength), <<ClientWriteMacSecret:HashSize/binary, ServerWriteMacSecret:HashSize/binary, ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary, ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock, {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, - ServerWriteKey, ClientIV, ServerIV}. + ServerWriteKey, ClientIV, ServerIV}; + +%% TLS v1.1 +setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, + KeyMatLen, IVSize) + when Version == 2 -> + %% RFC 4346 - 6.3. Key calculation + %% key_block = PRF(SecurityParameters.master_secret, + %% "key expansion", + %% SecurityParameters.server_random + + %% SecurityParameters.client_random); + %% Then the key_block is partitioned as follows: + %% client_write_MAC_secret[SecurityParameters.hash_size] + %% server_write_MAC_secret[SecurityParameters.hash_size] + %% client_write_key[SecurityParameters.key_material_length] + %% server_write_key[SecurityParameters.key_material_length] + %% + %% RFC 4346 is incomplete, the client and server IVs have to + %% be generated just like for TLS 1.0 + WantedLength = 2 * (HashSize + KeyMatLen + IVSize), + KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion", + [ServerRandom, ClientRandom], WantedLength), + <<ClientWriteMacSecret:HashSize/binary, + ServerWriteMacSecret:HashSize/binary, + ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary, + ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock, + {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, + ServerWriteKey, ClientIV, ServerIV}; -%% TLS v1.1 uncomment when supported. -%% setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen) -> -%% %% RFC 4346 - 6.3. Key calculation -%% %% key_block = PRF(SecurityParameters.master_secret, -%% %% "key expansion", -%% %% SecurityParameters.server_random + -%% %% SecurityParameters.client_random); -%% %% Then the key_block is partitioned as follows: -%% %% client_write_MAC_secret[SecurityParameters.hash_size] -%% %% server_write_MAC_secret[SecurityParameters.hash_size] -%% %% client_write_key[SecurityParameters.key_material_length] -%% %% server_write_key[SecurityParameters.key_material_length] -%% WantedLength = 2 * (HashSize + KeyMatLen), -%% KeyBlock = prf(MasterSecret, "key expansion", -%% [ServerRandom, ClientRandom], WantedLength), -%% <<ClientWriteMacSecret:HashSize/binary, -%% ServerWriteMacSecret:HashSize/binary, -%% ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary>> -%% = KeyBlock, -%% {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, -%% ServerWriteKey, undefined, undefined}. +%% TLS v1.2 +setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, + KeyMatLen, IVSize) + when Version == 3 -> + %% RFC 5246 - 6.3. Key calculation + %% key_block = PRF(SecurityParameters.master_secret, + %% "key expansion", + %% SecurityParameters.server_random + + %% SecurityParameters.client_random); + %% Then the key_block is partitioned as follows: + %% client_write_MAC_secret[SecurityParameters.hash_size] + %% server_write_MAC_secret[SecurityParameters.hash_size] + %% client_write_key[SecurityParameters.key_material_length] + %% server_write_key[SecurityParameters.key_material_length] + %% client_write_IV[SecurityParameters.fixed_iv_length] + %% server_write_IV[SecurityParameters.fixed_iv_length] + WantedLength = 2 * (HashSize + KeyMatLen + IVSize), + KeyBlock = prf(PrfAlgo, MasterSecret, "key expansion", + [ServerRandom, ClientRandom], WantedLength), + <<ClientWriteMacSecret:HashSize/binary, + ServerWriteMacSecret:HashSize/binary, + ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary, + ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock, + {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, + ServerWriteKey, ClientIV, ServerIV}. -spec mac_hash(integer(), binary(), integer(), integer(), tls_version(), integer(), binary()) -> binary(). @@ -134,9 +180,9 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, Fragment]), Mac. --spec suites() -> [cipher_suite()]. +-spec suites(1|2|3) -> [cipher_suite()]. -suites() -> +suites(Minor) when Minor == 1; Minor == 2-> [ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, @@ -152,7 +198,19 @@ suites() -> ?TLS_RSA_WITH_RC4_128_MD5, ?TLS_DHE_RSA_WITH_DES_CBC_SHA, ?TLS_RSA_WITH_DES_CBC_SHA - ]. + ]; + +suites(Minor) when Minor == 3 -> + [ + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + ?TLS_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, + ?TLS_RSA_WITH_AES_128_CBC_SHA256 + %% ?TLS_DH_anon_WITH_AES_128_CBC_SHA256, + %% ?TLS_DH_anon_WITH_AES_256_CBC_SHA256 + ] ++ suites(2). %%-------------------------------------------------------------------- %%% Internal functions @@ -163,7 +221,19 @@ hmac_hash(?NULL, _, _) -> hmac_hash(?MD5, Key, Value) -> crypto:md5_mac(Key, Value); hmac_hash(?SHA, Key, Value) -> - crypto:sha_mac(Key, Value). + crypto:sha_mac(Key, Value); +hmac_hash(?SHA256, Key, Value) -> + crypto:sha256_mac(Key, Value); +hmac_hash(?SHA384, Key, Value) -> + crypto:sha384_mac(Key, Value); +hmac_hash(?SHA512, Key, Value) -> + crypto:sha512_mac(Key, Value). + +mac_algo(?MD5) -> md5; +mac_algo(?SHA) -> sha; +mac_algo(?SHA256) -> sha256; +mac_algo(?SHA384) -> sha384; +mac_algo(?SHA512) -> sha512. % First, we define a data expansion function, P_hash(secret, data) that % uses a single hash function to expand a secret and seed into an @@ -182,7 +252,7 @@ p_hash(_Secret, _Seed, WantedLength, _Method, _N, [Last | Acc]) when WantedLength =< 0 -> Keep = byte_size(Last) + WantedLength, <<B:Keep/binary, _/binary>> = Last, - lists:reverse(Acc, [B]); + list_to_binary(lists:reverse(Acc, [B])); p_hash(Secret, Seed, WantedLength, Method, N, Acc) -> N1 = N+1, Bin = hmac_hash(Method, Secret, [a(N1, Secret, Seed, Method), Seed]), @@ -214,13 +284,18 @@ split_secret(BinSecret) -> <<_:Div/binary, Secret2:EvenLength/binary>> = BinSecret, {Secret1, Secret2}. -prf(Secret, Label, Seed, WantedLength) -> +prf(?MD5SHA, Secret, Label, Seed, WantedLength) -> %% PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR %% P_SHA-1(S2, label + seed); {S1, S2} = split_secret(Secret), LS = list_to_binary([Label, Seed]), crypto:exor(p_hash(S1, LS, WantedLength, ?MD5), - p_hash(S2, LS, WantedLength, ?SHA)). + p_hash(S2, LS, WantedLength, ?SHA)); + +prf(MAC, Secret, Label, Seed, WantedLength) -> + %% PRF(secret, label, seed) = P_SHA256(secret, label + seed); + LS = list_to_binary([Label, Seed]), + p_hash(Secret, LS, WantedLength, MAC). %%%% Misc help functions %%%% @@ -228,8 +303,3 @@ finished_label(client) -> <<"client finished">>; finished_label(server) -> <<"server finished">>. - -hash_final(?MD5, Conntext) -> - crypto:md5_final(Conntext); -hash_final(?SHA, Conntext) -> - crypto:sha_final(Conntext). diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile index 6b1da63d08..a0f54c0359 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -120,11 +120,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(HRL_FILES_NEEDED_IN_TEST) $(COVER_FILE) $(RELSYSDIR) - $(INSTALL_DATA) ssl.spec ssl.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(HRL_FILES_NEEDED_IN_TEST) $(COVER_FILE) "$(RELSYSDIR)" + $(INSTALL_DATA) ssl.spec ssl.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 590ecf33ca..93f7209aea 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -27,9 +27,11 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("public_key/include/public_key.hrl"). +-include("ssl_internal.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). +-include("ssl_handshake.hrl"). -define('24H_in_sec', 86400). -define(TIMEOUT, 60000). @@ -50,10 +52,10 @@ %%-------------------------------------------------------------------- init_per_suite(Config0) -> Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), + catch crypto:stop(), try crypto:start() of ok -> application:start(public_key), - ssl:start(), %% make rsa certs using oppenssl Result = @@ -90,46 +92,28 @@ end_per_suite(_Config) -> %% variable, but should NOT alter/remove any existing entries. %% Description: Initialization before each test case %%-------------------------------------------------------------------- -init_per_testcase(session_cache_process_list, Config) -> - init_customized_session_cache(list, Config); - -init_per_testcase(session_cache_process_mnesia, Config) -> - mnesia:start(), - init_customized_session_cache(mnesia, Config); - -init_per_testcase(reuse_session_expired, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5), - ssl:stop(), - application:load(ssl), - application:set_env(ssl, session_lifetime, ?EXPIRE), - ssl:start(), - [{watchdog, Dog} | Config]; - init_per_testcase(no_authority_key_identifier, Config) -> %% Clear cach so that root cert will not %% be found. - ssl:stop(), - ssl:start(), + ssl:clear_pem_cache(), Config; -init_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3; - TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3; - TestCase == ciphers_dsa_signed_certs_ssl3; - TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3 -> +init_per_testcase(protocol_versions, Config) -> ssl:stop(), application:load(ssl), - application:set_env(ssl, protocol_version, sslv3), + %% For backwards compatibility sslv2 should be filtered out. + application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]), ssl:start(), Config; -init_per_testcase(protocol_versions, Config) -> +init_per_testcase(reuse_session_expired, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5), ssl:stop(), application:load(ssl), - %% For backwards compatibility sslv2 should be filtered out. - application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]), + application:set_env(ssl, session_lifetime, ?EXPIRE), ssl:start(), - Config; + [{watchdog, Dog} | Config]; init_per_testcase(empty_protocol_versions, Config) -> ssl:stop(), @@ -138,24 +122,15 @@ init_per_testcase(empty_protocol_versions, Config) -> ssl:start(), Config; -init_per_testcase(different_ca_peer_sign, Config0) -> - ssl_test_lib:make_mix_cert(Config0); +%% init_per_testcase(different_ca_peer_sign, Config0) -> +%% ssl_test_lib:make_mix_cert(Config0); init_per_testcase(_TestCase, Config0) -> + test_server:format("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]), Config = lists:keydelete(watchdog, 1, Config0), Dog = test_server:timetrap(?TIMEOUT), [{watchdog, Dog} | Config]. -init_customized_session_cache(Type, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), - ssl:stop(), - application:load(ssl), - application:set_env(ssl, session_cb, ?MODULE), - application:set_env(ssl, session_cb_init_args, [Type]), - ssl:start(), - [{watchdog, Dog} | Config]. - %%-------------------------------------------------------------------- %% Function: end_per_testcase(TestCase, Config) -> _ %% Case - atom() @@ -164,27 +139,10 @@ init_customized_session_cache(Type, Config0) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(session_cache_process_list, Config) -> - application:unset_env(ssl, session_cb), - end_per_testcase(default_action, Config); -end_per_testcase(session_cache_process_mnesia, Config) -> - application:unset_env(ssl, session_cb), - application:unset_env(ssl, session_cb_init_args), - mnesia:stop(), - ssl:stop(), - ssl:start(), - end_per_testcase(default_action, Config); end_per_testcase(reuse_session_expired, Config) -> application:unset_env(ssl, session_lifetime), end_per_testcase(default_action, Config); -end_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3; - TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3; - TestCase == ciphers_dsa_signed_certs_ssl3; - TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3; - TestCase == protocol_versions; - TestCase == empty_protocol_versions-> - application:unset_env(ssl, protocol_version), - end_per_testcase(default_action, Config); + end_per_testcase(_TestCase, Config) -> Dog = ?config(watchdog, Config), case Dog of @@ -205,73 +163,170 @@ end_per_testcase(_TestCase, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app, alerts, connection_info, protocol_versions, - empty_protocol_versions, controlling_process, - controller_dies, client_closes_socket, - connect_dist, peername, peercert, sockname, socket_options, - invalid_inet_get_option, invalid_inet_get_option_not_list, + [ + {group, basic}, + {group, options}, + {group, session}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'} + ]. + +groups() -> + [{basic, [], basic_tests()}, + {options, [], options_tests()}, + {'tlsv1.2', [], all_versions_groups()}, + {'tlsv1.1', [], all_versions_groups()}, + {'tlsv1', [], all_versions_groups() ++ rizzo_tests()}, + {'sslv3', [], all_versions_groups() ++ rizzo_tests()}, + {api,[], api_tests()}, + {certificate_verify, [], certificate_verify_tests()}, + {session, [], session_tests()}, + {renegotiate, [], renegotiate_tests()}, + {ciphers, [], cipher_tests()}, + {error_handling_tests, [], error_handling_tests()} + ]. + +all_versions_groups ()-> + [{group, api}, + {group, certificate_verify}, + {group, renegotiate}, + {group, ciphers}, + {group, error_handling_tests}]. + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. + + +end_per_group(_GroupName, Config) -> + Config. + +basic_tests() -> + [app, + alerts, + send_close, + connect_twice, + connect_dist + ]. + +options_tests() -> + [der_input, + misc_ssl_options, + socket_options, + invalid_inet_get_option, + invalid_inet_get_option_not_list, invalid_inet_get_option_improper_list, - invalid_inet_set_option, invalid_inet_set_option_not_list, + invalid_inet_set_option, + invalid_inet_set_option_not_list, invalid_inet_set_option_improper_list, - misc_ssl_options, versions, cipher_suites, upgrade, - upgrade_with_timeout, tcp_connect, tcp_connect_big, ipv6, ekeyfile, - ecertfile, ecacertfile, eoptions, shutdown, - shutdown_write, shutdown_both, shutdown_error, - ciphers_rsa_signed_certs, ciphers_rsa_signed_certs_ssl3, - ciphers_rsa_signed_certs_openssl_names, - ciphers_rsa_signed_certs_openssl_names_ssl3, - ciphers_dsa_signed_certs, ciphers_dsa_signed_certs_ssl3, - ciphers_dsa_signed_certs_openssl_names, - ciphers_dsa_signed_certs_openssl_names_ssl3, - anonymous_cipher_suites, - default_reject_anonymous, - send_close, - close_transport_accept, dh_params, - server_verify_peer_passive, server_verify_peer_active, + dh_params, + ecertfile, + ecacertfile, + ekeyfile, + eoptions, + protocol_versions, + empty_protocol_versions, + ipv6, + reuseaddr]. + +api_tests() -> + [connection_info, + peername, + peercert, + sockname, + versions, + controlling_process, + upgrade, + upgrade_with_timeout, + shutdown, + shutdown_write, + shutdown_both, + shutdown_error, + hibernate + ]. + +certificate_verify_tests() -> + [server_verify_peer_passive, + server_verify_peer_active, server_verify_peer_active_once, - server_verify_none_passive, server_verify_none_active, + server_verify_none_passive, + server_verify_none_active, server_verify_none_active_once, - server_verify_no_cacerts, server_require_peer_cert_ok, + server_verify_no_cacerts, + server_require_peer_cert_ok, server_require_peer_cert_fail, server_verify_client_once_passive, server_verify_client_once_active, server_verify_client_once_active_once, - client_verify_none_passive, client_verify_none_active, + client_verify_none_passive, + client_verify_none_active, client_verify_none_active_once, - reuse_session, - reuse_session_expired, - server_does_not_want_to_reuse_session, - client_renegotiate, server_renegotiate, - client_renegotiate_reused_session, - server_renegotiate_reused_session, - client_no_wrap_sequence_number, - server_no_wrap_sequence_number, extended_key_usage_verify_peer, + extended_key_usage_verify_peer, extended_key_usage_verify_none, - no_authority_key_identifier, invalid_signature_client, - invalid_signature_server, cert_expired, + invalid_signature_client, + invalid_signature_server, + cert_expired, client_with_cert_cipher_suites_handshake, verify_fun_always_run_client, verify_fun_always_run_server, - unknown_server_ca_fail, der_input, + unknown_server_ca_fail, unknown_server_ca_accept_verify_none, unknown_server_ca_accept_verify_peer, unknown_server_ca_accept_backwardscompatibility, - %%different_ca_peer_sign, - no_reuses_session_server_restart_new_cert, - no_reuses_session_server_restart_new_cert_file, reuseaddr, - hibernate, connect_twice, renegotiate_dos_mitigate_active, - renegotiate_dos_mitigate_passive, - tcp_error_propagation_in_active_mode, rizzo, no_rizzo_rc4 + no_authority_key_identifier ]. -groups() -> - []. +session_tests() -> + [reuse_session, + reuse_session_expired, + server_does_not_want_to_reuse_session, + no_reuses_session_server_restart_new_cert, + no_reuses_session_server_restart_new_cert_file]. -init_per_group(_GroupName, Config) -> - Config. +renegotiate_tests() -> + [client_renegotiate, + server_renegotiate, + client_renegotiate_reused_session, + server_renegotiate_reused_session, + client_no_wrap_sequence_number, + server_no_wrap_sequence_number, + renegotiate_dos_mitigate_active, + renegotiate_dos_mitigate_passive]. -end_per_group(_GroupName, Config) -> - Config. +cipher_tests() -> + [cipher_suites, + ciphers_rsa_signed_certs, + ciphers_rsa_signed_certs_openssl_names, + ciphers_dsa_signed_certs, + ciphers_dsa_signed_certs_openssl_names, + anonymous_cipher_suites, + default_reject_anonymous]. + +error_handling_tests()-> + [controller_dies, + client_closes_socket, + tcp_error_propagation_in_active_mode, + tcp_connect, + tcp_connect_big, + close_transport_accept + ]. + +rizzo_tests() -> + [rizzo, + no_rizzo_rc4]. %% Test cases starts here. %%-------------------------------------------------------------------- @@ -1724,21 +1779,7 @@ ciphers_rsa_signed_certs(Config) when is_list(Config) -> ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:rsa_suites(), - test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]), - run_suites(Ciphers, Version, Config, rsa). - -ciphers_rsa_signed_certs_ssl3(doc) -> - ["Test all rsa ssl cipher suites in ssl3"]; - -ciphers_rsa_signed_certs_ssl3(suite) -> - []; - -ciphers_rsa_signed_certs_ssl3(Config) when is_list(Config) -> - Version = - ssl_record:protocol_version({3,0}), - - Ciphers = ssl_test_lib:rsa_suites(), - test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]), + test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]), run_suites(Ciphers, Version, Config, rsa). ciphers_rsa_signed_certs_openssl_names(doc) -> @@ -1755,18 +1796,6 @@ ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> run_suites(Ciphers, Version, Config, rsa). -ciphers_rsa_signed_certs_openssl_names_ssl3(doc) -> - ["Test all dsa ssl cipher suites in ssl3"]; - -ciphers_rsa_signed_certs_openssl_names_ssl3(suite) -> - []; - -ciphers_rsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) -> - Version = ssl_record:protocol_version({3,0}), - Ciphers = ssl_test_lib:openssl_rsa_suites(), - run_suites(Ciphers, Version, Config, rsa). - - ciphers_dsa_signed_certs(doc) -> ["Test all dsa ssl cipher suites in highest support ssl/tls version"]; @@ -1778,23 +1807,8 @@ ciphers_dsa_signed_certs(Config) when is_list(Config) -> ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:dsa_suites(), - test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]), - run_suites(Ciphers, Version, Config, dsa). - -ciphers_dsa_signed_certs_ssl3(doc) -> - ["Test all dsa ssl cipher suites in ssl3"]; - -ciphers_dsa_signed_certs_ssl3(suite) -> - []; - -ciphers_dsa_signed_certs_ssl3(Config) when is_list(Config) -> - Version = - ssl_record:protocol_version({3,0}), - - Ciphers = ssl_test_lib:dsa_suites(), - test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]), + test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]), run_suites(Ciphers, Version, Config, dsa). - ciphers_dsa_signed_certs_openssl_names(doc) -> ["Test all dsa ssl cipher suites in highest support ssl/tls version"]; @@ -1810,18 +1824,6 @@ ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) -> test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]), run_suites(Ciphers, Version, Config, dsa). - -ciphers_dsa_signed_certs_openssl_names_ssl3(doc) -> - ["Test all dsa ssl cipher suites in ssl3"]; - -ciphers_dsa_signed_certs_openssl_names_ssl3(suite) -> - []; - -ciphers_dsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) -> - Version = ssl_record:protocol_version({3,0}), - Ciphers = ssl_test_lib:openssl_dsa_suites(), - run_suites(Ciphers, Version, Config, dsa). - anonymous_cipher_suites(doc)-> ["Test the anonymous ciphersuites"]; anonymous_cipher_suites(suite) -> @@ -1858,7 +1860,7 @@ run_suites(Ciphers, Version, Config, Type) -> end. erlang_cipher_suite(Suite) when is_list(Suite)-> - ssl_cipher:suite_definition(ssl_cipher:openssl_suite(Suite)); + ssl:suite_definition(ssl_cipher:openssl_suite(Suite)); erlang_cipher_suite(Suite) -> Suite. @@ -2085,7 +2087,9 @@ reuse_session_expired(Config) when is_list(Config) -> Server ! listen, %% Make sure session is unregistered due to expiration - test_server:sleep((?EXPIRE+1) * 1000), + test_server:sleep((?EXPIRE+1)), + [{session_id, Id} |_] = SessionInfo, + make_sure_expired(Hostname, Port, Id), Client2 = ssl_test_lib:start_client([{node, ClientNode}, @@ -2104,6 +2108,22 @@ reuse_session_expired(Config) when is_list(Config) -> ssl_test_lib:close(Client1), ssl_test_lib:close(Client2). +make_sure_expired(Host, Port, Id) -> + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + Cache = element(2, State), + case ssl_session_cache:lookup(Cache, {{Host, Port}, Id}) of + undefined -> + ok; + #session{is_resumable = false} -> + ok; + _ -> + test_server:sleep(?SLEEP), + make_sure_expired(Host, Port, Id) + end. + + %%-------------------------------------------------------------------- server_does_not_want_to_reuse_session(doc) -> ["Test reuse of sessions (short handshake)"]; @@ -3649,6 +3669,8 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client0), + ssl:clear_pem_cache(), + NewServerOpts = new_config(PrivDir, DsaServerOpts), Server1 = @@ -3872,16 +3894,16 @@ tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ServerOpts}]), + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {Client, #sslsocket{pid=Pid} = SslSocket} = ssl_test_lib:start_client([return_socket, - {node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, receive_msg, []}}, - {options, ClientOpts}]), + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, receive_msg, []}}, + {options, ClientOpts}]), {status, _, _, StatusInfo} = sys:get_status(Pid), [_, _,_, _, Prop] = StatusInfo, @@ -3892,6 +3914,32 @@ tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> Pid ! {tcp_error, Socket, etimedout}, ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}). + + +%%-------------------------------------------------------------------- + +recv_error_handling(doc) -> + ["Special case of call error handling"]; +recv_error_handling(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, recv_close, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + {_Client, #sslsocket{} = SslSocket} = ssl_test_lib:start_client([return_socket, + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + ssl:close(SslSocket), + ssl_test_lib:check_result(Server, ok). + + %%-------------------------------------------------------------------- rizzo(doc) -> ["Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is @@ -3899,21 +3947,22 @@ rizzo(doc) -> ["Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as i rizzo(Config) when is_list(Config) -> Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128], - run_send_recv_rizzo(Ciphers, Config, sslv3, - {?MODULE, send_recv_result_active_rizzo, []}), - run_send_recv_rizzo(Ciphers, Config, tlsv1, + Prop = ?config(tc_group_properties, Config), + Version = proplists:get_value(name, Prop), + run_send_recv_rizzo(Ciphers, Config, Version, {?MODULE, send_recv_result_active_rizzo, []}). - +%%-------------------------------------------------------------------- no_rizzo_rc4(doc) -> ["Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"]; no_rizzo_rc4(Config) when is_list(Config) -> Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(),Y == rc4_128], - run_send_recv_rizzo(Ciphers, Config, sslv3, - {?MODULE, send_recv_result_active_no_rizzo, []}), - run_send_recv_rizzo(Ciphers, Config, tlsv1, + Prop = ?config(tc_group_properties, Config), + Version = proplists:get_value(name, Prop), + run_send_recv_rizzo(Ciphers, Config, Version, {?MODULE, send_recv_result_active_no_rizzo, []}). +%%-------------------------------------------------------------------- run_send_recv_rizzo(Ciphers, Config, Version, Mfa) -> Result = lists:map(fun(Cipher) -> rizzo_test(Cipher, Config, Version, Mfa) end, @@ -3967,6 +4016,15 @@ send_recv_result(Socket) -> {ok,"Hello world"} = ssl:recv(Socket, 11), ok. +recv_close(Socket) -> + {error, closed} = ssl:recv(Socket, 11), + receive + {_,{error,closed}} -> + error_extra_close_sent_to_user_process + after 500 -> + ok + end. + send_recv_result_active(Socket) -> ssl:send(Socket, "Hello world"), receive diff --git a/lib/ssl/test/ssl_cipher_SUITE.erl b/lib/ssl/test/ssl_cipher_SUITE.erl index 99bc21e820..83beeb0131 100644 --- a/lib/ssl/test/ssl_cipher_SUITE.erl +++ b/lib/ssl/test/ssl_cipher_SUITE.erl @@ -27,6 +27,7 @@ -include("ssl_internal.hrl"). -include("ssl_record.hrl"). -include("ssl_cipher.hrl"). +-include("ssl_alert.hrl"). -define(TIMEOUT, 600000). @@ -103,7 +104,7 @@ end_per_testcase(_TestCase, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [aes_decipher_good, aes_decipher_fail]. + [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11]. groups() -> []. @@ -131,10 +132,39 @@ aes_decipher_good(Config) when is_list(Config) -> 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, - Version = {3,3}, - Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56,72,69,76,76,79,10>>, + Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56, "HELLO\n">>, Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>, + Version = {3,0}, {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), + Version1 = {3,1}, + {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), + ok. + +%%-------------------------------------------------------------------- + +aes_decipher_good_tls11(doc) -> + ["Decipher a known TLS 1.1 cryptotext."]; + +aes_decipher_good_tls11(suite) -> + []; + +%% the fragment is actuall a TLS 1.1 record, with +%% Version = TLS 1.1, we get the correct NextIV in #cipher_state +aes_decipher_good_tls11(Config) when is_list(Config) -> + HashSz = 32, + CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, + key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,148>>}, + Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8, + 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, + 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, + 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, + Content = <<"HELLO\n">>, + NextIV = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56>>, + Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>, + Version = {3,2}, + {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), + Version1 = {3,2}, + {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), ok. %%-------------------------------------------------------------------- @@ -154,10 +184,38 @@ aes_decipher_fail(Config) when is_list(Config) -> 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, - Version = {3,3}, + Version = {3,0}, {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), 32 = byte_size(Content), 32 = byte_size(Mac), + Version1 = {3,1}, + {Content1, Mac1, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), + 32 = byte_size(Content1), + 32 = byte_size(Mac1), + ok. + +%%-------------------------------------------------------------------- + +aes_decipher_fail_tls11(doc) -> + ["Decipher a known TLS 1.1 cryptotext."]; + +aes_decipher_fail_tls11(suite) -> + []; + +%% same as above, last byte of key replaced +%% stricter padding checks in TLS 1.1 mean we get an alert instead +aes_decipher_fail_tls11(Config) when is_list(Config) -> + HashSz = 32, + CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, + key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,254>>}, + Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8, + 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, + 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, + 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, + Version = {3,2}, + #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), + Version1 = {3,3}, + #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), ok. %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl index 12d92c8ca1..818f7f1897 100644 --- a/lib/ssl/test/ssl_dist_SUITE.erl +++ b/lib/ssl/test/ssl_dist_SUITE.erl @@ -369,7 +369,7 @@ mk_node_cmdline(ListenPort, Name, Args) -> _ -> "-name " end, {ok, Pwd} = file:get_cwd(), - Prog ++ " " + "\"" ++ Prog ++ "\" " ++ Static ++ " " ++ NameSw ++ " " ++ Name ++ " " ++ "-pa " ++ Pa ++ " " @@ -729,7 +729,7 @@ add_ssl_opts_config(Config) -> [{ssl_opts, "-boot " ++ Script} | Config] catch _:_ -> - [{ssl_opts, "-pa " ++ filename:dirname(code:which(ssl))} + [{ssl_opts, "-pa \"" ++ filename:dirname(code:which(ssl))++"\""} | add_comment_config( "Bootscript wasn't used since the test wasn't run on an " "installed OTP system.", diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index 08c23b2d47..946865a3d8 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -48,7 +48,8 @@ decode_hello_handshake(_Config) -> 16#00, 16#00, 16#33, 16#74, 16#00, 16#07, 16#06, 16#73, 16#70, 16#64, 16#79, 16#2f, 16#32>>, - {Records, _Buffer} = ssl_handshake:get_tls_handshake(HelloPacket, <<>>), + Version = {3, 0}, + {Records, _Buffer} = ssl_handshake:get_tls_handshake(Version, HelloPacket, <<>>), {Hello, _Data} = hd(Records), #renegotiation_info{renegotiated_connection = <<0>>} = Hello#server_hello.renegotiation_info. diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 4b74f57a60..8ce80cb725 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -53,6 +53,7 @@ %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> + catch crypto:stop(), try crypto:start() of ok -> application:start(public_key), @@ -121,15 +122,56 @@ end_per_testcase(_TestCase, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> + [ + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'} + ]. + +groups() -> + [{'tlsv1.2', [], packet_tests()}, + {'tlsv1.1', [], packet_tests()}, + {'tlsv1', [], packet_tests()}, + {'sslv3', [], packet_tests()}]. + +packet_tests() -> + active_packet_tests() ++ active_once_packet_tests() ++ passive_packet_tests() ++ + [packet_send_to_large, + packet_cdr_decode, packet_cdr_decode_list, + packet_http_decode, packet_http_decode_list, + packet_http_bin_decode_multi, + packet_line_decode, packet_line_decode_list, + packet_asn1_decode, packet_asn1_decode_list, + packet_tpkt_decode, packet_tpkt_decode_list, + packet_sunrm_decode, packet_sunrm_decode_list]. + +passive_packet_tests() -> [packet_raw_passive_many_small, packet_0_passive_many_small, packet_1_passive_many_small, packet_2_passive_many_small, packet_4_passive_many_small, - packet_raw_passive_some_big, packet_0_passive_some_big, - packet_1_passive_some_big, packet_2_passive_some_big, + packet_raw_passive_some_big, + packet_0_passive_some_big, + packet_1_passive_some_big, + packet_2_passive_some_big, packet_4_passive_some_big, - packet_raw_active_once_many_small, + packet_httph_passive, + packet_httph_bin_passive, + packet_http_error_passive, + packet_wait_passive, + packet_size_passive, + packet_baddata_passive, + %% inet header option should be deprecated! + header_decode_one_byte_passive, + header_decode_two_bytes_passive, + header_decode_two_bytes_two_sent_passive, + header_decode_two_bytes_one_sent_passive + ]. + +active_once_packet_tests() -> + [packet_raw_active_once_many_small, packet_0_active_once_many_small, packet_1_active_once_many_small, packet_2_active_once_many_small, @@ -139,44 +181,49 @@ all() -> packet_1_active_once_some_big, packet_2_active_once_some_big, packet_4_active_once_some_big, - packet_raw_active_many_small, - packet_0_active_many_small, packet_1_active_many_small, - packet_2_active_many_small, packet_4_active_many_small, - packet_raw_active_some_big, packet_0_active_some_big, - packet_1_active_some_big, packet_2_active_some_big, - packet_4_active_some_big, packet_send_to_large, - packet_wait_passive, packet_wait_active, - packet_baddata_passive, packet_baddata_active, - packet_size_passive, packet_size_active, - packet_cdr_decode, packet_cdr_decode_list, - packet_http_decode, packet_http_decode_list, - packet_http_bin_decode_multi, packet_http_error_passive, - packet_httph_active, packet_httph_bin_active, - packet_httph_active_once, packet_httph_bin_active_once, - packet_httph_passive, packet_httph_bin_passive, - packet_line_decode, packet_line_decode_list, - packet_asn1_decode, packet_asn1_decode_list, - packet_tpkt_decode, packet_tpkt_decode_list, - packet_sunrm_decode, packet_sunrm_decode_list, - {group, header} + packet_httph_active_once, + packet_httph_bin_active_once ]. -groups() -> - [{header, [], [ header_decode_one_byte, - header_decode_two_bytes, - header_decode_two_bytes_one_sent, - header_decode_two_bytes_two_sent]}]. +active_packet_tests() -> + [packet_raw_active_many_small, + packet_0_active_many_small, + packet_1_active_many_small, + packet_2_active_many_small, + packet_4_active_many_small, + packet_raw_active_some_big, + packet_0_active_some_big, + packet_1_active_some_big, + packet_2_active_some_big, + packet_4_active_some_big, + packet_httph_active, + packet_httph_bin_active, + packet_wait_active, + packet_baddata_active, + packet_size_active, + %% inet header option should be deprecated! + header_decode_one_byte_active, + header_decode_two_bytes_active, + header_decode_two_bytes_two_sent_active, + header_decode_two_bytes_one_sent_active + ]. -init_per_group(header, Config) -> - case ssl_record:highest_protocol_version(ssl_record:supported_protocol_versions()) of - {3, N} when N < 2 -> - {skip, ""}; + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, "Missing crypto support"} + end; _ -> + ssl:start(), Config - end; + end. -init_per_group(_, Config) -> - Config. end_per_group(_GroupName, Config) -> Config. @@ -2435,11 +2482,11 @@ packet_sunrm_decode_list(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -header_decode_one_byte(doc) -> +header_decode_one_byte_active(doc) -> ["Test setting the packet option {header, 1}"]; -header_decode_one_byte(suite) -> +header_decode_one_byte_active(suite) -> []; -header_decode_one_byte(Config) when is_list(Config) -> +header_decode_one_byte_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -2448,7 +2495,7 @@ header_decode_one_byte(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_header_decode, + {mfa, {?MODULE, server_header_decode_active, [Data, [11 | <<"Hello world">>]]}}, {options, [{active, true}, binary, {header,1}|ServerOpts]}]), @@ -2457,7 +2504,7 @@ header_decode_one_byte(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_header_decode, + {mfa, {?MODULE, client_header_decode_active, [Data, [11 | <<"Hello world">> ]]}}, {options, [{active, true}, {header, 1}, binary | ClientOpts]}]), @@ -2469,11 +2516,11 @@ header_decode_one_byte(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes(doc) -> +header_decode_two_bytes_active(doc) -> ["Test setting the packet option {header, 2}"]; -header_decode_two_bytes(suite) -> +header_decode_two_bytes_active(suite) -> []; -header_decode_two_bytes(Config) when is_list(Config) -> +header_decode_two_bytes_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -2482,7 +2529,7 @@ header_decode_two_bytes(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_header_decode, + {mfa, {?MODULE, server_header_decode_active, [Data, [11, $H | <<"ello world">> ]]}}, {options, [{active, true}, binary, {header,2}|ServerOpts]}]), @@ -2491,7 +2538,7 @@ header_decode_two_bytes(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_header_decode, + {mfa, {?MODULE, client_header_decode_active, [Data, [11, $H | <<"ello world">> ]]}}, {options, [{active, true}, {header, 2}, binary | ClientOpts]}]), @@ -2504,11 +2551,11 @@ header_decode_two_bytes(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_two_sent(doc) -> - ["Test setting the packet option {header, 2} and sending on byte"]; -header_decode_two_bytes_two_sent(suite) -> +header_decode_two_bytes_two_sent_active(doc) -> + ["Test setting the packet option {header, 2} and sending two byte"]; +header_decode_two_bytes_two_sent_active(suite) -> []; -header_decode_two_bytes_two_sent(Config) when is_list(Config) -> +header_decode_two_bytes_two_sent_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -2517,8 +2564,8 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_header_decode, - [Data, [$H, $e | <<>> ]]}}, + {mfa, {?MODULE, server_header_decode_active, + [Data, [$H, $e]]}}, {options, [{active, true}, binary, {header,2}|ServerOpts]}]), @@ -2526,8 +2573,8 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_header_decode, - [Data, [$H, $e | <<>> ]]}}, + {mfa, {?MODULE, client_header_decode_active, + [Data, [$H, $e]]}}, {options, [{active, true}, {header, 2}, binary | ClientOpts]}]), @@ -2539,11 +2586,11 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_one_sent(doc) -> - ["Test setting the packet option {header, 2} and sending on byte"]; -header_decode_two_bytes_one_sent(suite) -> +header_decode_two_bytes_one_sent_active(doc) -> + ["Test setting the packet option {header, 2} and sending one byte"]; +header_decode_two_bytes_one_sent_active(suite) -> []; -header_decode_two_bytes_one_sent(Config) when is_list(Config) -> +header_decode_two_bytes_one_sent_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -2552,7 +2599,7 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_header_decode, + {mfa, {?MODULE, server_header_decode_active, [Data, "H"]}}, {options, [{active, true}, binary, {header,2}|ServerOpts]}]), @@ -2561,7 +2608,7 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_header_decode, + {mfa, {?MODULE, client_header_decode_active, [Data, "H"]}}, {options, [{active, true}, {header, 2}, binary | ClientOpts]}]), @@ -2571,6 +2618,143 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- + +header_decode_one_byte_passive(doc) -> + ["Test setting the packet option {header, 1}"]; +header_decode_one_byte_passive(suite) -> + []; +header_decode_one_byte_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<11:8, "Hello world">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode_passive, + [Data, [11 | <<"Hello world">>]]}}, + {options, [{active, false}, binary, + {header,1}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode_passive, + [Data, [11 | <<"Hello world">> ]]}}, + {options, [{active, false}, {header, 1}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +header_decode_two_bytes_passive(doc) -> + ["Test setting the packet option {header, 2}"]; +header_decode_two_bytes_passive(suite) -> + []; +header_decode_two_bytes_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<11:8, "Hello world">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode_passive, + [Data, [11, $H | <<"ello world">> ]]}}, + {options, [{active, false}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode_passive, + [Data, [11, $H | <<"ello world">> ]]}}, + {options, [{active, false}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +header_decode_two_bytes_two_sent_passive(doc) -> + ["Test setting the packet option {header, 2} and sending two byte"]; +header_decode_two_bytes_two_sent_passive(suite) -> + []; +header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<"He">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode_passive, + [Data, [$H, $e]]}}, + {options, [{active, false}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode_passive, + [Data, [$H, $e]]}}, + {options, [{active, false}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +header_decode_two_bytes_one_sent_passive(doc) -> + ["Test setting the packet option {header, 2} and sending one byte"]; +header_decode_two_bytes_one_sent_passive(suite) -> + []; +header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<"H">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode_passive, + [Data, "H"]}}, + {options, [{active, false}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode_passive, + [Data, "H"]}}, + {options, [{active, false}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- %% Internal functions @@ -2757,29 +2941,52 @@ client_packet_decode(Socket, P1, P2, Packet) -> Other2 -> exit({?LINE, Other2}) end. -server_header_decode(Socket, Packet, Result) -> +server_header_decode_active(Socket, Packet, Result) -> receive - {ssl, Socket, Result} -> ok; - Other1 -> exit({?LINE, Other1}) - end, - ok = ssl:send(Socket, Packet), - receive - {ssl, Socket, Result} -> ok; - Other2 -> exit({?LINE, Other2}) + {ssl, Socket, Result} -> + ok; + {ssl, Socket, Other1} -> + check_header_result(Result, Other1) end, ok = ssl:send(Socket, Packet). -client_header_decode(Socket, Packet, Result) -> +client_header_decode_active(Socket, Packet, Result) -> ok = ssl:send(Socket, Packet), receive - {ssl, Socket, Result} -> ok; - Other1 -> exit({?LINE, Other1}) + {ssl, Socket, Result} -> + ok; + {ssl, Socket, Other1} -> + check_header_result(Result, Other1) + end. + +server_header_decode_passive(Socket, Packet, Result) -> + case ssl:recv(Socket, 0) of + {ok, Result} -> + ok; + {ok, Other} -> + check_header_result(Result, Other) end, + ok = ssl:send(Socket, Packet). + +client_header_decode_passive(Socket, Packet, Result) -> ok = ssl:send(Socket, Packet), - receive - {ssl, Socket, Result} -> ok; - Other2 -> exit({?LINE, Other2}) + + case ssl:recv(Socket, 0) of + {ok, Result} -> + ok; + {ok, Other} -> + check_header_result(Result, Other) end. + +%% The inet header option is a broken option as it does not buffer until it gets enough data. +%% This check only checks that it has the same behavior as inet, but it is a quite useless +%% option and the bitsynax makes it obsolete! +check_header_result([Byte1 | _], [Byte1]) -> + ok; +check_header_result([Byte1, Byte2 | _], [Byte1, Byte2]) -> + ok; +check_header_result(_,Got) -> + exit({?LINE, Got}). server_line_packet_decode(Socket, Packet) when is_binary(Packet) -> [L1, L2] = string:tokens(binary_to_list(Packet), "\n"), diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index 24e86b3913..c97f97e70b 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -37,6 +37,7 @@ %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> + catch crypto:stop(), try crypto:start() of ok -> application:start(public_key), @@ -102,23 +103,56 @@ end_per_testcase(_TestCase, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> + [ + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'} + ]. + +groups() -> + [ + {'tlsv1.2', [], payload_tests()}, + {'tlsv1.1', [], payload_tests()}, + {'tlsv1', [], payload_tests()}, + {'sslv3', [], payload_tests()} + ]. + +payload_tests() -> [server_echos_passive_small, server_echos_active_once_small, - server_echos_active_small, client_echos_passive_small, + server_echos_active_small, + client_echos_passive_small, client_echos_active_once_small, - client_echos_active_small, server_echos_passive_big, - server_echos_active_once_big, server_echos_active_big, - client_echos_passive_big, client_echos_active_once_big, - client_echos_active_big, server_echos_passive_huge, - server_echos_active_once_huge, server_echos_active_huge, + client_echos_active_small, + server_echos_passive_big, + server_echos_active_once_big, + server_echos_active_big, + client_echos_passive_big, + client_echos_active_once_big, + client_echos_active_big, + server_echos_passive_huge, + server_echos_active_once_huge, + server_echos_active_huge, client_echos_passive_huge, - client_echos_active_once_huge, client_echos_active_huge]. + client_echos_active_once_huge, + client_echos_active_huge]. -groups() -> - []. -init_per_group(_GroupName, Config) -> - Config. +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. end_per_group(_GroupName, Config) -> Config. diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index 491aa893c2..6d758ecb01 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -49,6 +49,7 @@ %%-------------------------------------------------------------------- init_per_suite(Config0) -> Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), + catch crypto:stop(), try crypto:start() of ok -> application:start(public_key), diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index fa8a1826f2..63731ee25c 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -150,7 +150,7 @@ run_client(Opts) -> {ok, Socket} -> Pid ! { connected, Socket }, test_server:format("Client: connected~n", []), - %% In specail cases we want to know the client port, it will + %% In special cases we want to know the client port, it will %% be indicated by sending {port, 0} in options list! send_selected_port(Pid, proplists:get_value(port, Options), Socket), {Module, Function, Args} = proplists:get_value(mfa, Opts), @@ -708,3 +708,33 @@ state([{data,[{"StateData", State}]} | _]) -> State; state([_ | Rest]) -> state(Rest). + +is_tls_version('tlsv1.2') -> + true; +is_tls_version('tlsv1.1') -> + true; +is_tls_version('tlsv1') -> + true; +is_tls_version('sslv3') -> + true; +is_tls_version(_) -> + false. + +init_tls_version(Version) -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, protocol_version, Version), + ssl:start(). + +sufficient_crypto_support('tlsv1.2') -> + Data = "Sampl", + Data2 = "e #1", + Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39, + 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>, + try + crypto:sha256_mac(Key, lists:flatten([Data, Data2])), + true + catch _:_ -> false + end; +sufficient_crypto_support(_) -> + true. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 01fca1f166..d446014f7b 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -50,6 +50,7 @@ init_per_suite(Config0) -> false -> {skip, "Openssl not found"}; _ -> + catch crypto:stop(), try crypto:start() of ok -> application:start(public_key), @@ -106,7 +107,8 @@ init_per_testcase(TestCase, Config0) -> special_init(TestCase, Config) when TestCase == erlang_client_openssl_server_renegotiate; TestCase == erlang_client_openssl_server_no_wrap_sequence_number; - TestCase == erlang_server_openssl_client_no_wrap_sequence_number -> + TestCase == erlang_server_openssl_client_no_wrap_sequence_number + -> check_sane_openssl_renegotaite(Config); special_init(ssl2_erlang_server_openssl_client, Config) -> @@ -149,37 +151,59 @@ end_per_testcase(_, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [erlang_client_openssl_server, + [ + {group, basic}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'} + ]. + +groups() -> + [{basic, [], basic_tests()}, + {'tlsv1.2', [], all_versions_tests()}, + {'tlsv1.1', [], all_versions_tests()}, + {'tlsv1', [], all_versions_tests()}, + {'sslv3', [], all_versions_tests()}]. + +basic_tests() -> + [basic_erlang_client_openssl_server, + basic_erlang_server_openssl_client, + expired_session]. + +all_versions_tests() -> + [ + erlang_client_openssl_server, erlang_server_openssl_client, - tls1_erlang_client_openssl_server_dsa_cert, - tls1_erlang_server_openssl_client_dsa_cert, - ssl3_erlang_client_openssl_server_dsa_cert, - ssl3_erlang_server_openssl_client_dsa_cert, + erlang_client_openssl_server_dsa_cert, + erlang_server_openssl_client_dsa_cert, erlang_server_openssl_client_reuse_session, erlang_client_openssl_server_renegotiate, erlang_client_openssl_server_no_wrap_sequence_number, erlang_server_openssl_client_no_wrap_sequence_number, erlang_client_openssl_server_no_server_ca_cert, - ssl3_erlang_client_openssl_server, - ssl3_erlang_server_openssl_client, - ssl3_erlang_client_openssl_server_client_cert, - ssl3_erlang_server_openssl_client_client_cert, - ssl3_erlang_server_erlang_client_client_cert, - tls1_erlang_client_openssl_server, - tls1_erlang_server_openssl_client, - tls1_erlang_client_openssl_server_client_cert, - tls1_erlang_server_openssl_client_client_cert, - tls1_erlang_server_erlang_client_client_cert, - ciphers_rsa_signed_certs, ciphers_dsa_signed_certs, + erlang_client_openssl_server_client_cert, + erlang_server_openssl_client_client_cert, + ciphers_rsa_signed_certs, + ciphers_dsa_signed_certs, erlang_client_bad_openssl_server, - expired_session, - ssl2_erlang_server_openssl_client]. + ssl2_erlang_server_openssl_client + ]. -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case check_sane_openssl_version(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, openssl_does_not_support_version} + end; + _ -> + ssl:start(), + Config + end. end_per_group(_GroupName, Config) -> Config. @@ -187,12 +211,11 @@ end_per_group(_GroupName, Config) -> %% Test cases starts here. %%-------------------------------------------------------------------- - -erlang_client_openssl_server(doc) -> +basic_erlang_client_openssl_server(doc) -> ["Test erlang client with openssl server"]; -erlang_client_openssl_server(suite) -> +basic_erlang_client_openssl_server(suite) -> []; -erlang_client_openssl_server(Config) when is_list(Config) -> +basic_erlang_client_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -204,8 +227,8 @@ erlang_client_openssl_server(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -230,13 +253,12 @@ erlang_client_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, false), ok. - %%-------------------------------------------------------------------- -erlang_server_openssl_client(doc) -> +basic_erlang_server_openssl_client(doc) -> ["Test erlang server with openssl client"]; -erlang_server_openssl_client(suite) -> +basic_erlang_server_openssl_client(suite) -> []; -erlang_server_openssl_client(Config) when is_list(Config) -> +basic_erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -249,8 +271,8 @@ erlang_server_openssl_client(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ + + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -265,30 +287,26 @@ erlang_server_openssl_client(Config) when is_list(Config) -> close_port(OpenSslPort), process_flag(trap_exit, false), ok. - -%%-------------------------------------------------------------------- - -tls1_erlang_client_openssl_server_dsa_cert(doc) -> - ["Test erlang server with openssl client"]; -tls1_erlang_client_openssl_server_dsa_cert(suite) -> +%%-------------------------------------------------------------------- +erlang_client_openssl_server(doc) -> + ["Test erlang client with openssl server"]; +erlang_client_openssl_server(suite) -> []; -tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> +erlang_client_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = ?config(client_dsa_opts, Config), - ServerOpts = ?config(server_dsa_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - + Data = "From openssl to erlang", Port = ssl_test_lib:inet_port(node()), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ - " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1 -msg", + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -cert " ++ CertFile ++ " -key " ++ KeyFile, test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -302,44 +320,39 @@ tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), - ssl_test_lib:check_result(Client, ok), - + ssl_test_lib:check_result(Client, ok), + %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), process_flag(trap_exit, false), ok. -%%-------------------------------------------------------------------- -tls1_erlang_server_openssl_client_dsa_cert(doc) -> +%%-------------------------------------------------------------------- +erlang_server_openssl_client(doc) -> ["Test erlang server with openssl client"]; -tls1_erlang_server_openssl_client_dsa_cert(suite) -> +erlang_server_openssl_client(suite) -> []; -tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> +erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = ?config(client_dsa_opts, Config), - ServerOpts = ?config(server_dsa_verify_opts, Config), + ServerOpts = ?config(server_opts, Config), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Data = "From openssl to erlang", - CaCertFile = proplists:get_value(cacertfile, ClientOpts), - CertFile = proplists:get_value(certfile, ClientOpts), - KeyFile = proplists:get_value(keyfile, ClientOpts), - + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ - " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -tls1 -msg", + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -host localhost", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -347,7 +360,7 @@ tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), - + %% Clean close down! Server needs to be closed first !! ssl_test_lib:close(Server), close_port(OpenSslPort), @@ -356,11 +369,11 @@ tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ssl3_erlang_client_openssl_server_dsa_cert(doc) -> +erlang_client_openssl_server_dsa_cert(doc) -> ["Test erlang server with openssl client"]; -ssl3_erlang_client_openssl_server_dsa_cert(suite) -> +erlang_client_openssl_server_dsa_cert(suite) -> []; -ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> +erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_dsa_opts, Config), ServerOpts = ?config(server_dsa_opts, Config), @@ -373,10 +386,11 @@ ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3 -msg", + ++ " -key " ++ KeyFile ++ " -Verify 2 -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -400,49 +414,46 @@ ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> ssl_test_lib:close(Client), process_flag(trap_exit, false), ok. - -%%-------------------------------------------------------------------- - -ssl3_erlang_server_openssl_client_dsa_cert(doc) -> +%%-------------------------------------------------------------------- +erlang_server_openssl_client_dsa_cert(doc) -> ["Test erlang server with openssl client"]; -ssl3_erlang_server_openssl_client_dsa_cert(suite) -> +erlang_server_openssl_client_dsa_cert(suite) -> []; -ssl3_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> +erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_dsa_opts, Config), - ServerOpts = ?config(server_dsa_verify_opts, Config), + ServerOpts = ?config(server_dsa_verify_opts, Config), {_, ServerNode, _} = ssl_test_lib:run_where(Config), - + Data = "From openssl to erlang", CaCertFile = proplists:get_value(cacertfile, ClientOpts), CertFile = proplists:get_value(certfile, ClientOpts), KeyFile = proplists:get_value(keyfile, ClientOpts), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, ServerOpts}]), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ - " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -ssl3 -msg", + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile + ++ " -key " ++ KeyFile ++ " -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), port_command(OpenSslPort, Data), - + ssl_test_lib:check_result(Server, ok), - + %% Clean close down! Server needs to be closed first !! ssl_test_lib:close(Server), close_port(OpenSslPort), process_flag(trap_exit, false), ok. - %%-------------------------------------------------------------------- erlang_server_openssl_client_reuse_session(doc) -> @@ -464,8 +475,8 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> {reconnect_times, 5}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost -reconnect", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -501,8 +512,9 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -552,8 +564,8 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -600,10 +612,10 @@ erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config {from, self()}, {mfa, {ssl_test_lib, trigger_renegotiate, [[Data, N+2]]}}, - {options, [{renegotiate_at, N} | ServerOpts]}]), + {options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -639,8 +651,8 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -667,85 +679,11 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> ok. %%-------------------------------------------------------------------- -ssl3_erlang_client_openssl_server(doc) -> - ["Test erlang client with openssl server"]; -ssl3_erlang_client_openssl_server(suite) -> - []; -ssl3_erlang_client_openssl_server(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ - " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -ssl3", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - wait_for_openssl_server(), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - connection_info, [sslv3]}}, - {options, - [{versions, [sslv3]} | ClientOpts]}]), - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- - -ssl3_erlang_server_openssl_client(doc) -> - ["Test erlang server with openssl client"]; -ssl3_erlang_server_openssl_client(suite) -> - []; -ssl3_erlang_server_openssl_client(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_opts, Config), - - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, - {?MODULE, connection_info, [sslv3]}}, - {options, - [{versions, [sslv3]} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ - " -host localhost -ssl3", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - ssl_test_lib:check_result(Server, ok), - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- -ssl3_erlang_client_openssl_server_client_cert(doc) -> +erlang_client_openssl_server_client_cert(doc) -> ["Test erlang client with openssl server when client sends cert"]; -ssl3_erlang_client_openssl_server_client_cert(suite) -> +erlang_client_openssl_server_client_cert(suite) -> []; -ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> +erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), ClientOpts = ?config(client_verification_opts, Config), @@ -758,10 +696,10 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> CertFile = proplists:get_value(certfile, ServerOpts), CaCertFile = proplists:get_value(cacertfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3", + ++ " -key " ++ KeyFile ++ " -Verify 2", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -787,11 +725,11 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ssl3_erlang_server_openssl_client_client_cert(doc) -> +erlang_server_openssl_client_client_cert(doc) -> ["Test erlang server with openssl client when client sends cert"]; -ssl3_erlang_server_openssl_client_client_cert(suite) -> +erlang_server_openssl_client_client_cert(suite) -> []; -ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> +erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), ClientOpts = ?config(client_verification_opts, Config), @@ -812,10 +750,10 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> CaCertFile = proplists:get_value(cacertfile, ClientOpts), CertFile = proplists:get_value(certfile, ClientOpts), KeyFile = proplists:get_value(keyfile, ClientOpts), - + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ - " -host localhost -ssl3", + ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -host localhost", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -833,15 +771,15 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ssl3_erlang_server_erlang_client_client_cert(doc) -> +erlang_server_erlang_client_client_cert(doc) -> ["Test erlang server with erlang client when client sends cert"]; -ssl3_erlang_server_erlang_client_client_cert(suite) -> +erlang_server_erlang_client_client_cert(suite) -> []; -ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> +erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), ClientOpts = ?config(client_verification_opts, Config), - + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Data = "From erlang to erlang", @@ -863,7 +801,7 @@ ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> %% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast {mfa, {ssl, send, [Data]}}, {options, - [{versions, [sslv3]} | ClientOpts]}]), + [{versions, [Version]} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -871,215 +809,8 @@ ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> ssl_test_lib:close(Client), process_flag(trap_exit, false), ok. - - -%%-------------------------------------------------------------------- - -tls1_erlang_client_openssl_server(doc) -> - ["Test erlang client with openssl server"]; -tls1_erlang_client_openssl_server(suite) -> - []; -tls1_erlang_client_openssl_server(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), - - - test_server:format("Server Opts", [ServerOpts]), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ - " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -tls1", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - wait_for_openssl_server(), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - connection_info, [tlsv1]}}, - {options, - [{versions, [tlsv1]} | ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- - -tls1_erlang_server_openssl_client(doc) -> - ["Test erlang server with openssl client"]; -tls1_erlang_server_openssl_client(suite) -> - []; -tls1_erlang_server_openssl_client(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_opts, Config), - - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, - {?MODULE, connection_info, [tlsv1]}}, - {options, - [{versions, [tlsv1]} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ - " -host localhost -tls1", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- - -tls1_erlang_client_openssl_server_client_cert(doc) -> - ["Test erlang client with openssl server when client sends cert"]; -tls1_erlang_client_openssl_server_client_cert(suite) -> - []; -tls1_erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_verification_opts, Config), - ClientOpts = ?config(client_verification_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ - " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - wait_for_openssl_server(), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, ClientOpts}]), - port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. - %%-------------------------------------------------------------------- -tls1_erlang_server_openssl_client_client_cert(doc) -> - ["Test erlang server with openssl client when client sends cert"]; -tls1_erlang_server_openssl_client_client_cert(suite) -> - []; -tls1_erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_verification_opts, Config), - ClientOpts = ?config(client_verification_opts, Config), - - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, - [{verify , verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - CaCertFile = proplists:get_value(cacertfile, ClientOpts), - CertFile = proplists:get_value(certfile, ClientOpts), - KeyFile = proplists:get_value(keyfile, ClientOpts), - - Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ - " -host localhost -tls1", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- -tls1_erlang_server_erlang_client_client_cert(doc) -> - ["Test erlang server with erlang client when client sends cert"]; -tls1_erlang_server_erlang_client_client_cert(suite) -> - []; -tls1_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_verification_opts, Config), - ClientOpts = ?config(client_verification_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, - [{verify , verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl, send, [Data]}}, - {options, - [{versions, [tlsv1]} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - process_flag(trap_exit, false), - ok. -%%-------------------------------------------------------------------- - ciphers_rsa_signed_certs(doc) -> ["Test cipher suites that uses rsa certs"]; @@ -1186,12 +917,6 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> process_flag(trap_exit, false), Return. - -version_flag(tlsv1) -> - " -tls1 "; -version_flag(sslv3) -> - " -ssl3 ". - %%-------------------------------------------------------------------- erlang_client_bad_openssl_server(doc) -> [""]; @@ -1207,8 +932,8 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -1222,7 +947,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> {from, self()}, {mfa, {?MODULE, server_sent_garbage, []}}, {options, - [{versions, [tlsv1]} | ClientOpts]}]), + [{versions, [Version]} | ClientOpts]}]), %% Send garbage port_command(OpensslPort, ?OPENSSL_GARBAGE), @@ -1241,7 +966,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> {from, self()}, {mfa, {ssl_test_lib, no_result_msg, []}}, {options, - [{versions, [tlsv1]} | ClientOpts]}]), + [{versions, [Version]} | ClientOpts]}]), %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), @@ -1267,8 +992,8 @@ expired_session(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -1433,21 +1158,46 @@ wait_for_openssl_server() -> %% more so than sleep!) test_server:sleep(?SLEEP) end. - + +version_flag(tlsv1) -> + " -tls1 "; +version_flag('tlsv1.1') -> + " -tls1_1 "; +version_flag('tlsv1.2') -> + " -tls1_2 "; +version_flag(sslv3) -> + " -ssl3 ". + check_sane_openssl_renegotaite(Config) -> case os:cmd("openssl version") of "OpenSSL 0.9.8" ++ _ -> - {skip, "Known renegotiation bug in OppenSSL"}; + {skip, "Known renegotiation bug in OpenSSL"}; "OpenSSL 0.9.7" ++ _ -> - {skip, "Known renegotiation bug in OppenSSL"}; + {skip, "Known renegotiation bug in OpenSSL"}; _ -> Config end. check_sane_openssl_sslv2(Config) -> case os:cmd("openssl version") of - "OpenSSL 1.0.0" ++ _ -> + "OpenSSL 1." ++ _ -> {skip, "sslv2 by default turned of in 1.*"}; _ -> Config end. + +check_sane_openssl_version(Version) -> + case {Version, os:cmd("openssl version")} of + {_, "OpenSSL 1.0.1" ++ _} -> + true; + {'tlsv1.2', "OpenSSL 1.0" ++ _} -> + false; + {'tlsv1.1', "OpenSSL 1.0" ++ _} -> + false; + {'tlsv1.2', "OpenSSL 0" ++ _} -> + false; + {'tlsv1.1', "OpenSSL 0" ++ _} -> + false; + {_, _} -> + true + end. diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 0fccbfe908..e381b73c27 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 5.0.1 +SSL_VSN = 5.1 diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile index 6c92756ae7..c3138c4d9f 100644 --- a/lib/stdlib/doc/src/Makefile +++ b/lib/stdlib/doc/src/Makefile @@ -173,15 +173,15 @@ $(SPECDIR)/specs_erl_id_trans.xml: 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 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index a7e010a05f..b7b5d497d0 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -53,7 +53,7 @@ <item>Basically the same as UTF-32, but without some Unicode semantics, defined by IEEE and has little use as a separate encoding standard. For all normal (and possibly abnormal) usages, UTF-32 and UCS-4 are interchangeable.</item> </taglist> <p>Certain ranges of characters are left unused and certain ranges are even deemed invalid. The most notable invalid range is 16#D800 - 16#DFFF, as the UTF-16 encoding does not allow for encoding of these numbers. It can be speculated that the UTF-16 encoding standard was, from the beginning, expected to be able to hold all Unicode characters in one 16-bit entity, but then had to be extended, leaving a hole in the Unicode range to cope with backward compatibility.</p> -<p>Additionally, the codepoint 16#FEFF is used for byte order marks (BOM's) and use of that character is not encouraged in other contexts than that. It actually is valid though, as the character "ZWNBS" (Zero Width Non Breaking Space). BOM's are used to identify encodings and byte order for programs where such parameters are not known in advance. Byte order marks are more seldom used than one could expect, put their use is becoming more widely spread as they provide the means for programs to make educated guesses about the Unicode format of a certain file.</p> +<p>Additionally, the codepoint 16#FEFF is used for byte order marks (BOM's) and use of that character is not encouraged in other contexts than that. It actually is valid though, as the character "ZWNBS" (Zero Width Non Breaking Space). BOM's are used to identify encodings and byte order for programs where such parameters are not known in advance. Byte order marks are more seldom used than one could expect, but their use is becoming more widely spread as they provide the means for programs to make educated guesses about the Unicode format of a certain file.</p> </section> <section> <title>Standard Unicode representation in Erlang</title> @@ -210,6 +210,12 @@ Eshell V5.7 (abort with ^G) </section> </section> <section> +<title>Unicode in environment variables and parameters</title> +<p>Environment variables and their interpretation is handled much in the same way as file names. If Unicode file names are enabled, environment variables as well as parameters to the Erlang VM are expected to be in Unicode.</p> +<p>If Unicode file names are enabled, the calls to <seealso marker="kernel:os#os_getenv/0"><c>os:getenv/0</c></seealso>, <seealso marker="kernel:os#os_getenv/1"><c>os:getenv/1</c></seealso> and <seealso marker="kernel:os#os_putenv/2"><c>os:putenv/2</c></seealso> will handle Unicode strings. On Unix-like platforms, the built-in functions will translate environment variables in UTF-8 to/from Unicode strings, possibly with codepoints > 255. On Windows the Unicode versions of the environment system API will be used, also allowing for codepoints > 255.</p> +<p>On Unix-like operating systems, parameters are expected to be UTF-8 without translation if Unicode file names are enabled.</p> +</section> +<section> <title>Unicode-aware modules</title> <p>Most of the modules in Erlang/OTP are of course Unicode-unaware in the sense that they have no notion of Unicode and really shouldn't have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p> <p>Modules that actually handle textual data (like <c>io_lib</c>, <c>string</c> etc) are sometimes subject to conversion or extension to be able to handle Unicode characters.</p> diff --git a/lib/stdlib/examples/Makefile b/lib/stdlib/examples/Makefile index 25c88d1de3..198aceb3a9 100644 --- a/lib/stdlib/examples/Makefile +++ b/lib/stdlib/examples/Makefile @@ -47,6 +47,6 @@ RELSYSDIR = $(RELEASE_PATH)/lib/stdlib-$(STDLIB_VSN)/examples EXAMPLES = erl_id_trans.erl release_spec: - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(EXAMPLES) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EXAMPLES) "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile index 90e239b00f..54186a3ba7 100644 --- a/lib/stdlib/src/Makefile +++ b/lib/stdlib/src/Makefile @@ -167,6 +167,7 @@ docs: # This is a trick so that the preloaded files will get the correct type # specifications. primary_bootstrap_compiler: \ + $(BOOTSTRAP_COMPILER)/ebin/epp.beam \ $(BOOTSTRAP_COMPILER)/ebin/erl_scan.beam \ $(BOOTSTRAP_COMPILER)/ebin/erl_parse.beam \ $(BOOTSTRAP_COMPILER)/ebin/erl_lint.beam \ @@ -198,13 +199,13 @@ $(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) erl_parse.yrl $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) erl_parse.yrl "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index abab81d31f..648ff349a4 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -248,6 +248,8 @@ format_error({illegal_guard_local_call, {F,A}}) -> io_lib:format("call to local/imported function ~w/~w is illegal in guard", [F,A]); format_error(illegal_guard_expr) -> "illegal guard expression"; +format_error(deprecated_tuple_fun) -> + "tuple funs are deprecated and will be removed in R16"; %% --- exports --- format_error({explicit_export,F,A}) -> io_lib:format("in this release, the call to ~w/~w must be written " @@ -1914,7 +1916,8 @@ gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, Vt, St0) -> true -> {Asvt,St1}; false -> {Asvt,add_error(Line, illegal_guard_expr, St1)} end; -gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, Vt, St) -> +gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, Vt, St0) -> + St = add_warning(L, deprecated_tuple_fun, St0), gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, Vt, St); gexpr({op,Line,Op,A}, Vt, St0) -> {Avt,St1} = gexpr(A, Vt, St0), diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index 27e70ac4d4..498d850df3 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -22,7 +22,7 @@ -export([script_name/0, create/2, extract/2]). %% Internal API. --export([start/0, start/1]). +-export([start/0, start/1, parse_file/1]). %%----------------------------------------------------------------------- @@ -346,7 +346,8 @@ parse_and_run(File, Args, Options) -> case Source of archive -> {ok, FileInfo} = file:read_file_info(File), - case code:set_primary_archive(File, FormsOrBin, FileInfo) of + case code:set_primary_archive(File, FormsOrBin, FileInfo, + fun escript:parse_file/1) of ok when CheckOnly -> case code:load_file(Module) of {module, _} -> @@ -396,6 +397,19 @@ parse_and_run(File, Args, Options) -> %% Parse script %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Only used as callback by erl_prim_loader +parse_file(File) -> + try parse_file(File, false) of + {_Source, _Module, FormsOrBin, _HasRecs, _Mode} + when is_binary(FormsOrBin) -> + {ok, FormsOrBin}; + _ -> + {error, no_archive_bin} + catch + throw:Reason -> + {error, Reason} + end. + parse_file(File, CheckOnly) -> {HeaderSz, NextLineNo, Fd, Sections} = parse_header(File, false), diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl index d532cea187..b098d4cb91 100644 --- a/lib/stdlib/src/filelib.erl +++ b/lib/stdlib/src/filelib.erl @@ -264,6 +264,9 @@ ensure_dir(F) -> case do_is_dir(Dir, file) of true -> ok; + false when Dir =:= F -> + %% Protect against infinite loop + {error,einval}; false -> ensure_dir(Dir), case file:make_dir(Dir) of diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index dbfcbea4f7..870af4e95f 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -726,6 +726,8 @@ nativename(Name0) -> _ -> Name end. +win32_nativename(Name) when is_binary(Name) -> + binary:replace(Name, <<"/">>, <<"\\">>, [global]); win32_nativename([$/|Rest]) -> [$\\|win32_nativename(Rest)]; win32_nativename([C|Rest]) -> diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index 59c6d240ba..04308a51b7 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -270,7 +270,7 @@ enter_loop(Mod, Options, State) -> enter_loop(Mod, Options, State, self(), infinity). enter_loop(Mod, Options, State, ServerName = {Scope, _}) - when Scope == local; Scope == local -> + when Scope == local; Scope == global -> enter_loop(Mod, Options, State, ServerName, infinity); enter_loop(Mod, Options, State, ServerName = {via, _, _}) -> diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl index 314fd60903..cf4b87d7eb 100644 --- a/lib/stdlib/src/lib.erl +++ b/lib/stdlib/src/lib.erl @@ -1,7 +1,7 @@ %% %% %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 @@ -209,7 +209,7 @@ explain_reason(badarg, error, [], _PF, _S) -> explain_reason({badarg,V}, error=Cl, [], PF, S) -> % orelse, andalso format_value(V, <<"bad argument: ">>, Cl, PF, S); explain_reason(badarith, error, [], _PF, _S) -> - <<"bad argument in an arithmetic expression">>; + <<"an error occurred when evaluating an arithmetic expression">>; explain_reason({badarity,{Fun,As}}, error, [], _PF, _S) when is_function(Fun) -> %% Only the arity is displayed, not the arguments As. diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl index c82c8159b6..fd480726a7 100644 --- a/lib/stdlib/src/zip.erl +++ b/lib/stdlib/src/zip.erl @@ -1017,7 +1017,7 @@ cd_file_header_from_lh_and_pos(LH, Pos) -> file_name_length = FileNameLength, extra_field_length = ExtraFieldLength, file_comment_length = 0, % FileCommentLength, - disk_num_start = 1, % DiskNumStart, + disk_num_start = 0, % DiskNumStart, internal_attr = 0, % InternalAttr, external_attr = 0, % ExternalAttr, local_header_offset = Pos}. diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile index 4de6ea3ee7..29b8e28d3a 100644 --- a/lib/stdlib/test/Makefile +++ b/lib/stdlib/test/Makefile @@ -135,10 +135,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) stdlib.spec $(EMAKEFILE) \ - $(ERL_FILES) $(COVERFILE) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(ERL_FILES) $(COVERFILE) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index dcd622b984..9f9d97b619 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1331,17 +1331,20 @@ guard(Config) when is_list(Config) -> foo. ">>, [warn_unused_vars, nowarn_obsolete_guard], - {errors,[{2,erl_lint,illegal_guard_expr}, - {4,erl_lint,illegal_guard_expr}, - {6,erl_lint,illegal_guard_expr}, - {8,erl_lint,illegal_guard_expr}, - {10,erl_lint,illegal_guard_expr}, - {12,erl_lint,illegal_guard_expr}, - {14,erl_lint,illegal_guard_expr}, - {16,erl_lint,illegal_guard_expr}, - {18,erl_lint,illegal_guard_expr}, - {20,erl_lint,illegal_guard_expr}], - []}}, + {error,[{2,erl_lint,illegal_guard_expr}, + {4,erl_lint,illegal_guard_expr}, + {6,erl_lint,illegal_guard_expr}, + {8,erl_lint,illegal_guard_expr}, + {10,erl_lint,illegal_guard_expr}, + {12,erl_lint,illegal_guard_expr}, + {14,erl_lint,illegal_guard_expr}, + {16,erl_lint,illegal_guard_expr}, + {18,erl_lint,illegal_guard_expr}, + {20,erl_lint,illegal_guard_expr}], + [{8,erl_lint,deprecated_tuple_fun}, + {14,erl_lint,deprecated_tuple_fun}, + {20,erl_lint,deprecated_tuple_fun}, + {28,erl_lint,deprecated_tuple_fun}]}}, {guard6, <<"-record(apa,{a=a,b=foo:bar()}). apa() -> diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl index 9f95df062b..38c085616d 100644 --- a/lib/stdlib/test/escript_SUITE.erl +++ b/lib/stdlib/test/escript_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -29,6 +29,7 @@ module_script/1, beam_script/1, archive_script/1, + archive_script_file_access/1, epp/1, create_and_extract/1, foldl/1, @@ -44,7 +45,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, errors, strange_name, emulator_flags, module_script, beam_script, archive_script, epp, - create_and_extract, foldl, overflow]. + create_and_extract, foldl, overflow, + archive_script_file_access]. groups() -> []. @@ -62,7 +64,7 @@ end_per_group(_GroupName, Config) -> Config. init_per_testcase(_Case, Config) -> - ?line Dog = ?t:timetrap(?t:minutes(1)), + ?line Dog = ?t:timetrap(?t:minutes(2)), [{watchdog,Dog}|Config]. end_per_testcase(_Case, Config) -> @@ -356,7 +358,7 @@ beam_script(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Create an archive file containing two entire applications plus two %% alternate main modules. Generate a new escript containing the archive -%% (with .app and .beam files and ) and the escript header. +%% (with .app and .beam files and) and the escript header. archive_script(Config) when is_list(Config) -> %% Copy the orig files to priv_dir @@ -464,6 +466,147 @@ archive_script(Config) when is_list(Config) -> ok. +%% Test the correction of OTP-10071 +%% The errors identified are +%% +%% a) If primary archive was named "xxx", then a file in the same +%% directory named "xxxyyy" would be interpreted as a file named yyy +%% inside the archive. +%% +%% b) erl_prim_loader did not correctly create and normalize absolute +%% paths for primary archive and files inside it, so unless given +%% with exact same path files inside the archive would not be +%% found. E.g. if escript was started as ./xxx then "xxx/file" +%% would not be found since erl_prim_loader would try to match +%% /full/path/to/xxx with /full/path/to/./xxx. Same problem with +%% ../. Also, the use of symlinks in the path to the archive would +%% cause problems. +%% +%% c) Depending on how the primary archive was built, +%% erl_prim_loader:list_dir/1 would sometimes return an empty string +%% inside the file list. This was a virtual element representing the +%% top directory of the archive. This shall not occur. +%% +archive_script_file_access(Config) when is_list(Config) -> + %% Copy the orig files to priv_dir + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + + MainMod = "archive_script_file_access", + MainSrc = MainMod ++ ".erl", + MainBeam = MainMod ++ ".beam", + + Archive = filename:join([PrivDir, "archive_script_file_access.zip"]), + ?line {ok, _} = zip:create(Archive, ["archive_script_file_access"], + [{compress, []}, {cwd, DataDir}]), + ?line {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]), + TopDir = filename:join([PrivDir, "archive_script_file_access"]), + + %% Compile the code + ?line ok = compile_files([MainSrc], TopDir, TopDir), + + %% First, create a file structure which will be included in the archive: + %% + %% dir1/ + %% dir1/subdir1/ + %% dir1/subdir1/file1 + %% + {ok, OldDir} = file:get_cwd(), + ok = file:set_cwd(TopDir), + DummyDir = "dir1", + DummySubDir = filename:join(DummyDir, "subdir1"), + RelDummyFile = filename:join(DummySubDir, "file1"), + DummyFile = filename:join(TopDir,RelDummyFile), + ok = filelib:ensure_dir(DummyFile), + ok = file:write_file(DummyFile, ["foo\nbar\nbaz"]), + + %% 1. Create zip archive by adding the dummy file and the beam + %% file as binaries to zip. + %% + %% This used to provoke the following issues when the script was run as + %% "./<script_name>": + %% a. erl_prim_loader:read_file_info/1 returning 'error' + %% b. erl_prim_loader:list_dir/1 returning {ok, ["dir1", [], "file1"]} + %% leading to an infinite loop in reltool_target:spec_dir/1 + Files1 = + lists:map(fun(Filename) -> + {ok, Bin} = file:read_file(Filename), + {Filename,Bin} + end, + [RelDummyFile,MainBeam]), + {ok, {"mem", Bin1}} = zip:create("mem", Files1, [memory]), + + %% Create the escript + ScriptName1 = "archive_script_file_access1", + Script1 = filename:join([PrivDir, ScriptName1]), + Flags = "-escript main " ++ MainMod, + ok = escript:create(Script1,[shebang,{emu_args,Flags},{archive,Bin1}]), + ok = file:change_mode(Script1,8#00744), + + %% If supported, create a symlink to the script. This is used to + %% test error b) described above this test case. + SymlinkName1 = "symlink_to_"++ScriptName1, + Symlink1 = filename:join([PrivDir, SymlinkName1]), + file:make_symlink(ScriptName1,Symlink1), % will fail if not supported + + %% Also add a dummy file in the same directory with the same name + %% as the script except is also has an extension. This used to + %% test error a) described above this test case. + ok = file:write_file(Script1 ++ ".extension", + <<"same name as script, but with extension">>), + + %% Change to script's directory and run it as "./<script_name>" + ok = file:set_cwd(PrivDir), + run(PrivDir, "./" ++ ScriptName1 ++ " " ++ ScriptName1, + [<<"ExitCode:0">>]), + ok = file:set_cwd(TopDir), + + + %% 2. Create zip archive by letting zip read the files from the file system + %% + %% The difference compared to the archive_script_file_access1 is + %% that this will have a file element for each directory in the + %% archive - while archive_script_file_access1 will only have a + %% file element per regular file. + Files2 = [DummyDir,MainBeam], + {ok, {"mem", Bin2}} = zip:create("mem", Files2, [memory]), + + %% Create the escript + ScriptName2 = "archive_script_file_access2", + Script2 = filename:join([PrivDir, ScriptName2]), + ok = escript:create(Script2,[shebang,{emu_args,Flags},{archive,Bin2}]), + ok = file:change_mode(Script2,8#00744), + + %% Also add a dummy file in the same directory with the same name + %% as the script except is also has an extension. This used to + %% test error a) described above this test case. + ok = file:write_file(Script2 ++ ".extension", + <<"same name as script, but with extension">>), + + %% If supported, create a symlink to the script. This is used to + %% test error b) described above this test case. + SymlinkName2 = "symlink_to_"++ScriptName2, + Symlink2 = filename:join([PrivDir, SymlinkName2]), + file:make_symlink(ScriptName2,Symlink2), % will fail if not supported + + %% Change to script's directory and run it as "./<script_name>" + ok = file:set_cwd(PrivDir), + run(PrivDir, "./" ++ ScriptName2 ++ " " ++ ScriptName2, + [<<"ExitCode:0">>]), + + %% 3. If symlinks are supported, run one of the scripts via a symlink. + %% + %% This is in order to test error b) described above this test case. + case file:read_link(Symlink2) of + {ok,_} -> + run(PrivDir, "./" ++ SymlinkName2 ++ " " ++ ScriptName2, + [<<"ExitCode:0">>]); + _ -> % not supported + ok + end, + ok = file:set_cwd(OldDir). + + compile_app(TopDir, AppName) -> AppDir = filename:join([TopDir, AppName]), SrcDir = filename:join([AppDir, "src"]), diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl b/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl index de56579998..431a51b0e5 100644 --- a/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl +++ b/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl @@ -21,6 +21,8 @@ -export([main/1]). +-include_lib("kernel/include/file.hrl"). + -define(DUMMY, archive_script_dummy). -define(DICT, archive_script_dict). @@ -32,7 +34,7 @@ main(MainArgs) -> io:format("dummy:~p\n",[[E || E <- ErlArgs, element(1, E) =:= ?DUMMY]]), %% Start the applications - {error, {not_started, ?DICT}} = application:start(archive_script_dummy), + {error, {not_started, ?DICT}} = application:start(?DUMMY), ok = application:start(?DICT), ok = application:start(?DUMMY), @@ -57,4 +59,17 @@ main(MainArgs) -> ok = ?DICT:erase(Tab, Key), error = ?DICT:find(Tab, Key), ok = ?DICT:erase(Tab), + + %% Check mtime related caching bug with escript/primary archive files + Escript = escript:script_name(), + {ok, FileInfo} = file:read_file_info(Escript), + %% Modify mtime of archive file and try to reload module + FileInfo2 = FileInfo#file_info{mtime=calendar:now_to_local_time(now())}, + ok = file:write_file_info(Escript, FileInfo2), + Module = ?DICT, + {file, _} = code:is_loaded(Module), + true = code:delete(Module), + false = code:is_loaded(Module), + {module, Module} = code:ensure_loaded(Module), + ok. diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl new file mode 100644 index 0000000000..b03c8ba70d --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl @@ -0,0 +1,105 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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 +%% 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(archive_script_file_access). +-behaviour(escript). + +-export([main/1]). + +-include_lib("kernel/include/file.hrl"). + +main([RelArchiveFile]) -> + + AbsArchiveFile = filename:absname(RelArchiveFile), + DotSlashArchiveFile = "./" ++ RelArchiveFile, + + Beam = atom_to_list(?MODULE) ++ ".beam", + AbsBeam = filename:join(AbsArchiveFile,Beam), + RelBeam = filename:join(RelArchiveFile,Beam), + DotSlashBeam = filename:join(DotSlashArchiveFile,Beam), + Dir = "dir1", + AbsDir = filename:join(AbsArchiveFile,Dir), + RelDir = filename:join(RelArchiveFile,Dir), + DotSlashDir = filename:join(DotSlashArchiveFile,Dir), + SubDir = "subdir1", + AbsSubDir = filename:join(AbsDir,SubDir), + RelSubDir = filename:join(RelDir,SubDir), + DotSlashSubDir = filename:join(DotSlashDir,SubDir), + + {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(RelArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(DotSlashArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile ++ "/"), + {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile ++ "/."), + {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsDir,".."])), + {ok,List1} = erl_prim_loader:list_dir(filename:join([RelDir,".."])), + {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashDir,".."])), + {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsSubDir,"..",".."])), + {ok,List1} = erl_prim_loader:list_dir(filename:join([RelSubDir,"..",".."])), + {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashSubDir,"..",".."])), + false = lists:member([],List1), + + %% If symlinks are supported on this platform... + RelSymlinkArchiveFile = "symlink_to_" ++ RelArchiveFile, + case file:read_link(RelSymlinkArchiveFile) of + {ok,_} -> + DotSlashSymlinkArchiveFile = "./" ++ RelSymlinkArchiveFile, + AbsSymlinkArchiveFile=filename:join(filename:dirname(AbsArchiveFile), + RelSymlinkArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(AbsSymlinkArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(RelSymlinkArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(DotSlashSymlinkArchiveFile); + _ -> % not supported + ok + end, + + + {ok,List2} = erl_prim_loader:list_dir(AbsDir), + {ok,List2} = erl_prim_loader:list_dir(RelDir), + {ok,List2} = erl_prim_loader:list_dir(DotSlashDir), + false = lists:member([],List2), + + error = erl_prim_loader:list_dir(AbsBeam), + error = erl_prim_loader:list_dir(RelBeam), + error = erl_prim_loader:list_dir(DotSlashBeam), + + error = erl_prim_loader:get_file(AbsArchiveFile), + error = erl_prim_loader:get_file(RelArchiveFile), + error = erl_prim_loader:get_file(DotSlashArchiveFile), + error = erl_prim_loader:get_file(AbsArchiveFile ++ "/"), + error = erl_prim_loader:get_file(AbsArchiveFile ++ "/."), + {ok,Bin,AbsBeam} = erl_prim_loader:get_file(AbsBeam), + {ok,Bin,RelBeam} = erl_prim_loader:get_file(RelBeam), + {ok,Bin,DotSlashBeam} = erl_prim_loader:get_file(DotSlashBeam), + + {ok,#file_info{type=directory}=DFI} = + erl_prim_loader:read_file_info(AbsArchiveFile), + {ok,DFI} = erl_prim_loader:read_file_info(RelArchiveFile), + {ok,DFI} = erl_prim_loader:read_file_info(DotSlashArchiveFile), + {ok,DFI} = erl_prim_loader:read_file_info(AbsArchiveFile ++ "/"), + {ok,DFI} = erl_prim_loader:read_file_info(AbsArchiveFile ++ "/."), + {ok,#file_info{type=regular}=RFI} = erl_prim_loader:read_file_info(AbsBeam), + {ok,RFI} = erl_prim_loader:read_file_info(RelBeam), + {ok,RFI} = erl_prim_loader:read_file_info(DotSlashBeam), + + F = AbsArchiveFile ++ ".extension", + error = erl_prim_loader:list_dir(F), + {ok,_,_} = erl_prim_loader:get_file(F), + {ok,#file_info{type=regular}} = erl_prim_loader:read_file_info(F), + + ok. diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 954d19a46f..95f10b1df3 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -74,6 +74,7 @@ -export([bad_table/1, types/1]). -export([otp_9932/1]). -export([otp_9423/1]). +-export([otp_10182/1]). -export([init_per_testcase/2, end_per_testcase/2]). %% Convenience for manual testing @@ -146,6 +147,7 @@ all() -> exit_many_large_table_owner, exit_many_tables_owner, exit_many_many_tables_owner, write_concurrency, heir, give_away, setopts, bad_table, types, + otp_10182, otp_9932, otp_9423]. @@ -715,30 +717,17 @@ adjust_xmem([T1,T2,T3,T4], {A0,B0,C0,D0} = _Mem0) -> TabDiff = ?TAB_STRUCT_SZ, Mem1 = {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff}, - Mem2 = case {erlang:system_info({wordsize,internal}),erlang:system_info({wordsize,external})} of - %% Halfword, corrections for regular pointers occupying two internal words. - {4,8} -> - {A1,B1,C1,D1} = Mem1, - {A1+4*ets:info(T1, size)+?DB_TREE_STACK_NEED, - B1+3*ets:info(T2, size)+?DB_HASH_SIZEOF_EXTSEG, - C1+3*ets:info(T3, size)+?DB_HASH_SIZEOF_EXTSEG, - D1+3*ets:info(T4, size)+?DB_HASH_SIZEOF_EXTSEG}; - _ -> - Mem1 - end, - - %% Adjust for hybrid and shared heaps: - %% Each record is one word smaller. - %%Mem2 = case erlang:system_info(heap_type) of - %% private -> - %% Mem1; - %% _ -> - %% {A1,B1,C1,D1} = Mem1, - %% {A1-ets:info(T1, size),B1-ets:info(T2, size), - %% C1-ets:info(T3, size),D1-ets:info(T4, size)} - %% end, - %%{Mem2,{ets:info(T1,stats),ets:info(T2,stats),ets:info(T3,stats),ets:info(T4,stats)}}. - Mem2. + case {erlang:system_info({wordsize,internal}),erlang:system_info({wordsize,external})} of + %% Halfword, corrections for regular pointers occupying two internal words. + {4,8} -> + {A1,B1,C1,D1} = Mem1, + {A1+4*ets:info(T1, size)+?DB_TREE_STACK_NEED, + B1+3*ets:info(T2, size)+?DB_HASH_SIZEOF_EXTSEG, + C1+3*ets:info(T3, size)+?DB_HASH_SIZEOF_EXTSEG, + D1+3*ets:info(T4, size)+?DB_HASH_SIZEOF_EXTSEG}; + _ -> + Mem1 + end. t_whitebox(doc) -> ["Diverse whitebox testes"]; @@ -1037,6 +1026,8 @@ t_test_ms(Config) when is_list(Config) -> [{{'$1','$2'},[{'<','$1','$2'}],['$$']}]), ?line {ok,false} = ets:test_ms({a,b}, [{{'$1','$2'},[{'>','$1','$2'}],['$$']}]), + Tpl = {a,gb_sets:new()}, + ?line {ok,Tpl} = ets:test_ms(Tpl, [{{'_','_'}, [], ['$_']}]), % OTP-10190 ?line {error,[{error,String}]} = ets:test_ms({a,b}, [{{'$1','$2'}, [{'flurp','$1','$2'}], @@ -5483,6 +5474,20 @@ otp_9423(Config) when is_list(Config) -> Skipped -> Skipped end. + +%% Corrupted binary in compressed table +otp_10182(Config) when is_list(Config) -> + Bin = <<"aHR0cDovL2hvb3RzdWl0ZS5jb20vYy9wcm8tYWRyb2xsLWFi">>, + Key = {test, Bin}, + Value = base64:decode(Bin), + In = {Key,Value}, + Db = ets:new(undefined, [set, protected, {read_concurrency, true}, compressed]), + ets:insert(Db, In), + [Out] = ets:lookup(Db, Key), + io:format("In : ~p\nOut: ~p\n", [In,Out]), + ets:delete(Db), + In = Out. + diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl index 4cfa589660..99516c0c04 100644 --- a/lib/stdlib/test/filename_SUITE.erl +++ b/lib/stdlib/test/filename_SUITE.erl @@ -25,7 +25,7 @@ -export([pathtype/1,rootname/1,split/1,find_src/1]). -export([absname_bin/1, absname_bin_2/1, basename_bin_1/1, basename_bin_2/1, - dirname_bin/1, extension_bin/1, join_bin/1]). + dirname_bin/1, extension_bin/1, join_bin/1, t_nativename_bin/1]). -export([pathtype_bin/1,rootname_bin/1,split_bin/1]). -include_lib("test_server/include/test_server.hrl"). @@ -38,7 +38,7 @@ all() -> join, pathtype, rootname, split, t_nativename, find_src, absname_bin, absname_bin_2, basename_bin_1, basename_bin_2, dirname_bin, extension_bin, - join_bin, pathtype_bin, rootname_bin, split_bin]. + join_bin, pathtype_bin, rootname_bin, split_bin, t_nativename_bin]. groups() -> []. @@ -804,3 +804,14 @@ split_bin(Config) when is_list(Config) -> ok end. +t_nativename_bin(Config) when is_list(Config) -> + ?line <<"abcedf">> = filename:nativename(<<"abcedf">>), + case os:type() of + {win32, _} -> + ?line <<"a:\\temp\\arne.exe">> = + filename:nativename(<<"A:/temp//arne.exe/">>); + _ -> + ?line <<"/usr/tmp/arne">> = + filename:nativename(<<"/usr/tmp//arne/">>) + end. + diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl index cdf15ba017..48ef7e55ed 100644 --- a/lib/stdlib/test/gen_server_SUITE.erl +++ b/lib/stdlib/test/gen_server_SUITE.erl @@ -37,7 +37,8 @@ % spawn export -export([spec_init_local/2, spec_init_global/2, spec_init_via/2, - spec_init_default_timeout/2, spec_init_anonymous/1, + spec_init_default_timeout/2, spec_init_global_default_timeout/2, + spec_init_anonymous/1, spec_init_anonymous_default_timeout/1, spec_init_not_proc_lib/1, cast_fast_messup/0]). @@ -749,7 +750,7 @@ spec_init(suite) -> spec_init(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), - + ?line {ok, Pid0} = start_link(spec_init_local, [{ok, my_server}, []]), ?line ok = gen_server:call(Pid0, started_p), ?line ok = gen_server:call(Pid0, stop), @@ -819,6 +820,14 @@ spec_init(Config) when is_list(Config) -> test_server:fail(gen_server_did_not_die) end, + %% Before the OTP-10130 fix this failed because a timeout message + %% was generated as the spawned process crashed because a {global, Name} + %% was matched as a timeout value instead of matching on scope. + {ok, _PidHurra} = + start_link(spec_init_global_default_timeout, [{ok, hurra}, []]), + timer:sleep(1000), + ok = gen_server:call(_PidHurra, started_p), + ?line Pid5 = erlang:spawn_link(?MODULE, spec_init_not_proc_lib, [[]]), receive @@ -1125,6 +1134,15 @@ spec_init_default_timeout({ok, Name}, Options) -> %% Supervised init can occur here ... gen_server:enter_loop(?MODULE, Options, {}, {local, Name}). +%% OTP-10130, A bug was introduced where global scope was not matched when +%% enter_loop/4 was called (no timeout). +spec_init_global_default_timeout({ok, Name}, Options) -> + process_flag(trap_exit, true), + global:register_name(Name, self()), + proc_lib:init_ack({ok, self()}), + %% Supervised init can occur here ... + gen_server:enter_loop(?MODULE, Options, {}, {global, Name}). + spec_init_anonymous(Options) -> process_flag(trap_exit, true), proc_lib:init_ack({ok, self()}), diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl index 9bed20f771..661d57c85b 100644 --- a/lib/stdlib/test/io_proto_SUITE.erl +++ b/lib/stdlib/test/io_proto_SUITE.erl @@ -137,7 +137,7 @@ unicode_prompt(Config) when is_list(Config) -> {putline, "io:get_line('')."}, {putline, "hej"}, {getline, "<<\"hej\\n\">>"} - ],[],[],"-pa "++ PA), + ],[],[],"-pa \""++ PA++"\""), %% And one with oldshell ?line rtnode([{putline,""}, {putline, "2."}, @@ -153,7 +153,7 @@ unicode_prompt(Config) when is_list(Config) -> {putline, "io:get_line('')."}, {putline, "hej"}, {getline_re, ".*<<\"hej\\\\n\">>"} - ],[],[],"-oldshell -pa "++PA), + ],[],[],"-oldshell -pa \""++PA++"\""), ok. @@ -732,7 +732,7 @@ bc_with_r12_1(Config) -> PA = filename:dirname(code:which(?MODULE)), Name1 = io_proto_r12_1, ?line N1 = list_to_atom(atom_to_list(Name1) ++ "@" ++ hostname()), - ?line ?t:start_node(Name1, peer, [{args, "-pz "++PA},{erl,[{release,"r12b"}]}]), + ?line ?t:start_node(Name1, peer, [{args, "-pz \""++PA++"\""},{erl,[{release,"r12b"}]}]), DataDir = ?config(data_dir,Config), %PrivDir = ?config(priv_dir,Config), FileName1 = filename:join([DataDir,"testdata_latin1.dat"]), @@ -908,7 +908,7 @@ bc_with_r12_gl_1(_Config,Machine) -> PA = filename:dirname(code:which(?MODULE)), Name1 = io_proto_r12_gl_1, ?line N1 = list_to_atom(atom_to_list(Name1) ++ "@" ++ hostname()), - ?line ?t:start_node(Name1, peer, [{args, "-pz "++PA},{erl,[{release,"r12b"}]}]), + ?line ?t:start_node(Name1, peer, [{args, "-pz \""++PA++"\""},{erl,[{release,"r12b"}]}]), TestDataLine1 = [229,228,246], TestDataLine1BinUtf = unicode:characters_to_binary(TestDataLine1), TestDataLine1BinLatin = list_to_binary(TestDataLine1), @@ -1290,7 +1290,7 @@ eof_on_pipe(Config) when is_list(Config) -> end end, CommandLine1 = EchoLine ++ - Erl++" -noshell -eval " + "\""++Erl++"\" -noshell -eval " "'io:format(\"~p\",[io:get_line(\"\")])," "io:format(\"~p\",[io:get_line(\"\")])," "io:format(\"~p\",[io:get_line(\"\")]).' -run init stop", @@ -1301,7 +1301,7 @@ eof_on_pipe(Config) when is_list(Config) -> exit({unexpected1,Other1}) end, CommandLine2 = EchoLine ++ - Erl++" -noshell -eval " + "\""++Erl++"\" -noshell -eval " "'io:setopts([binary]),io:format(\"~p\",[io:get_line(\"\")])," "io:format(\"~p\",[io:get_line(\"\")])," "io:format(\"~p\",[io:get_line(\"\")]).' -run init stop", @@ -1340,7 +1340,8 @@ rtnode(Commands,Nodename,ErlPrefix,Extra) -> ?line {skip, Reason2}; Tempdir -> ?line SPid = - start_runerl_node(RunErl,ErlPrefix++Erl, + start_runerl_node(RunErl,ErlPrefix++ + "\\\""++Erl++"\\\"", Tempdir,Nodename, Extra), ?line CPid = start_toerl_server(ToErl,Tempdir), ?line erase(getline_skipped), @@ -1607,10 +1608,10 @@ start_runerl_node(RunErl,Erl,Tempdir,Nodename,Extra) -> " "++Extra end, spawn(fun() -> - ?dbg(RunErl++" "++Tempdir++"/ "++Tempdir++" \""++ - Erl++XArg++XXArg++"\""), - os:cmd(RunErl++" "++Tempdir++"/ "++Tempdir++" \""++ - Erl++XArg++XXArg++"\"") + ?dbg("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++ + " \""++Erl++XArg++XXArg++"\""), + os:cmd("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++ + " \""++Erl++XArg++XXArg++"\"") end). start_toerl_server(ToErl,Tempdir) -> @@ -1640,7 +1641,7 @@ try_to_erl(Command, N) -> end. toerl_server(Parent,ToErl,Tempdir) -> - Port = try_to_erl(ToErl++" "++Tempdir++"/ 2>/dev/null",8), + Port = try_to_erl("\""++ToErl++"\" "++Tempdir++"/ 2>/dev/null",8), case Port of P when is_port(P) -> Parent ! {self(),started}; diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index 1e74ad7727..192268f90e 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -2969,12 +2969,14 @@ lookup1(Config) when is_list(Config) -> [3] = lookup_keys(Q) end, [{1,a},{3,3}])">>, + {cres, <<"A = 3, etsc(fun(E) -> Q = qlc:q([X || X <- ets:table(E), A =:= {erlang,element}(1, X)]), [{3,3}] = qlc:e(Q), [3] = lookup_keys(Q) end, [{1,a},{3,3}])">>, + {warnings,[{3,erl_lint,deprecated_tuple_fun}]}}, <<"etsc(fun(E) -> A = 3, @@ -3439,12 +3441,14 @@ lookup2(Config) when is_list(Config) -> [r] = qlc:e(Q), [r] = lookup_keys(Q) end, [{keypos,1}], [#r{}])">>, + {cres, <<"etsc(fun(E) -> Q = qlc:q([element(1, X) || X <- ets:table(E), {erlang,is_record}(X, r, 2)]), [r] = qlc:e(Q), [r] = lookup_keys(Q) end, [{keypos,1}], [#r{}])">>, + {warnings,[{4,erl_lint,deprecated_tuple_fun}]}}, {cres, <<"etsc(fun(E) -> Q = qlc:q([element(1, X) || X <- ets:table(E), @@ -3465,12 +3469,14 @@ lookup2(Config) when is_list(Config) -> [r] = qlc:e(Q), [r] = lookup_keys(Q) end, [{keypos,1}], [#r{}])">>, + {cres, <<"etsc(fun(E) -> Q = qlc:q([element(1, X) || X <- ets:table(E), {erlang,is_record}(X, r)]), [r] = qlc:e(Q), [r] = lookup_keys(Q) - end, [{keypos,1}], [#r{}])">> + end, [{keypos,1}], [#r{}])">>, + {warnings,[{4,erl_lint,deprecated_tuple_fun}]}} ], ?line run(Config, <<"-record(r, {a}).\n">>, TsR), diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index 5be14767fa..4b83e42ee0 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -153,7 +153,7 @@ start_restricted_from_shell(Config) when is_list(Config) -> comm_err(<<"begin init:stop() end.">>), ?line "exception exit: restricted shell does not allow init:stop()" = comm_err(<<"begin F = fun() -> init:stop() end, F() end.">>), - ?line "exception error: bad argument in an arithmetic expression" = + ?line "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"begin +a end.">>), ?line "exception exit: restricted shell does not allow a + b" = comm_err(<<"begin a+b end.">>), @@ -2359,7 +2359,7 @@ otp_6554(Config) when is_list(Config) -> comm_err(<<"fun(X) -> not X end(a).">>), ?line "exception error: bad argument: a" = comm_err(<<"fun(A, B) -> A orelse B end(a, b).">>), - ?line "exception error: bad argument in an arithmetic expression" = + ?line "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"math:sqrt(2)/round(math:sqrt(0)).">>), ?line "exception error: interpreted function with arity 1 called with no arguments" = comm_err(<<"fun(V) -> V end().">>), @@ -2478,9 +2478,9 @@ otp_6554(Config) when is_list(Config) -> " receive {'EXIT', Pid, {{nocatch,foo},_}} -> ok end " "end.">>), - ?line "exception error: bad argument in an arithmetic expression" = + ?line "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"begin catch_exception(true), 1/0 end.">>), - ?line "exception error: bad argument in an arithmetic expression" = + ?line "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"begin catch_exception(false), 1/0 end.">>), ?line "exception error: no function clause matching call to catch_exception/1" = comm_err(<<"catch_exception(1).">>), @@ -2637,7 +2637,7 @@ otp_8393(Config) when is_list(Config) -> prompt_err(<<"shell:prompt_func('> ').">>), ?line _ = shell:prompt_func(default), - ?line "exception error: bad argument in an arithmetic expression"++_ = + ?line "exception error: an error occurred when evaluating an arithmetic expression"++_ = prompt_err(<<"shell:prompt_func({shell_SUITE,prompt4}).">>), ?line _ = shell:prompt_func(default), @@ -2710,7 +2710,7 @@ prompt3(L) -> integer_to_list(N). prompt4(_L) -> - erlang:apply({erlang,'/'}, [1,0]). + erlang:apply(fun erlang:'/'/2, [1,0]). prompt5(_L) -> [1050,1072,1082,1074,1086,32,1077,32,85,110,105,99,111,100,101,32,63]. diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index 694d39ce9c..3df7495ec4 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 1.18.1 +STDLIB_VSN = 1.18.2 diff --git a/lib/syntax_tools/doc/overview.edoc b/lib/syntax_tools/doc/overview.edoc index 23eadce8fe..df02ad0b3a 100644 --- a/lib/syntax_tools/doc/overview.edoc +++ b/lib/syntax_tools/doc/overview.edoc @@ -1,5 +1,9 @@ + -*- html -*- -@author Richard Carlsson <[email protected]> + Syntax Tools overview page + + +@author Richard Carlsson <[email protected]> @copyright 1997-2004 Richard Carlsson @version {@version} @title Erlang Syntax Tools diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile index 291b3e3047..d1c6989bd9 100644 --- a/lib/syntax_tools/doc/src/Makefile +++ b/lib/syntax_tools/doc/src/Makefile @@ -125,13 +125,13 @@ 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/syntax_tools/examples/Makefile b/lib/syntax_tools/examples/Makefile index 7cfe9185c2..2724b0899b 100644 --- a/lib/syntax_tools/examples/Makefile +++ b/lib/syntax_tools/examples/Makefile @@ -49,8 +49,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(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/syntax_tools/src/Makefile b/lib/syntax_tools/src/Makefile index bac138e95a..dca5e78be9 100644 --- a/lib/syntax_tools/src/Makefile +++ b/lib/syntax_tools/src/Makefile @@ -80,10 +80,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(OBJECTS) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(SOURCES) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(OBJECTS) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(SOURCES) "$(RELSYSDIR)/src" release_docs_spec: diff --git a/lib/syntax_tools/src/epp_dodger.erl b/lib/syntax_tools/src/epp_dodger.erl index 9f6f7d815e..b3ced34c14 100644 --- a/lib/syntax_tools/src/epp_dodger.erl +++ b/lib/syntax_tools/src/epp_dodger.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 2001-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl index 108ab3bffd..b833e1c069 100644 --- a/lib/syntax_tools/src/erl_comment_scan.erl +++ b/lib/syntax_tools/src/erl_comment_scan.erl @@ -16,7 +16,7 @@ %% %% ===================================================================== %% @copyright 1997-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index 7caf0b3db6..f4bbf975c3 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1997-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/erl_recomment.erl b/lib/syntax_tools/src/erl_recomment.erl index fc7c515700..7b2f9f7adb 100644 --- a/lib/syntax_tools/src/erl_recomment.erl +++ b/lib/syntax_tools/src/erl_recomment.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1997-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 32fd3722d6..151f04b03b 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1997-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== @@ -26,23 +24,20 @@ %% This module defines an abstract data type for representing Erlang %% source code as syntax trees, in a way that is backwards compatible %% with the data structures created by the Erlang standard library -%% parser module <code>erl_parse</code> (often referred to as "parse +%% parser module `erl_parse' (often referred to as "parse %% trees", which is a bit of a misnomer). This means that all -%% <code>erl_parse</code> trees are valid abstract syntax trees, but the +%% `erl_parse' trees are valid abstract syntax trees, but the %% reverse is not true: abstract syntax trees can in general not be used -%% as input to functions expecting an <code>erl_parse</code> tree. +%% as input to functions expecting an `erl_parse' tree. %% However, as long as an abstract syntax tree represents a correct -%% Erlang program, the function <a -%% href="#revert-1"><code>revert/1</code></a> should be able to -%% transform it to the corresponding <code>erl_parse</code> +%% Erlang program, the function {@link revert/1} should be able to +%% transform it to the corresponding `erl_parse' %% representation. %% -%% A recommended starting point for the first-time user is the -%% documentation of the <a -%% href="#type-syntaxTree"><code>syntaxTree()</code></a> data type, and -%% the function <a href="#type-1"><code>type/1</code></a>. +%% A recommended starting point for the first-time user is the documentation +%% of the {@link syntaxTree()} data type, and the function {@link type/1}. %% -%% <h3><b>NOTES:</b></h3> +%% == NOTES: == %% %% This module deals with the composition and decomposition of %% <em>syntactic</em> entities (as opposed to semantic ones); its @@ -52,36 +47,31 @@ %% in general, the user is assumed to pass type-correct arguments - if %% this is not done, the effects are not defined. %% -%% With the exception of the <code>erl_parse</code> data structures, +%% With the exception of the {@link erl_parse()} data structures, %% the internal representations of abstract syntax trees are subject to %% change without notice, and should not be documented outside this %% module. Furthermore, we do not give any guarantees on how an abstract %% syntax tree may or may not be represented, <em>with the following %% exceptions</em>: no syntax tree is represented by a single atom, such -%% as <code>none</code>, by a list constructor <code>[X | Y]</code>, or -%% by the empty list <code>[]</code>. This can be relied on when writing +%% as `none', by a list constructor `[X | Y]', or +%% by the empty list `[]'. This can be relied on when writing %% functions that operate on syntax trees. -%% @type syntaxTree(). An abstract syntax tree. The -%% <code>erl_parse</code> "parse tree" representation is a subset of the -%% <code>syntaxTree()</code> representation. +%% @type syntaxTree(). An abstract syntax tree. The {@link erl_parse()} +%% "parse tree" representation is a proper subset of the `syntaxTree()' +%% representation. %% %% Every abstract syntax tree node has a <em>type</em>, given by the -%% function <a href="#type-1"><code>type/1</code></a>. Each node also -%% has associated <em>attributes</em>; see <a -%% href="#get_attrs-1"><code>get_attrs/1</code></a> for details. The -%% functions <a href="#make_tree-2"><code>make_tree/2</code></a> and <a -%% href="#subtrees-1"><code>subtrees/1</code></a> are generic +%% function {@link type/1}. Each node also has associated +%% <em>attributes</em>; see {@link get_attrs/1} for details. The functions +%% {@link make_tree/2} and {@link subtrees/1} are generic %% constructor/decomposition functions for abstract syntax trees. The -%% functions <a href="#abstract-1"><code>abstract/1</code></a> and <a -%% href="#concrete-1"><code>concrete/1</code></a> convert between +%% functions {@link abstract/1} and {@link concrete/1} convert between %% constant Erlang terms and their syntactic representations. The set of -%% syntax tree nodes is extensible through the <a -%% href="#tree-2"><code>tree/2</code></a> function. +%% syntax tree nodes is extensible through the {@link tree/2} function. %% -%% A syntax tree can be transformed to the <code>erl_parse</code> -%% representation with the <a href="#revert-1"><code>revert/1</code></a> -%% function. +%% A syntax tree can be transformed to the {@link erl_parse()} +%% representation with the {@link revert/1} function. -module(erl_syntax). @@ -309,7 +299,7 @@ data/1, is_tree/1]). --export_type([forms/0, syntaxTree/0, syntaxTreeAttributes/0]). +-export_type([forms/0, syntaxTree/0, syntaxTreeAttributes/0, padding/0]). %% ===================================================================== %% IMPLEMENTATION NOTES: @@ -390,11 +380,15 @@ -record(wrapper, {type :: atom(), attr = #attr{} :: #attr{}, - tree :: term()}). + tree :: erl_parse()}). %% ===================================================================== --type syntaxTree() :: #tree{} | #wrapper{} | tuple(). % XXX: refine +-type syntaxTree() :: #tree{} | #wrapper{} | erl_parse(). + +-type erl_parse() :: erl_parse:abstract_form() | erl_parse:abstract_expr(). +%% The representation built by the Erlang standard library parser +%% `erl_parse'. This is a subset of the {@link syntaxTree()} type. %% ===================================================================== %% @@ -404,12 +398,11 @@ %% ===================================================================== -%% @spec type(Node::syntaxTree()) -> atom() -%% -%% @doc Returns the type tag of <code>Node</code>. If <code>Node</code> +%% @doc Returns the type tag of `Node'. If `Node' %% does not represent a syntax tree, evaluation fails with reason -%% <code>badarg</code>. Node types currently defined by this module are: -%% <p><center><table border="1"> +%% `badarg'. Node types currently defined by this module are: +%% +%% <center><table border="1"> %% <tr> %% <td>application</td> %% <td>arity_qualifier</td> @@ -476,12 +469,13 @@ %% <td>variable</td> %% <td>warning_marker</td> %% </tr> -%% </table></center></p> -%% <p>The user may (for special purposes) create additional nodes -%% with other type tags, using the <code>tree/2</code> function.</p> +%% </table></center> +%% +%% The user may (for special purposes) create additional nodes +%% with other type tags, using the {@link tree/2} function. %% -%% <p>Note: The primary constructor functions for a node type should -%% always have the same name as the node type itself.</p> +%% Note: The primary constructor functions for a node type should +%% always have the same name as the node type itself. %% %% @see tree/2 %% @see application/3 @@ -606,39 +600,38 @@ type(Node) -> %% ===================================================================== -%% @spec is_leaf(Node::syntaxTree()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> is a leaf node, -%% otherwise <code>false</code>. The currently recognised leaf node +%% @doc Returns `true' if `Node' is a leaf node, +%% otherwise `false'. The currently recognised leaf node %% types are: -%% <p><center><table border="1"> +%% +%% <center><table border="1"> %% <tr> -%% <td><code>atom</code></td> -%% <td><code>char</code></td> -%% <td><code>comment</code></td> -%% <td><code>eof_marker</code></td> -%% <td><code>error_marker</code></td> +%% <td>`atom'</td> +%% <td>`char'</td> +%% <td>`comment'</td> +%% <td>`eof_marker'</td> +%% <td>`error_marker'</td> %% </tr><tr> -%% <td><code>float</code></td> -%% <td><code>integer</code></td> -%% <td><code>nil</code></td> -%% <td><code>operator</code></td> -%% <td><code>string</code></td> +%% <td>`float'</td> +%% <td>`integer'</td> +%% <td>`nil'</td> +%% <td>`operator'</td> +%% <td>`string'</td> %% </tr><tr> -%% <td><code>text</code></td> -%% <td><code>underscore</code></td> -%% <td><code>variable</code></td> -%% <td><code>warning_marker</code></td> +%% <td>`text'</td> +%% <td>`underscore'</td> +%% <td>`variable'</td> +%% <td>`warning_marker'</td> %% </tr> -%% </table></center></p> -%% <p>A node of type <code>tuple</code> is a leaf node if and only if -%% its arity is zero.</p> +%% </table></center> %% -%% <p>Note: not all literals are leaf nodes, and vice versa. E.g., +%% A node of type `tuple' is a leaf node if and only if its arity is zero. +%% +%% Note: not all literals are leaf nodes, and vice versa. E.g., %% tuples with nonzero arity and nonempty lists may be literals, but are %% not leaf nodes. Variables, on the other hand, are leaf nodes but not -%% literals.</p> -%% +%% literals. +%% %% @see type/1 %% @see is_literal/1 @@ -666,29 +659,29 @@ is_leaf(Node) -> %% ===================================================================== -%% @spec is_form(Node::syntaxTree()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> is a syntax tree +%% @doc Returns `true' if `Node' is a syntax tree %% representing a so-called "source code form", otherwise -%% <code>false</code>. Forms are the Erlang source code units which, +%% `false'. Forms are the Erlang source code units which, %% placed in sequence, constitute an Erlang program. Current form types %% are: -%% <p><center><table border="1"> +%% +%% <center><table border="1"> %% <tr> -%% <td><code>attribute</code></td> -%% <td><code>comment</code></td> -%% <td><code>error_marker</code></td> -%% <td><code>eof_marker</code></td> -%% <td><code>form_list</code></td> +%% <td>`attribute'</td> +%% <td>`comment'</td> +%% <td>`error_marker'</td> +%% <td>`eof_marker'</td> +%% <td>`form_list'</td> %% </tr><tr> -%% <td><code>function</code></td> -%% <td><code>rule</code></td> -%% <td><code>warning_marker</code></td> -%% <td><code>text</code></td> +%% <td>`function'</td> +%% <td>`rule'</td> +%% <td>`warning_marker'</td> +%% <td>`text'</td> %% </tr> -%% </table></center></p> +%% </table></center> +%% %% @see type/1 -%% @see attribute/2 +%% @see attribute/2 %% @see comment/2 %% @see eof_marker/0 %% @see error_marker/1 @@ -715,10 +708,8 @@ is_form(Node) -> %% ===================================================================== -%% @spec get_pos(Node::syntaxTree()) -> term() -%% %% @doc Returns the position information associated with -%% <code>Node</code>. This is usually a nonnegative integer (indicating +%% `Node'. This is usually a nonnegative integer (indicating %% the source code line number), but may be any term. By default, all %% new tree nodes have their associated position information set to the %% integer zero. @@ -750,10 +741,7 @@ get_pos(Node) -> %% ===================================================================== -%% @spec set_pos(Node::syntaxTree(), Pos::term()) -> syntaxTree() -%% -%% @doc Sets the position information of <code>Node</code> to -%% <code>Pos</code>. +%% @doc Sets the position information of `Node' to `Pos'. %% %% @see get_pos/1 %% @see copy_pos/2 @@ -774,14 +762,10 @@ set_pos(Node, Pos) -> %% ===================================================================== -%% @spec copy_pos(Source::syntaxTree(), Target::syntaxTree()) -> -%% syntaxTree() -%% -%% @doc Copies the position information from <code>Source</code> to -%% <code>Target</code>. +%% @doc Copies the position information from `Source' to `Target'. %% -%% <p>This is equivalent to <code>set_pos(Target, -%% get_pos(Source))</code>, but potentially more efficient.</p> +%% This is equivalent to `set_pos(Target, +%% get_pos(Source))', but potentially more efficient. %% %% @see get_pos/1 %% @see set_pos/2 @@ -811,24 +795,20 @@ set_com(Node, Com) -> %% ===================================================================== -%% @spec get_precomments(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the associated pre-comments of a node. This is a %% possibly empty list of abstract comments, in top-down textual order. %% When the code is formatted, pre-comments are typically displayed %% directly above the node. For example: -%% <pre> -%% % Pre-comment of function -%% foo(X) -> {bar, X}.</pre> +%% ```% Pre-comment of function +%% foo(X) -> {bar, X}.''' %% -%% <p>If possible, the comment should be moved before any preceding +%% If possible, the comment should be moved before any preceding %% separator characters on the same line. E.g.: -%% <pre> -%% foo([X | Xs]) -> -%% % Pre-comment of 'bar(X)' node -%% [bar(X) | foo(Xs)]; -%% ...</pre> -%% (where the comment is moved before the "<code>[</code>").</p> +%% ```foo([X | Xs]) -> +%% % Pre-comment of 'bar(X)' node +%% [bar(X) | foo(Xs)]; +%% ...''' +%% (where the comment is moved before the "`['"). %% %% @see comment/2 %% @see set_precomments/2 @@ -846,11 +826,8 @@ get_precomments_1(#attr{com = #com{pre = Cs}}) -> Cs. %% ===================================================================== -%% @spec set_precomments(Node::syntaxTree(), -%% Comments::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Sets the pre-comments of <code>Node</code> to -%% <code>Comments</code>. <code>Comments</code> should be a possibly +%% @doc Sets the pre-comments of `Node' to +%% `Comments'. `Comments' should be a possibly %% empty list of abstract comments, in top-down textual order. %% %% @see comment/2 @@ -880,15 +857,11 @@ set_precomments_1(#attr{com = Com} = Attr, Cs) -> %% ===================================================================== -%% @spec add_precomments(Comments::[syntaxTree()], -%% Node::syntaxTree()) -> syntaxTree() +%% @doc Appends `Comments' to the pre-comments of `Node'. %% -%% @doc Appends <code>Comments</code> to the pre-comments of -%% <code>Node</code>. -%% -%% <p>Note: This is equivalent to <code>set_precomments(Node, -%% get_precomments(Node) ++ Comments)</code>, but potentially more -%% efficient.</p> +%% Note: This is equivalent to `set_precomments(Node, +%% get_precomments(Node) ++ Comments)', but potentially more +%% efficient. %% %% @see comment/2 %% @see get_precomments/1 @@ -915,24 +888,20 @@ add_precomments_1(Cs, #attr{com = Com} = Attr) -> %% ===================================================================== -%% @spec get_postcomments(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the associated post-comments of a node. This is a %% possibly empty list of abstract comments, in top-down textual order. %% When the code is formatted, post-comments are typically displayed to %% the right of and/or below the node. For example: -%% <pre> -%% {foo, X, Y} % Post-comment of tuple</pre> +%% ```{foo, X, Y} % Post-comment of tuple''' %% -%% <p>If possible, the comment should be moved past any following +%% If possible, the comment should be moved past any following %% separator characters on the same line, rather than placing the %% separators on the following line. E.g.: -%% <pre> -%% foo([X | Xs], Y) -> -%% foo(Xs, bar(X)); % Post-comment of 'bar(X)' node -%% ...</pre> -%% (where the comment is moved past the rightmost "<code>)</code>" and -%% the "<code>;</code>").</p> +%% ```foo([X | Xs], Y) -> +%% foo(Xs, bar(X)); % Post-comment of 'bar(X)' node +%% ...''' +%% (where the comment is moved past the rightmost "`)'" and +%% the "`;'"). %% %% @see comment/2 %% @see set_postcomments/2 @@ -950,11 +919,8 @@ get_postcomments_1(#attr{com = #com{post = Cs}}) -> Cs. %% ===================================================================== -%% @spec set_postcomments(Node::syntaxTree(), -%% Comments::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Sets the post-comments of <code>Node</code> to -%% <code>Comments</code>. <code>Comments</code> should be a possibly +%% @doc Sets the post-comments of `Node' to +%% `Comments'. `Comments' should be a possibly %% empty list of abstract comments, in top-down textual order %% %% @see comment/2 @@ -984,15 +950,11 @@ set_postcomments_1(#attr{com = Com} = Attr, Cs) -> %% ===================================================================== -%% @spec add_postcomments(Comments::[syntaxTree()], -%% Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Appends <code>Comments</code> to the post-comments of -%% <code>Node</code>. +%% @doc Appends `Comments' to the post-comments of `Node'. %% -%% <p>Note: This is equivalent to <code>set_postcomments(Node, -%% get_postcomments(Node) ++ Comments)</code>, but potentially more -%% efficient.</p> +%% Note: This is equivalent to `set_postcomments(Node, +%% get_postcomments(Node) ++ Comments)', but potentially more +%% efficient. %% %% @see comment/2 %% @see get_postcomments/1 @@ -1019,14 +981,12 @@ add_postcomments_1(Cs, #attr{com = Com} = Attr) -> %% ===================================================================== -%% @spec has_comments(Node::syntaxTree()) -> boolean() +%% @doc Yields `false' if the node has no associated +%% comments, and `true' otherwise. %% -%% @doc Yields <code>false</code> if the node has no associated -%% comments, and <code>true</code> otherwise. -%% -%% <p>Note: This is equivalent to <code>(get_precomments(Node) == []) -%% and (get_postcomments(Node) == [])</code>, but potentially more -%% efficient.</p> +%% Note: This is equivalent to `(get_precomments(Node) == []) +%% and (get_postcomments(Node) == [])', but potentially more +%% efficient. %% %% @see get_precomments/1 %% @see get_postcomments/1 @@ -1050,13 +1010,11 @@ has_comments(_) -> false. %% ===================================================================== -%% @spec remove_comments(Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Clears the associated comments of <code>Node</code>. +%% @doc Clears the associated comments of `Node'. %% -%% <p>Note: This is equivalent to -%% <code>set_precomments(set_postcomments(Node, []), [])</code>, but -%% potentially more efficient.</p> +%% Note: This is equivalent to +%% `set_precomments(set_postcomments(Node, []), [])', but +%% potentially more efficient. %% %% @see set_precomments/2 %% @see set_postcomments/2 @@ -1075,16 +1033,12 @@ remove_comments(Node) -> %% ===================================================================== -%% @spec copy_comments(Source::syntaxTree(), Target::syntaxTree()) -> -%% syntaxTree() -%% -%% @doc Copies the pre- and postcomments from <code>Source</code> to -%% <code>Target</code>. +%% @doc Copies the pre- and postcomments from `Source' to `Target'. %% -%% <p>Note: This is equivalent to -%% <code>set_postcomments(set_precomments(Target, -%% get_precomments(Source)), get_postcomments(Source))</code>, but -%% potentially more efficient.</p> +%% Note: This is equivalent to +%% `set_postcomments(set_precomments(Target, +%% get_precomments(Source)), get_postcomments(Source))', but +%% potentially more efficient. %% %% @see comment/2 %% @see get_precomments/1 @@ -1099,16 +1053,13 @@ copy_comments(Source, Target) -> %% ===================================================================== -%% @spec join_comments(Source::syntaxTree(), Target::syntaxTree()) -> -%% syntaxTree() +%% @doc Appends the comments of `Source' to the current +%% comments of `Target'. %% -%% @doc Appends the comments of <code>Source</code> to the current -%% comments of <code>Target</code>. -%% -%% <p>Note: This is equivalent to -%% <code>add_postcomments(get_postcomments(Source), -%% add_precomments(get_precomments(Source), Target))</code>, but -%% potentially more efficient.</p> +%% Note: This is equivalent to +%% `add_postcomments(get_postcomments(Source), +%% add_precomments(get_precomments(Source), Target))', but +%% potentially more efficient. %% %% @see comment/2 %% @see get_precomments/1 @@ -1125,8 +1076,6 @@ join_comments(Source, Target) -> %% ===================================================================== -%% @spec get_ann(syntaxTree()) -> [term()] -%% %% @doc Returns the list of user annotations associated with a syntax %% tree node. For a newly created node, this is the empty list. The %% annotations may be any terms. @@ -1142,11 +1091,7 @@ get_ann(_) -> []. %% ===================================================================== -%% @spec set_ann(Node::syntaxTree(), Annotations::[term()]) -> -%% syntaxTree() -%% -%% @doc Sets the list of user annotations of <code>Node</code> to -%% <code>Annotations</code>. +%% @doc Sets the list of user annotations of `Node' to `Annotations'. %% %% @see get_ann/1 %% @see add_ann/2 @@ -1168,13 +1113,11 @@ set_ann(Node, As) -> %% ===================================================================== -%% @spec add_ann(Annotation::term(), Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Appends the term <code>Annotation</code> to the list of user -%% annotations of <code>Node</code>. +%% @doc Appends the term `Annotation' to the list of user +%% annotations of `Node'. %% -%% <p>Note: this is equivalent to <code>set_ann(Node, [Annotation | -%% get_ann(Node)])</code>, but potentially more efficient.</p> +%% Note: this is equivalent to `set_ann(Node, [Annotation | +%% get_ann(Node)])', but potentially more efficient. %% %% @see get_ann/1 %% @see set_ann/2 @@ -1195,14 +1138,10 @@ add_ann(A, Node) -> %% ===================================================================== -%% @spec copy_ann(Source::syntaxTree(), Target::syntaxTree()) -> -%% syntaxTree() +%% @doc Copies the list of user annotations from `Source' to `Target'. %% -%% @doc Copies the list of user annotations from <code>Source</code> to -%% <code>Target</code>. -%% -%% <p>Note: this is equivalent to <code>set_ann(Target, -%% get_ann(Source))</code>, but potentially more efficient.</p> +%% Note: this is equivalent to `set_ann(Target, +%% get_ann(Source))', but potentially more efficient. %% %% @see get_ann/1 %% @see set_ann/2 @@ -1214,23 +1153,20 @@ copy_ann(Source, Target) -> %% ===================================================================== -%% @spec get_attrs(syntaxTree()) -> syntaxTreeAttributes() -%% %% @doc Returns a representation of the attributes associated with a %% syntax tree node. The attributes are all the extra information that %% can be attached to a node. Currently, this includes position %% information, source code comments, and user annotations. The result %% of this function cannot be inspected directly; only attached to -%% another node (cf. <code>set_attrs/2</code>). +%% another node (see {@link set_attrs/2}). %% -%% <p>For accessing individual attributes, see <code>get_pos/1</code>, -%% <code>get_ann/1</code>, <code>get_precomments/1</code> and -%% <code>get_postcomments/1</code>.</p> +%% For accessing individual attributes, see {@link get_pos/1}, +%% {@link get_ann/1}, {@link get_precomments/1} and +%% {@link get_postcomments/1}. %% %% @type syntaxTreeAttributes(). This is an abstract representation of -%% syntax tree node attributes; see the function <a -%% href="#get_attrs-1"><code>get_attrs/1</code></a>. -%% +%% syntax tree node attributes; see the function {@link get_attrs/1}. +%% %% @see set_attrs/2 %% @see get_pos/1 %% @see get_ann/1 @@ -1247,11 +1183,7 @@ get_attrs(Node) -> #attr{pos = get_pos(Node), %% ===================================================================== -%% @spec set_attrs(Node::syntaxTree(), -%% Attributes::syntaxTreeAttributes()) -> syntaxTree() -%% -%% @doc Sets the attributes of <code>Node</code> to -%% <code>Attributes</code>. +%% @doc Sets the attributes of `Node' to `Attributes'. %% %% @see get_attrs/1 %% @see copy_attrs/2 @@ -1270,14 +1202,10 @@ set_attrs(Node, Attr) -> %% ===================================================================== -%% @spec copy_attrs(Source::syntaxTree(), Target::syntaxTree()) -> -%% syntaxTree() +%% @doc Copies the attributes from `Source' to `Target'. %% -%% @doc Copies the attributes from <code>Source</code> to -%% <code>Target</code>. -%% -%% <p>Note: this is equivalent to <code>set_attrs(Target, -%% get_attrs(Source))</code>, but potentially more efficient.</p> +%% Note: this is equivalent to `set_attrs(Target, +%% get_attrs(Source))', but potentially more efficient. %% %% @see get_attrs/1 %% @see set_attrs/2 @@ -1289,7 +1217,6 @@ copy_attrs(S, T) -> %% ===================================================================== -%% @spec comment(Strings) -> syntaxTree() %% @equiv comment(none, Strings) -spec comment([string()]) -> syntaxTree(). @@ -1299,22 +1226,19 @@ comment(Strings) -> %% ===================================================================== -%% @spec comment(Padding, Strings::[string()]) -> syntaxTree() -%% Padding = none | integer() -%% %% @doc Creates an abstract comment with the given padding and text. If -%% <code>Strings</code> is a (possibly empty) list +%% `Strings' is a (possibly empty) list %% <code>["<em>Txt1</em>", ..., "<em>TxtN</em>"]</code>, the result %% represents the source code text %% <pre> -%% %<em>Txt1</em> -%% ... -%% %<em>TxtN</em></pre> -%% <code>Padding</code> states the number of empty character positions +%% %<em>Txt1</em> +%% ... +%% %<em>TxtN</em></pre> +%% `Padding' states the number of empty character positions %% to the left of the comment separating it horizontally from -%% source code on the same line (if any). If <code>Padding</code> is -%% <code>none</code>, a default positive number is used. If -%% <code>Padding</code> is an integer less than 1, there should be no +%% source code on the same line (if any). If `Padding' is +%% `none', a default positive number is used. If +%% `Padding' is an integer less than 1, there should be no %% separating space. Comments are in themselves regarded as source %% program forms. %% @@ -1338,8 +1262,6 @@ comment(Pad, Strings) -> %% ===================================================================== -%% @spec comment_text(syntaxTree()) -> [string()] -%% %% @doc Returns the lines of text of the abstract comment. %% %% @see comment/2 @@ -1351,11 +1273,8 @@ comment_text(Node) -> %% ===================================================================== -%% @spec comment_padding(syntaxTree()) -> none | integer() -%% %% @doc Returns the amount of padding before the comment, or -%% <code>none</code>. The latter means that a default padding may be -%% used. +%% `none'. The latter means that a default padding may be used. %% %% @see comment/2 @@ -1366,23 +1285,21 @@ comment_padding(Node) -> %% ===================================================================== -%% @spec form_list(Forms::[syntaxTree()]) -> syntaxTree() -%% %% @doc Creates an abstract sequence of "source code forms". If -%% <code>Forms</code> is <code>[F1, ..., Fn]</code>, where each -%% <code>Fi</code> is a form (cf. <code>is_form/1</code>, the result +%% `Forms' is `[F1, ..., Fn]', where each +%% `Fi' is a form (see {@link is_form/1}, the result %% represents %% <pre> -%% <em>F1</em> -%% ... -%% <em>Fn</em></pre> -%% where the <code>Fi</code> are separated by one or more line breaks. A -%% node of type <code>form_list</code> is itself regarded as a source -%% code form; cf. <code>flatten_form_list/1</code>. -%% -%% <p>Note: this is simply a way of grouping source code forms as a +%% <em>F1</em> +%% ... +%% <em>Fn</em></pre> +%% where the `Fi' are separated by one or more line breaks. A +%% node of type `form_list' is itself regarded as a source +%% code form; see {@link flatten_form_list/1}. +%% +%% Note: this is simply a way of grouping source code forms as a %% single syntax tree, usually in order to form an Erlang module -%% definition.</p> +%% definition. %% %% @see form_list_elements/1 %% @see is_form/1 @@ -1401,9 +1318,7 @@ form_list(Forms) -> %% ===================================================================== -%% @spec form_list_elements(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of subnodes of a <code>form_list</code> node. +%% @doc Returns the list of subnodes of a `form_list' node. %% %% @see form_list/1 @@ -1414,10 +1329,8 @@ form_list_elements(Node) -> %% ===================================================================== -%% @spec flatten_form_list(Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Flattens sublists of a <code>form_list</code> node. Returns -%% <code>Node</code> with all subtrees of type <code>form_list</code> +%% @doc Flattens sublists of a `form_list' node. Returns +%% `Node' with all subtrees of type `form_list' %% recursively expanded, yielding a single "flat" abstract form %% sequence. %% @@ -1443,10 +1356,8 @@ flatten_form_list_1([], As) -> %% ===================================================================== -%% @spec text(String::string()) -> syntaxTree() -%% %% @doc Creates an abstract piece of source code text. The result -%% represents exactly the sequence of characters in <code>String</code>. +%% represents exactly the sequence of characters in `String'. %% This is useful in cases when one wants full control of the resulting %% output, e.g., for the appearance of floating-point numbers or macro %% definitions. @@ -1463,10 +1374,7 @@ text(String) -> %% ===================================================================== -%% @spec text_string(syntaxTree()) -> string() -%% -%% @doc Returns the character sequence represented by a -%% <code>text</code> node. +%% @doc Returns the character sequence represented by a `text' node. %% %% @see text/1 @@ -1477,18 +1385,15 @@ text_string(Node) -> %% ===================================================================== -%% @spec variable(Name) -> syntaxTree() -%% Name = atom() | string() -%% %% @doc Creates an abstract variable with the given name. -%% <code>Name</code> may be any atom or string that represents a +%% `Name' may be any atom or string that represents a %% lexically valid variable name, but <em>not</em> a single underscore -%% character; cf. <code>underscore/0</code>. +%% character; see {@link underscore/0}. %% -%% <p>Note: no checking is done whether the character sequence +%% Note: no checking is done whether the character sequence %% represents a proper variable name, i.e., whether or not its first %% character is an uppercase Erlang character, or whether it does not -%% contain control characters, whitespace, etc.</p> +%% contain control characters, whitespace, etc. %% %% @see variable_name/1 %% @see variable_literal/1 @@ -1517,9 +1422,7 @@ revert_variable(Node) -> %% ===================================================================== -%% @spec variable_name(syntaxTree()) -> atom() -%% -%% @doc Returns the name of a <code>variable</code> node as an atom. +%% @doc Returns the name of a `variable' node as an atom. %% %% @see variable/1 @@ -1535,9 +1438,7 @@ variable_name(Node) -> %% ===================================================================== -%% @spec variable_literal(syntaxTree()) -> string() -%% -%% @doc Returns the name of a <code>variable</code> node as a string. +%% @doc Returns the name of a `variable' node as a string. %% %% @see variable/1 @@ -1553,9 +1454,7 @@ variable_literal(Node) -> %% ===================================================================== -%% @spec underscore() -> syntaxTree() -%% -%% @doc Creates an abstract universal pattern ("<code>_</code>"). The +%% @doc Creates an abstract universal pattern ("`_'"). The %% lexical representation is a single underscore character. Note that %% this is <em>not</em> a variable, lexically speaking. %% @@ -1579,10 +1478,8 @@ revert_underscore(Node) -> %% ===================================================================== -%% @spec integer(Value::integer()) -> syntaxTree() -%% %% @doc Creates an abstract integer literal. The lexical representation -%% is the canonical decimal numeral of <code>Value</code>. +%% is the canonical decimal numeral of `Value'. %% %% @see integer_value/1 %% @see integer_literal/1 @@ -1608,11 +1505,8 @@ revert_integer(Node) -> %% ===================================================================== -%% @spec is_integer(Node::syntaxTree(), Value::integer()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> has type -%% <code>integer</code> and represents <code>Value</code>, otherwise -%% <code>false</code>. +%% @doc Returns `true' if `Node' has type +%% `integer' and represents `Value', otherwise `false'. %% %% @see integer/1 @@ -1630,9 +1524,7 @@ is_integer(Node, Value) -> %% ===================================================================== -%% @spec integer_value(syntaxTree()) -> integer() -%% -%% @doc Returns the value represented by an <code>integer</code> node. +%% @doc Returns the value represented by an `integer' node. %% %% @see integer/1 @@ -1648,10 +1540,7 @@ integer_value(Node) -> %% ===================================================================== -%% @spec integer_literal(syntaxTree()) -> string() -%% -%% @doc Returns the numeral string represented by an -%% <code>integer</code> node. +%% @doc Returns the numeral string represented by an `integer' node. %% %% @see integer/1 @@ -1662,11 +1551,8 @@ integer_literal(Node) -> %% ===================================================================== -%% @spec float(Value::float()) -> syntaxTree() -%% %% @doc Creates an abstract floating-point literal. The lexical -%% representation is the decimal floating-point numeral of -%% <code>Value</code>. +%% representation is the decimal floating-point numeral of `Value'. %% %% @see float_value/1 %% @see float_literal/1 @@ -1701,9 +1587,7 @@ revert_float(Node) -> %% ===================================================================== -%% @spec float_value(syntaxTree()) -> float() -%% -%% @doc Returns the value represented by a <code>float</code> node. Note +%% @doc Returns the value represented by a `float' node. Note %% that floating-point values should usually not be compared for %% equality. %% @@ -1721,10 +1605,7 @@ float_value(Node) -> %% ===================================================================== -%% @spec float_literal(syntaxTree()) -> string() -%% -%% @doc Returns the numeral string represented by a <code>float</code> -%% node. +%% @doc Returns the numeral string represented by a `float' node. %% %% @see float/1 @@ -1735,17 +1616,15 @@ float_literal(Node) -> %% ===================================================================== -%% @spec char(Value::char()) -> syntaxTree() -%% %% @doc Creates an abstract character literal. The result represents -%% "<code>$<em>Name</em></code>", where <code>Name</code> corresponds to -%% <code>Value</code>. +%% "<code>$<em>Name</em></code>", where `Name' corresponds to +%% `Value'. %% -%% <p>Note: the literal corresponding to a particular character value is -%% not uniquely defined. E.g., the character "<code>a</code>" can be -%% written both as "<code>$a</code>" and "<code>$\141</code>", and a Tab -%% character can be written as "<code>$\11</code>", "<code>$\011</code>" -%% or "<code>$\t</code>".</p> +%% Note: the literal corresponding to a particular character value is +%% not uniquely defined. E.g., the character "`a'" can be +%% written both as "`$a'" and "`$\141'", and a Tab +%% character can be written as "`$\11'", "`$\011'" +%% or "`$\t'". %% %% @see char_value/1 %% @see char_literal/1 @@ -1771,11 +1650,8 @@ revert_char(Node) -> %% ===================================================================== -%% @spec is_char(Node::syntaxTree(), Value::char()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> has type -%% <code>char</code> and represents <code>Value</code>, otherwise -%% <code>false</code>. +%% @doc Returns `true' if `Node' has type +%% `char' and represents `Value', otherwise `false'. %% %% @see char/1 @@ -1793,9 +1669,7 @@ is_char(Node, Value) -> %% ===================================================================== -%% @spec char_value(syntaxTree()) -> char() -%% -%% @doc Returns the value represented by a <code>char</code> node. +%% @doc Returns the value represented by a `char' node. %% %% @see char/1 @@ -1811,10 +1685,8 @@ char_value(Node) -> %% ===================================================================== -%% @spec char_literal(syntaxTree()) -> string() -%% -%% @doc Returns the literal string represented by a <code>char</code> -%% node. This includes the leading "<code>$</code>" character. +%% @doc Returns the literal string represented by a `char' +%% node. This includes the leading "`$'" character. %% %% @see char/1 @@ -1825,16 +1697,14 @@ char_literal(Node) -> %% ===================================================================== -%% @spec string(Value::string()) -> syntaxTree() -%% %% @doc Creates an abstract string literal. The result represents %% <code>"<em>Text</em>"</code> (including the surrounding -%% double-quotes), where <code>Text</code> corresponds to the sequence -%% of characters in <code>Value</code>, but not representing a -%% <em>specific</em> string literal. E.g., the result of -%% <code>string("x\ny")</code> represents any and all of -%% <code>"x\ny"</code>, <code>"x\12y"</code>, <code>"x\012y"</code> and -%% <code>"x\^Jy"</code>; cf. <code>char/1</code>. +%% double-quotes), where `Text' corresponds to the sequence +%% of characters in `Value', but not representing a +%% <em>specific</em> string literal. +%% +%% For example, the result of `string("x\ny")' represents any and all of +%% `"x\ny"', `"x\12y"', `"x\012y"' and `"x\^Jy"'; see {@link char/1}. %% %% @see string_value/1 %% @see string_literal/1 @@ -1861,11 +1731,8 @@ revert_string(Node) -> %% ===================================================================== -%% @spec is_string(Node::syntaxTree(), Value::string()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> has type -%% <code>string</code> and represents <code>Value</code>, otherwise -%% <code>false</code>. +%% @doc Returns `true' if `Node' has type +%% `string' and represents `Value', otherwise `false'. %% %% @see string/1 @@ -1883,9 +1750,7 @@ is_string(Node, Value) -> %% ===================================================================== -%% @spec string_value(syntaxTree()) -> string() -%% -%% @doc Returns the value represented by a <code>string</code> node. +%% @doc Returns the value represented by a `string' node. %% %% @see string/1 @@ -1901,9 +1766,7 @@ string_value(Node) -> %% ===================================================================== -%% @spec string_literal(syntaxTree()) -> string() -%% -%% @doc Returns the literal string represented by a <code>string</code> +%% @doc Returns the literal string represented by a `string' %% node. This includes surrounding double-quote characters. %% %% @see string/1 @@ -1915,11 +1778,8 @@ string_literal(Node) -> %% ===================================================================== -%% @spec atom(Name) -> syntaxTree() -%% Name = atom() | string() -%% %% @doc Creates an abstract atom literal. The print name of the atom is -%% the character sequence represented by <code>Name</code>. +%% the character sequence represented by `Name'. %% %% @see atom_value/1 %% @see atom_name/1 @@ -1948,11 +1808,8 @@ revert_atom(Node) -> %% ===================================================================== -%% @spec is_atom(Node::syntaxTree(), Value::atom()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> has type -%% <code>atom</code> and represents <code>Value</code>, otherwise -%% <code>false</code>. +%% @doc Returns `true' if `Node' has type +%% `atom' and represents `Value', otherwise `false'. %% %% @see atom/1 @@ -1970,9 +1827,7 @@ is_atom(Node, Value) -> %% ===================================================================== -%% @spec atom_value(syntaxTree()) -> atom() -%% -%% @doc Returns the value represented by an <code>atom</code> node. +%% @doc Returns the value represented by an `atom' node. %% %% @see atom/1 @@ -1988,9 +1843,7 @@ atom_value(Node) -> %% ===================================================================== -%% @spec atom_name(syntaxTree()) -> string() -%% -%% @doc Returns the printname of an <code>atom</code> node. +%% @doc Returns the printname of an `atom' node. %% %% @see atom/1 @@ -2001,15 +1854,12 @@ atom_name(Node) -> %% ===================================================================== -%% @spec atom_literal(syntaxTree()) -> string() -%% -%% @doc Returns the literal string represented by an <code>atom</code> +%% @doc Returns the literal string represented by an `atom' %% node. This includes surrounding single-quote characters if necessary. %% -%% <p>Note that e.g. the result of <code>atom("x\ny")</code> represents -%% any and all of <code>'x\ny'</code>, <code>'x\12y'</code>, -%% <code>'x\012y'</code> and <code>'x\^Jy\'</code>; cf. -%% <code>string/1</code>.</p> +%% Note that e.g. the result of `atom("x\ny")' represents +%% any and all of `'x\ny'', `'x\12y'', +%% `'x\012y'' and `'x\^Jy\''; see {@link string/1}. %% %% @see atom/1 %% @see string/1 @@ -2021,14 +1871,12 @@ atom_literal(Node) -> %% ===================================================================== -%% @spec tuple(Elements::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract tuple. If <code>Elements</code> is -%% <code>[X1, ..., Xn]</code>, the result represents +%% @doc Creates an abstract tuple. If `Elements' is +%% `[X1, ..., Xn]', the result represents %% "<code>{<em>X1</em>, ..., <em>Xn</em>}</code>". %% -%% <p>Note: The Erlang language has distinct 1-tuples, i.e., -%% <code>{X}</code> is always distinct from <code>X</code> itself.</p> +%% Note: The Erlang language has distinct 1-tuples, i.e., +%% `{X}' is always distinct from `X' itself. %% %% @see tuple_elements/1 %% @see tuple_size/1 @@ -2055,10 +1903,7 @@ revert_tuple(Node) -> %% ===================================================================== -%% @spec tuple_elements(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of element subtrees of a <code>tuple</code> -%% node. +%% @doc Returns the list of element subtrees of a `tuple' node. %% %% @see tuple/1 @@ -2074,13 +1919,11 @@ tuple_elements(Node) -> %% ===================================================================== -%% @spec tuple_size(syntaxTree()) -> integer() -%% -%% @doc Returns the number of elements of a <code>tuple</code> node. +%% @doc Returns the number of elements of a `tuple' node. %% -%% <p>Note: this is equivalent to -%% <code>length(tuple_elements(Node))</code>, but potentially more -%% efficient.</p> +%% Note: this is equivalent to +%% `length(tuple_elements(Node))', but potentially more +%% efficient. %% %% @see tuple/1 %% @see tuple_elements/1 @@ -2092,7 +1935,6 @@ tuple_size(Node) -> %% ===================================================================== -%% @spec list(List) -> syntaxTree() %% @equiv list(List, none) -spec list([syntaxTree()]) -> syntaxTree(). @@ -2102,35 +1944,31 @@ list(List) -> %% ===================================================================== -%% @spec list(List, Tail) -> syntaxTree() -%% List = [syntaxTree()] -%% Tail = none | syntaxTree() -%% %% @doc Constructs an abstract list skeleton. The result has type -%% <code>list</code> or <code>nil</code>. If <code>List</code> is a -%% nonempty list <code>[E1, ..., En]</code>, the result has type -%% <code>list</code> and represents either "<code>[<em>E1</em>, ..., -%% <em>En</em>]</code>", if <code>Tail</code> is <code>none</code>, or +%% `list' or `nil'. If `List' is a +%% nonempty list `[E1, ..., En]', the result has type +%% `list' and represents either "<code>[<em>E1</em>, ..., +%% <em>En</em>]</code>", if `Tail' is `none', or %% otherwise "<code>[<em>E1</em>, ..., <em>En</em> | -%% <em>Tail</em>]</code>". If <code>List</code> is the empty list, -%% <code>Tail</code> <em>must</em> be <code>none</code>, and in that -%% case the result has type <code>nil</code> and represents -%% "<code>[]</code>" (cf. <code>nil/0</code>). +%% <em>Tail</em>]</code>". If `List' is the empty list, +%% `Tail' <em>must</em> be `none', and in that +%% case the result has type `nil' and represents +%% "`[]'" (see {@link nil/0}). %% -%% <p>The difference between lists as semantic objects (built up of +%% The difference between lists as semantic objects (built up of %% individual "cons" and "nil" terms) and the various syntactic forms %% for denoting lists may be bewildering at first. This module provides %% functions both for exact control of the syntactic representation as %% well as for the simple composition and deconstruction in terms of -%% cons and head/tail operations.</p> +%% cons and head/tail operations. %% -%% <p>Note: in <code>list(Elements, none)</code>, the "nil" list -%% terminator is implicit and has no associated information (cf. -%% <code>get_attrs/1</code>), while in the seemingly equivalent -%% <code>list(Elements, Tail)</code> when <code>Tail</code> has type -%% <code>nil</code>, the list terminator subtree <code>Tail</code> may +%% Note: in `list(Elements, none)', the "nil" list +%% terminator is implicit and has no associated information (see +%% {@link get_attrs/1}), while in the seemingly equivalent +%% `list(Elements, Tail)' when `Tail' has type +%% `nil', the list terminator subtree `Tail' may %% have attached attributes such as position, comments, and annotations, -%% which will be preserved in the result.</p> +%% which will be preserved in the result. %% %% @see nil/0 %% @see list/1 @@ -2187,10 +2025,8 @@ revert_list(Node) -> S, P). %% ===================================================================== -%% @spec nil() -> syntaxTree() -%% %% @doc Creates an abstract empty list. The result represents -%% "<code>[]</code>". The empty list is traditionally called "nil". +%% "`[]'". The empty list is traditionally called "nil". %% %% @see list/2 %% @see is_list_skeleton/1 @@ -2213,13 +2049,11 @@ revert_nil(Node) -> %% ===================================================================== -%% @spec list_prefix(Node::syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the prefix element subtrees of a <code>list</code> node. -%% If <code>Node</code> represents "<code>[<em>E1</em>, ..., +%% @doc Returns the prefix element subtrees of a `list' node. +%% If `Node' represents "<code>[<em>E1</em>, ..., %% <em>En</em>]</code>" or "<code>[<em>E1</em>, ..., <em>En</em> | -%% <em>Tail</em>]</code>", the returned value is <code>[E1, ..., -%% En]</code>. +%% <em>Tail</em>]</code>", the returned value is `[E1, ..., +%% En]'. %% %% @see list/2 @@ -2227,28 +2061,31 @@ revert_nil(Node) -> list_prefix(Node) -> case unwrap(Node) of - {cons, _, Head, _} -> - [Head]; + {cons, _, Head, Tail} -> + [Head | cons_prefix(Tail)]; Node1 -> (data(Node1))#list.prefix end. +%% collects sequences of conses; cf. cons_suffix/1 below +cons_prefix({cons, _, Head, Tail}) -> + [Head | cons_prefix(Tail)]; +cons_prefix(_) -> + []. + %% ===================================================================== -%% @spec list_suffix(Node::syntaxTree()) -> none | syntaxTree() -%% -%% @doc Returns the suffix subtree of a <code>list</code> node, if one -%% exists. If <code>Node</code> represents "<code>[<em>E1</em>, ..., +%% @doc Returns the suffix subtree of a `list' node, if one +%% exists. If `Node' represents "<code>[<em>E1</em>, ..., %% <em>En</em> | <em>Tail</em>]</code>", the returned value is -%% <code>Tail</code>, otherwise, i.e., if <code>Node</code> represents -%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", <code>none</code> is +%% `Tail', otherwise, i.e., if `Node' represents +%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", `none' is %% returned. %% -%% <p>Note that even if this function returns some <code>Tail</code> -%% that is not <code>none</code>, the type of <code>Tail</code> can be -%% <code>nil</code>, if the tail has been given explicitly, and the list -%% skeleton has not been compacted (cf. -%% <code>compact_list/1</code>).</p> +%% Note that even if this function returns some `Tail' +%% that is not `none', the type of `Tail' can be +%% `nil', if the tail has been given explicitly, and the list +%% skeleton has not been compacted (see {@link compact_list/1}). %% %% @see list/2 %% @see nil/0 @@ -2259,34 +2096,36 @@ list_prefix(Node) -> list_suffix(Node) -> case unwrap(Node) of {cons, _, _, Tail} -> - %% If there could be comments/annotations on the tail node, - %% we should not return `none' even if it has type `nil'. - case Tail of + case cons_suffix(Tail) of {nil, _} -> - none; % no interesting information is lost - _ -> - Tail + none; + Tail1 -> + Tail1 end; Node1 -> (data(Node1))#list.suffix end. +%% skips sequences of conses; cf. cons_prefix/1 above +cons_suffix({cons, _, _, Tail}) -> + cons_suffix(Tail); +cons_suffix(Tail) -> + Tail. + %% ===================================================================== -%% @spec cons(Head::syntaxTree(), Tail::syntaxTree()) -> syntaxTree() -%% %% @doc "Optimising" list skeleton cons operation. Creates an abstract -%% list skeleton whose first element is <code>Head</code> and whose tail -%% corresponds to <code>Tail</code>. This is similar to -%% <code>list([Head], Tail)</code>, except that <code>Tail</code> may -%% not be <code>none</code>, and that the result does not necessarily +%% list skeleton whose first element is `Head' and whose tail +%% corresponds to `Tail'. This is similar to +%% `list([Head], Tail)', except that `Tail' may +%% not be `none', and that the result does not necessarily %% represent exactly "<code>[<em>Head</em> | <em>Tail</em>]</code>", but -%% may depend on the <code>Tail</code> subtree. E.g., if -%% <code>Tail</code> represents <code>[X, Y]</code>, the result may +%% may depend on the `Tail' subtree. E.g., if +%% `Tail' represents `[X, Y]', the result may %% represent "<code>[<em>Head</em>, X, Y]</code>", rather than %% "<code>[<em>Head</em> | [X, Y]]</code>". Annotations on -%% <code>Tail</code> itself may be lost if <code>Tail</code> represents -%% a list skeleton, but comments on <code>Tail</code> are propagated to +%% `Tail' itself may be lost if `Tail' represents +%% a list skeleton, but comments on `Tail' are propagated to %% the result. %% %% @see list/2 @@ -2308,10 +2147,8 @@ cons(Head, Tail) -> %% ===================================================================== -%% @spec list_head(Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the head element subtree of a <code>list</code> node. If -%% <code>Node</code> represents "<code>[<em>Head</em> ...]</code>", the +%% @doc Returns the head element subtree of a `list' node. If +%% `Node' represents "<code>[<em>Head</em> ...]</code>", the %% result will represent "<code><em>Head</em></code>". %% %% @see list/2 @@ -2325,15 +2162,13 @@ list_head(Node) -> %% ===================================================================== -%% @spec list_tail(Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the tail of a <code>list</code> node. If -%% <code>Node</code> represents a single-element list +%% @doc Returns the tail of a `list' node. If +%% `Node' represents a single-element list %% "<code>[<em>E</em>]</code>", then the result has type -%% <code>nil</code>, representing "<code>[]</code>". If -%% <code>Node</code> represents "<code>[<em>E1</em>, <em>E2</em> +%% `nil', representing "`[]'". If +%% `Node' represents "<code>[<em>E1</em>, <em>E2</em> %% ...]</code>", the result will represent "<code>[<em>E2</em> -%% ...]</code>", and if <code>Node</code> represents +%% ...]</code>", and if `Node' represents %% "<code>[<em>Head</em> | <em>Tail</em>]</code>", the result will %% represent "<code><em>Tail</em></code>". %% @@ -2358,10 +2193,8 @@ list_tail(Node) -> %% ===================================================================== -%% @spec is_list_skeleton(syntaxTree()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> has type -%% <code>list</code> or <code>nil</code>, otherwise <code>false</code>. +%% @doc Returns `true' if `Node' has type +%% `list' or `nil', otherwise `false'. %% %% @see list/2 %% @see nil/0 @@ -2377,24 +2210,22 @@ is_list_skeleton(Node) -> %% ===================================================================== -%% @spec is_proper_list(Node::syntaxTree()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> represents a -%% proper list, and <code>false</code> otherwise. A proper list is a -%% list skeleton either on the form "<code>[]</code>" or +%% @doc Returns `true' if `Node' represents a +%% proper list, and `false' otherwise. A proper list is a +%% list skeleton either on the form "`[]'" or %% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", or "<code>[... | -%% <em>Tail</em>]</code>" where recursively <code>Tail</code> also +%% <em>Tail</em>]</code>" where recursively `Tail' also %% represents a proper list. %% -%% <p>Note: Since <code>Node</code> is a syntax tree, the actual +%% Note: Since `Node' is a syntax tree, the actual %% run-time values corresponding to its subtrees may often be partially -%% or completely unknown. Thus, if <code>Node</code> represents e.g. -%% "<code>[... | Ns]</code>" (where <code>Ns</code> is a variable), then -%% the function will return <code>false</code>, because it is not known -%% whether <code>Ns</code> will be bound to a list at run-time. If -%% <code>Node</code> instead represents e.g. "<code>[1, 2, 3]</code>" or -%% "<code>[A | []]</code>", then the function will return -%% <code>true</code>.</p> +%% or completely unknown. Thus, if `Node' represents e.g. +%% "`[... | Ns]'" (where `Ns' is a variable), then +%% the function will return `false', because it is not known +%% whether `Ns' will be bound to a list at run-time. If +%% `Node' instead represents e.g. "`[1, 2, 3]'" or +%% "`[A | []]'", then the function will return +%% `true'. %% %% @see list/2 @@ -2417,14 +2248,11 @@ is_proper_list(Node) -> %% ===================================================================== -%% @spec list_elements(Node::syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of element subtrees of a list skeleton. -%% <code>Node</code> must represent a proper list. E.g., if -%% <code>Node</code> represents "<code>[<em>X1</em>, <em>X2</em> | +%% `Node' must represent a proper list. E.g., if +%% `Node' represents "<code>[<em>X1</em>, <em>X2</em> | %% [<em>X3</em>, <em>X4</em> | []]</code>", then -%% <code>list_elements(Node)</code> yields the list <code>[X1, X2, X3, -%% X4]</code>. +%% `list_elements(Node)' yields the list `[X1, X2, X3, X4]'. %% %% @see list/2 %% @see is_proper_list/1 @@ -2450,17 +2278,15 @@ list_elements(Node, As) -> %% ===================================================================== -%% @spec list_length(Node::syntaxTree()) -> integer() -%% %% @doc Returns the number of element subtrees of a list skeleton. -%% <code>Node</code> must represent a proper list. E.g., if -%% <code>Node</code> represents "<code>[X1 | [X2, X3 | [X4, X5, -%% X6]]]</code>", then <code>list_length(Node)</code> returns the +%% `Node' must represent a proper list. E.g., if +%% `Node' represents "`[X1 | [X2, X3 | [X4, X5, +%% X6]]]'", then `list_length(Node)' returns the %% integer 6. %% -%% <p>Note: this is equivalent to -%% <code>length(list_elements(Node))</code>, but potentially more -%% efficient.</p> +%% Note: this is equivalent to +%% `length(list_elements(Node))', but potentially more +%% efficient. %% %% @see list/2 %% @see is_proper_list/1 @@ -2487,18 +2313,16 @@ list_length(Node, A) -> %% ===================================================================== -%% @spec normalize_list(Node::syntaxTree()) -> syntaxTree() -%% %% @doc Expands an abstract list skeleton to its most explicit form. If -%% <code>Node</code> represents "<code>[<em>E1</em>, ..., <em>En</em> | +%% `Node' represents "<code>[<em>E1</em>, ..., <em>En</em> | %% <em>Tail</em>]</code>", the result represents "<code>[<em>E1</em> | %% ... [<em>En</em> | <em>Tail1</em>] ... ]</code>", where -%% <code>Tail1</code> is the result of -%% <code>normalize_list(Tail)</code>. If <code>Node</code> represents +%% `Tail1' is the result of +%% `normalize_list(Tail)'. If `Node' represents %% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", the result simply %% represents "<code>[<em>E1</em> | ... [<em>En</em> | []] ... -%% ]</code>". If <code>Node</code> does not represent a list skeleton, -%% <code>Node</code> itself is returned. +%% ]</code>". If `Node' does not represent a list skeleton, +%% `Node' itself is returned. %% %% @see list/2 %% @see compact_list/1 @@ -2528,16 +2352,14 @@ normalize_list_1(Es, Tail) -> %% ===================================================================== -%% @spec compact_list(Node::syntaxTree()) -> syntaxTree() -%% %% @doc Yields the most compact form for an abstract list skeleton. The %% result either represents "<code>[<em>E1</em>, ..., <em>En</em> | -%% <em>Tail</em>]</code>", where <code>Tail</code> is not a list +%% <em>Tail</em>]</code>", where `Tail' is not a list %% skeleton, or otherwise simply "<code>[<em>E1</em>, ..., -%% <em>En</em>]</code>". Annotations on subtrees of <code>Node</code> +%% <em>En</em>]</code>". Annotations on subtrees of `Node' %% that represent list skeletons may be lost, but comments will be -%% propagated to the result. Returns <code>Node</code> itself if -%% <code>Node</code> does not represent a list skeleton. +%% propagated to the result. Returns `Node' itself if +%% `Node' does not represent a list skeleton. %% %% @see list/2 %% @see normalize_list/1 @@ -2575,10 +2397,8 @@ compact_list(Node) -> %% ===================================================================== -%% @spec binary(Fields::[syntaxTree()]) -> syntaxTree() -%% %% @doc Creates an abstract binary-object template. If -%% <code>Fields</code> is <code>[F1, ..., Fn]</code>, the result +%% `Fields' is `[F1, ..., Fn]', the result %% represents "<code><<<em>F1</em>, ..., %% <em>Fn</em>>></code>". %% @@ -2611,10 +2431,7 @@ revert_binary(Node) -> %% ===================================================================== -%% @spec binary_fields(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of field subtrees of a <code>binary</code> -%% node. +%% @doc Returns the list of field subtrees of a `binary' node. %% %% @see binary/1 %% @see binary_field/2 @@ -2631,7 +2448,6 @@ binary_fields(Node) -> %% ===================================================================== -%% @spec binary_field(Body) -> syntaxTree() %% @equiv binary_field(Body, []) -spec binary_field(syntaxTree()) -> syntaxTree(). @@ -2641,15 +2457,11 @@ binary_field(Body) -> %% ===================================================================== -%% @spec binary_field(Body::syntaxTree(), Size, -%% Types::[syntaxTree()]) -> syntaxTree() -%% Size = none | syntaxTree() -%% %% @doc Creates an abstract binary template field. -%% If <code>Size</code> is <code>none</code>, this is equivalent to -%% "<code>binary_field(Body, Types)</code>", otherwise it is -%% equivalent to "<code>binary_field(size_qualifier(Body, Size), -%% Types)</code>". +%% If `Size' is `none', this is equivalent to +%% "`binary_field(Body, Types)'", otherwise it is +%% equivalent to "`binary_field(size_qualifier(Body, Size), +%% Types)'". %% %% (This is a utility function.) %% @@ -2667,13 +2479,10 @@ binary_field(Body, Size, Types) -> %% ===================================================================== -%% @spec binary_field(Body::syntaxTree(), Types::[syntaxTree()]) -> -%% syntaxTree() -%% %% @doc Creates an abstract binary template field. If -%% <code>Types</code> is the empty list, the result simply represents -%% "<code><em>Body</em></code>", otherwise, if <code>Types</code> is -%% <code>[T1, ..., Tn]</code>, the result represents +%% `Types' is the empty list, the result simply represents +%% "<code><em>Body</em></code>", otherwise, if `Types' is +%% `[T1, ..., Tn]', the result represents %% "<code><em>Body</em>/<em>T1</em>-...-<em>Tn</em></code>". %% %% @see binary/1 @@ -2727,9 +2536,7 @@ revert_binary_field(Node) -> %% ===================================================================== -%% @spec binary_field_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>binary_field</code>. +%% @doc Returns the body subtree of a `binary_field'. %% %% @see binary_field/2 @@ -2749,12 +2556,10 @@ binary_field_body(Node) -> %% ===================================================================== -%% @spec binary_field_types(Node::syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of type-specifier subtrees of a -%% <code>binary_field</code> node. If <code>Node</code> represents +%% `binary_field' node. If `Node' represents %% "<code>.../<em>T1</em>, ..., <em>Tn</em></code>", the result is -%% <code>[T1, ..., Tn]</code>, otherwise the result is the empty list. +%% `[T1, ..., Tn]', otherwise the result is the empty list. %% %% @see binary_field/2 @@ -2774,14 +2579,12 @@ binary_field_types(Node) -> %% ===================================================================== -%% @spec binary_field_size(Node::syntaxTree()) -> none | syntaxTree() -%% %% @doc Returns the size specifier subtree of a -%% <code>binary_field</code> node, if any. If <code>Node</code> +%% `binary_field' node, if any. If `Node' %% represents "<code><em>Body</em>:<em>Size</em></code>" or %% "<code><em>Body</em>:<em>Size</em>/<em>T1</em>, ..., -%% <em>Tn</em></code>", the result is <code>Size</code>, otherwise -%% <code>none</code> is returned. +%% <em>Tn</em></code>", the result is `Size', otherwise +%% `none' is returned. %% %% (This is a utility function.) %% @@ -2810,9 +2613,6 @@ binary_field_size(Node) -> %% ===================================================================== -%% @spec size_qualifier(Body::syntaxTree(), Size::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract size qualifier. The result represents %% "<code><em>Body</em>:<em>Size</em></code>". %% @@ -2834,10 +2634,7 @@ size_qualifier(Body, Size) -> %% ===================================================================== -%% @spec size_qualifier_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>size_qualifier</code> -%% node. +%% @doc Returns the body subtree of a `size_qualifier' node. %% %% @see size_qualifier/2 @@ -2848,10 +2645,8 @@ size_qualifier_body(Node) -> %% ===================================================================== -%% @spec size_qualifier_argument(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the argument subtree (the size) of a -%% <code>size_qualifier</code> node. +%% `size_qualifier' node. %% %% @see size_qualifier/2 @@ -2862,16 +2657,14 @@ size_qualifier_argument(Node) -> %% ===================================================================== -%% @spec error_marker(Error::term()) -> syntaxTree() -%% %% @doc Creates an abstract error marker. The result represents an %% occurrence of an error in the source code, with an associated Erlang -%% I/O ErrorInfo structure given by <code>Error</code> (see module +%% I/O ErrorInfo structure given by `Error' (see module %% {@link //stdlib/io} for details). Error markers are regarded as source %% code forms, but have no defined lexical form. %% -%% <p>Note: this is supported only for backwards compatibility with -%% existing parsers and tools.</p> +%% Note: this is supported only for backwards compatibility with +%% existing parsers and tools. %% %% @see error_marker_info/1 %% @see warning_marker/1 @@ -2902,10 +2695,7 @@ revert_error_marker(Node) -> %% ===================================================================== -%% @spec error_marker_info(syntaxTree()) -> term() -%% -%% @doc Returns the ErrorInfo structure of an <code>error_marker</code> -%% node. +%% @doc Returns the ErrorInfo structure of an `error_marker' node. %% %% @see error_marker/1 @@ -2921,16 +2711,14 @@ error_marker_info(Node) -> %% ===================================================================== -%% @spec warning_marker(Error::term()) -> syntaxTree() -%% %% @doc Creates an abstract warning marker. The result represents an %% occurrence of a possible problem in the source code, with an -%% associated Erlang I/O ErrorInfo structure given by <code>Error</code> +%% associated Erlang I/O ErrorInfo structure given by `Error' %% (see module {@link //stdlib/io} for details). Warning markers are %% regarded as source code forms, but have no defined lexical form. %% -%% <p>Note: this is supported only for backwards compatibility with -%% existing parsers and tools.</p> +%% Note: this is supported only for backwards compatibility with +%% existing parsers and tools. %% %% @see warning_marker_info/1 %% @see error_marker/1 @@ -2961,10 +2749,7 @@ revert_warning_marker(Node) -> %% ===================================================================== -%% @spec warning_marker_info(syntaxTree()) -> term() -%% -%% @doc Returns the ErrorInfo structure of a <code>warning_marker</code> -%% node. +%% @doc Returns the ErrorInfo structure of a `warning_marker' node. %% %% @see warning_marker/1 @@ -2980,16 +2765,14 @@ warning_marker_info(Node) -> %% ===================================================================== -%% @spec eof_marker() -> syntaxTree() -%% %% @doc Creates an abstract end-of-file marker. This represents the %% end of input when reading a sequence of source code forms. An %% end-of-file marker is itself regarded as a source code form %% (namely, the last in any sequence in which it occurs). It has no %% defined lexical form. %% -%% <p>Note: this is retained only for backwards compatibility with -%% existing parsers and tools.</p> +%% Note: this is retained only for backwards compatibility with +%% existing parsers and tools. %% %% @see error_marker/1 %% @see warning_marker/1 @@ -3013,7 +2796,6 @@ revert_eof_marker(Node) -> %% ===================================================================== -%% @spec attribute(Name) -> syntaxTree() %% @equiv attribute(Name, none) -spec attribute(syntaxTree()) -> syntaxTree(). @@ -3023,23 +2805,20 @@ attribute(Name) -> %% ===================================================================== -%% @spec attribute(Name::syntaxTree(), Arguments) -> syntaxTree() -%% Arguments = none | [syntaxTree()] -%% %% @doc Creates an abstract program attribute. If -%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result +%% `Arguments' is `[A1, ..., An]', the result %% represents "<code>-<em>Name</em>(<em>A1</em>, ..., -%% <em>An</em>).</code>". Otherwise, if <code>Arguments</code> is -%% <code>none</code>, the result represents +%% <em>An</em>).</code>". Otherwise, if `Arguments' is +%% `none', the result represents %% "<code>-<em>Name</em>.</code>". The latter form makes it possible %% to represent preprocessor directives such as -%% "<code>-endif.</code>". Attributes are source code forms. +%% "`-endif.'". Attributes are source code forms. %% -%% <p>Note: The preprocessor macro definition directive +%% Note: The preprocessor macro definition directive %% "<code>-define(<em>Name</em>, <em>Body</em>).</code>" has relatively -%% few requirements on the syntactical form of <code>Body</code> (viewed -%% as a sequence of tokens). The <code>text</code> node type can be used -%% for a <code>Body</code> that is not a normal Erlang construct.</p> +%% few requirements on the syntactical form of `Body' (viewed +%% as a sequence of tokens). The `text' node type can be used +%% for a `Body' that is not a normal Erlang construct. %% %% @see attribute/1 %% @see attribute_name/1 @@ -3233,9 +3012,7 @@ revert_module_name(A) -> %% ===================================================================== -%% @spec attribute_name(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the name subtree of an <code>attribute</code> node. +%% @doc Returns the name subtree of an `attribute' node. %% %% @see attribute/1 @@ -3251,15 +3028,12 @@ attribute_name(Node) -> %% ===================================================================== -%% @spec attribute_arguments(Node::syntaxTree()) -> -%% none | [syntaxTree()] -%% %% @doc Returns the list of argument subtrees of an -%% <code>attribute</code> node, if any. If <code>Node</code> +%% `attribute' node, if any. If `Node' %% represents "<code>-<em>Name</em>.</code>", the result is -%% <code>none</code>. Otherwise, if <code>Node</code> represents +%% `none'. Otherwise, if `Node' represents %% "<code>-<em>Name</em>(<em>E1</em>, ..., <em>En</em>).</code>", -%% <code>[E1, ..., E1]</code> is returned. +%% `[E1, ..., E1]' is returned. %% %% @see attribute/1 @@ -3326,9 +3100,6 @@ attribute_arguments(Node) -> %% ===================================================================== -%% @spec arity_qualifier(Body::syntaxTree(), Arity::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract arity qualifier. The result represents %% "<code><em>Body</em>/<em>Arity</em></code>". %% @@ -3350,10 +3121,7 @@ arity_qualifier(Body, Arity) -> %% ===================================================================== -%% @spec arity_qualifier_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of an <code>arity_qualifier</code> -%% node. +%% @doc Returns the body subtree of an `arity_qualifier' node. %% %% @see arity_qualifier/2 @@ -3364,10 +3132,8 @@ arity_qualifier_body(Node) -> %% ===================================================================== -%% @spec arity_qualifier_argument(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the argument (the arity) subtree of an -%% <code>arity_qualifier</code> node. +%% `arity_qualifier' node. %% %% @see arity_qualifier/2 @@ -3378,9 +3144,6 @@ arity_qualifier_argument(Node) -> %% ===================================================================== -%% @spec module_qualifier(Module::syntaxTree(), Body::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract module qualifier. The result represents %% "<code><em>Module</em>:<em>Body</em></code>". %% @@ -3414,10 +3177,8 @@ revert_module_qualifier(Node) -> %% ===================================================================== -%% @spec module_qualifier_argument(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the argument (the module) subtree of a -%% <code>module_qualifier</code> node. +%% `module_qualifier' node. %% %% @see module_qualifier/2 @@ -3433,10 +3194,7 @@ module_qualifier_argument(Node) -> %% ===================================================================== -%% @spec module_qualifier_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>module_qualifier</code> -%% node. +%% @doc Returns the body subtree of a `module_qualifier' node. %% %% @see module_qualifier/2 @@ -3452,11 +3210,9 @@ module_qualifier_body(Node) -> %% ===================================================================== -%% @spec qualified_name(Segments::[syntaxTree()]) -> syntaxTree() -%% %% @doc Creates an abstract qualified name. The result represents %% "<code><em>S1</em>.<em>S2</em>. ... .<em>Sn</em></code>", if -%% <code>Segments</code> is <code>[S1, S2, ..., Sn]</code>. +%% `Segments' is `[S1, S2, ..., Sn]'. %% %% @see qualified_name_segments/1 @@ -3484,10 +3240,8 @@ revert_qualified_name(Node) -> %% ===================================================================== -%% @spec qualified_name_segments(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of name segments of a -%% <code>qualified_name</code> node. +%% `qualified_name' node. %% %% @see qualified_name/1 @@ -3503,13 +3257,10 @@ qualified_name_segments(Node) -> %% ===================================================================== -%% @spec function(Name::syntaxTree(), Clauses::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract function definition. If <code>Clauses</code> -%% is <code>[C1, ..., Cn]</code>, the result represents +%% @doc Creates an abstract function definition. If `Clauses' +%% is `[C1, ..., Cn]', the result represents %% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em> -%% <em>Cn</em>.</code>". More exactly, if each <code>Ci</code> +%% <em>Cn</em>.</code>". More exactly, if each `Ci' %% represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) <em>Gi</em> -> %% <em>Bi</em></code>", then the result represents %% "<code><em>Name</em>(<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> -> @@ -3523,13 +3274,12 @@ qualified_name_segments(Node) -> %% @see is_form/1 %% @see rule/2 --record(function, {name, clauses}). -%% XXX: This one is problematic because there is a tuple with the same -%% tag and size that comes from 'erl_parse' -%% -record(function, {name :: syntaxTree(), clauses :: [syntaxTree()]}). +%% Don't use the name 'function' for this record, to avoid confusion with +%% the tuples on the form {function,Name,Arity} used by erl_parse. +-record(func, {name :: syntaxTree(), clauses :: [syntaxTree()]}). %% type(Node) = function -%% data(Node) = #function{name :: Name, clauses :: Clauses} +%% data(Node) = #func{name :: Name, clauses :: Clauses} %% %% Name = syntaxTree() %% Clauses = [syntaxTree()] @@ -3556,7 +3306,7 @@ qualified_name_segments(Node) -> -spec function(syntaxTree(), [syntaxTree()]) -> syntaxTree(). function(Name, Clauses) -> - tree(function, #function{name = Name, clauses = Clauses}). + tree(function, #func{name = Name, clauses = Clauses}). revert_function(Node) -> Name = function_name(Node), @@ -3572,9 +3322,7 @@ revert_function(Node) -> %% ===================================================================== -%% @spec function_name(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the name subtree of a <code>function</code> node. +%% @doc Returns the name subtree of a `function' node. %% %% @see function/2 @@ -3585,15 +3333,12 @@ function_name(Node) -> {function, Pos, Name, _, _} -> set_pos(atom(Name), Pos); Node1 -> - (data(Node1))#function.name + (data(Node1))#func.name end. %% ===================================================================== -%% @spec function_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of a <code>function</code> -%% node. +%% @doc Returns the list of clause subtrees of a `function' node. %% %% @see function/2 @@ -3604,21 +3349,19 @@ function_clauses(Node) -> {function, _, _, _, Clauses} -> Clauses; Node1 -> - (data(Node1))#function.clauses + (data(Node1))#func.clauses end. %% ===================================================================== -%% @spec function_arity(Node::syntaxTree()) -> integer() -%% -%% @doc Returns the arity of a <code>function</code> node. The result +%% @doc Returns the arity of a `function' node. The result %% is the number of parameter patterns in the first clause of the %% function; subsequent clauses are ignored. %% -%% <p>An exception is thrown if <code>function_clauses(Node)</code> +%% An exception is thrown if `function_clauses(Node)' %% returns an empty list, or if the first element of that list is not -%% a syntax tree <code>C</code> of type <code>clause</code> such that -%% <code>clause_patterns(C)</code> is a nonempty list.</p> +%% a syntax tree `C' of type `clause' such that +%% `clause_patterns(C)' is a nonempty list. %% %% @see function/2 %% @see function_clauses/1 @@ -3634,7 +3377,6 @@ function_arity(Node) -> %% ===================================================================== -%% @spec clause(Guard, Body) -> syntaxTree() %% @equiv clause([], Guard, Body) -type guard() :: 'none' | syntaxTree() | [syntaxTree()] | [[syntaxTree()]]. @@ -3646,34 +3388,28 @@ clause(Guard, Body) -> %% ===================================================================== -%% @spec clause(Patterns::[syntaxTree()], Guard, -%% Body::[syntaxTree()]) -> syntaxTree() -%% Guard = none | syntaxTree() -%% | [syntaxTree()] | [[syntaxTree()]] -%% -%% @doc Creates an abstract clause. If <code>Patterns</code> is -%% <code>[P1, ..., Pn]</code> and <code>Body</code> is <code>[B1, ..., -%% Bm]</code>, then if <code>Guard</code> is <code>none</code>, the +%% @doc Creates an abstract clause. If `Patterns' is +%% `[P1, ..., Pn]' and `Body' is `[B1, ..., +%% Bm]', then if `Guard' is `none', the %% result represents "<code>(<em>P1</em>, ..., <em>Pn</em>) -> %% <em>B1</em>, ..., <em>Bm</em></code>", otherwise, unless -%% <code>Guard</code> is a list, the result represents +%% `Guard' is a list, the result represents %% "<code>(<em>P1</em>, ..., <em>Pn</em>) when <em>Guard</em> -> %% <em>B1</em>, ..., <em>Bm</em></code>". %% -%% <p>For simplicity, the <code>Guard</code> argument may also be any +%% For simplicity, the `Guard' argument may also be any %% of the following: %% <ul> -%% <li>An empty list <code>[]</code>. This is equivalent to passing -%% <code>none</code>.</li> -%% <li>A nonempty list <code>[E1, ..., Ej]</code> of syntax trees. -%% This is equivalent to passing <code>conjunction([E1, ..., -%% Ej])</code>.</li> -%% <li>A nonempty list of lists of syntax trees <code>[[E1_1, ..., -%% E1_k1], ..., [Ej_1, ..., Ej_kj]]</code>, which is equivalent -%% to passing <code>disjunction([conjunction([E1_1, ..., -%% E1_k1]), ..., conjunction([Ej_1, ..., Ej_kj])])</code>.</li> +%% <li>An empty list `[]'. This is equivalent to passing +%% `none'.</li> +%% <li>A nonempty list `[E1, ..., Ej]' of syntax trees. +%% This is equivalent to passing `conjunction([E1, ..., +%% Ej])'.</li> +%% <li>A nonempty list of lists of syntax trees `[[E1_1, ..., +%% E1_k1], ..., [Ej_1, ..., Ej_kj]]', which is equivalent +%% to passing `disjunction([conjunction([E1_1, ..., +%% E1_k1]), ..., conjunction([Ej_1, ..., Ej_kj])])'.</li> %% </ul> -%% </p> %% %% @see clause/2 %% @see clause_patterns/1 @@ -3789,10 +3525,7 @@ unfold_try_clause({clause, Pos, [{tuple, _, [C, V, _]}], %% ===================================================================== -%% @spec clause_patterns(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of pattern subtrees of a <code>clause</code> -%% node. +%% @doc Returns the list of pattern subtrees of a `clause' node. %% %% @see clause/3 @@ -3808,13 +3541,11 @@ clause_patterns(Node) -> %% ===================================================================== -%% @spec clause_guard(Node::syntaxTree()) -> none | syntaxTree() -%% -%% @doc Returns the guard subtree of a <code>clause</code> node, if -%% any. If <code>Node</code> represents "<code>(<em>P1</em>, ..., +%% @doc Returns the guard subtree of a `clause' node, if +%% any. If `Node' represents "<code>(<em>P1</em>, ..., %% <em>Pn</em>) when <em>Guard</em> -> <em>B1</em>, ..., -%% <em>Bm</em></code>", <code>Guard</code> is returned. Otherwise, the -%% result is <code>none</code>. +%% <em>Bm</em></code>", `Guard' is returned. Otherwise, the +%% result is `none'. %% %% @see clause/3 @@ -3836,10 +3567,7 @@ clause_guard(Node) -> %% ===================================================================== -%% @spec clause_body(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Return the list of body subtrees of a <code>clause</code> -%% node. +%% @doc Return the list of body subtrees of a `clause' node. %% %% @see clause/3 @@ -3855,10 +3583,8 @@ clause_body(Node) -> %% ===================================================================== -%% @spec disjunction(List::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract disjunction. If <code>List</code> is -%% <code>[E1, ..., En]</code>, the result represents +%% @doc Creates an abstract disjunction. If `List' is +%% `[E1, ..., En]', the result represents %% "<code><em>E1</em>; ...; <em>En</em></code>". %% %% @see disjunction_body/1 @@ -3874,10 +3600,8 @@ disjunction(Tests) -> %% ===================================================================== -%% @spec disjunction_body(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of body subtrees of a -%% <code>disjunction</code> node. +%% `disjunction' node. %% %% @see disjunction/1 @@ -3888,10 +3612,8 @@ disjunction_body(Node) -> %% ===================================================================== -%% @spec conjunction(List::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract conjunction. If <code>List</code> is -%% <code>[E1, ..., En]</code>, the result represents +%% @doc Creates an abstract conjunction. If `List' is +%% `[E1, ..., En]', the result represents %% "<code><em>E1</em>, ..., <em>En</em></code>". %% %% @see conjunction_body/1 @@ -3907,10 +3629,8 @@ conjunction(Tests) -> %% ===================================================================== -%% @spec conjunction_body(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of body subtrees of a -%% <code>conjunction</code> node. +%% `conjunction' node. %% %% @see conjunction/1 @@ -3921,8 +3641,6 @@ conjunction_body(Node) -> %% ===================================================================== -%% @spec catch_expr(Expr::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract catch-expression. The result represents %% "<code>catch <em>Expr</em></code>". %% @@ -3949,9 +3667,7 @@ revert_catch_expr(Node) -> %% ===================================================================== -%% @spec catch_expr_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>catch_expr</code> node. +%% @doc Returns the body subtree of a `catch_expr' node. %% %% @see catch_expr/1 @@ -3967,9 +3683,6 @@ catch_expr_body(Node) -> %% ===================================================================== -%% @spec match_expr(Pattern::syntaxTree(), Body::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract match-expression. The result represents %% "<code><em>Pattern</em> = <em>Body</em></code>". %% @@ -4002,9 +3715,7 @@ revert_match_expr(Node) -> %% ===================================================================== -%% @spec match_expr_pattern(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the pattern subtree of a <code>match_expr</code> node. +%% @doc Returns the pattern subtree of a `match_expr' node. %% %% @see match_expr/2 @@ -4020,9 +3731,7 @@ match_expr_pattern(Node) -> %% ===================================================================== -%% @spec match_expr_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>match_expr</code> node. +%% @doc Returns the body subtree of a `match_expr' node. %% %% @see match_expr/2 @@ -4038,15 +3747,12 @@ match_expr_body(Node) -> %% ===================================================================== -%% @spec operator(Name) -> syntaxTree() -%% Name = atom() | string() -%% %% @doc Creates an abstract operator. The name of the operator is the -%% character sequence represented by <code>Name</code>. This is +%% character sequence represented by `Name'. This is %% analogous to the print name of an atom, but an operator is never %% written within single-quotes; e.g., the result of -%% <code>operator('++')</code> represents "<code>++</code>" rather -%% than "<code>'++'</code>". +%% `operator('++')' represents "`++'" rather +%% than "`'++''". %% %% @see operator_name/1 %% @see operator_literal/1 @@ -4064,9 +3770,7 @@ operator(Name) -> %% ===================================================================== -%% @spec operator_name(syntaxTree()) -> atom() -%% -%% @doc Returns the name of an <code>operator</code> node. Note that +%% @doc Returns the name of an `operator' node. Note that %% the name is returned as an atom. %% %% @see operator/1 @@ -4078,11 +3782,8 @@ operator_name(Node) -> %% ===================================================================== -%% @spec operator_literal(syntaxTree()) -> string() -%% %% @doc Returns the literal string represented by an -%% <code>operator</code> node. This is simply the operator name as a -%% string. +%% `operator' node. This is simply the operator name as a string. %% %% @see operator/1 @@ -4093,9 +3794,6 @@ operator_literal(Node) -> %% ===================================================================== -%% @spec infix_expr(Left::syntaxTree(), Operator::syntaxTree(), -%% Right::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract infix operator expression. The result %% represents "<code><em>Left</em> <em>Operator</em> %% <em>Right</em></code>". @@ -4144,10 +3842,8 @@ revert_infix_expr(Node) -> %% ===================================================================== -%% @spec infix_expr_left(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the left argument subtree of an -%% <code>infix_expr</code> node. +%% `infix_expr' node. %% %% @see infix_expr/3 @@ -4163,10 +3859,7 @@ infix_expr_left(Node) -> %% ===================================================================== -%% @spec infix_expr_operator(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the operator subtree of an <code>infix_expr</code> -%% node. +%% @doc Returns the operator subtree of an `infix_expr' node. %% %% @see infix_expr/3 @@ -4182,10 +3875,8 @@ infix_expr_operator(Node) -> %% ===================================================================== -%% @spec infix_expr_right(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the right argument subtree of an -%% <code>infix_expr</code> node. +%% `infix_expr' node. %% %% @see infix_expr/3 @@ -4201,9 +3892,6 @@ infix_expr_right(Node) -> %% ===================================================================== -%% @spec prefix_expr(Operator::syntaxTree(), Argument::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract prefix operator expression. The result %% represents "<code><em>Operator</em> <em>Argument</em></code>". %% @@ -4247,10 +3935,7 @@ revert_prefix_expr(Node) -> %% ===================================================================== -%% @spec prefix_expr_operator(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the operator subtree of a <code>prefix_expr</code> -%% node. +%% @doc Returns the operator subtree of a `prefix_expr' node. %% %% @see prefix_expr/2 @@ -4266,10 +3951,7 @@ prefix_expr_operator(Node) -> %% ===================================================================== -%% @spec prefix_expr_argument(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the argument subtree of a <code>prefix_expr</code> -%% node. +%% @doc Returns the argument subtree of a `prefix_expr' node. %% %% @see prefix_expr/2 @@ -4285,7 +3967,6 @@ prefix_expr_argument(Node) -> %% ===================================================================== -%% @spec record_field(Name) -> syntaxTree() %% @equiv record_field(Name, none) -spec record_field(syntaxTree()) -> syntaxTree(). @@ -4295,11 +3976,8 @@ record_field(Name) -> %% ===================================================================== -%% @spec record_field(Name::syntaxTree(), Value) -> syntaxTree() -%% Value = none | syntaxTree() -%% %% @doc Creates an abstract record field specification. If -%% <code>Value</code> is <code>none</code>, the result represents +%% `Value' is `none', the result represents %% simply "<code><em>Name</em></code>", otherwise it represents %% "<code><em>Name</em> = <em>Value</em></code>". %% @@ -4321,9 +3999,7 @@ record_field(Name, Value) -> %% ===================================================================== -%% @spec record_field_name(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the name subtree of a <code>record_field</code> node. +%% @doc Returns the name subtree of a `record_field' node. %% %% @see record_field/2 @@ -4334,13 +4010,11 @@ record_field_name(Node) -> %% ===================================================================== -%% @spec record_field_value(syntaxTree()) -> none | syntaxTree() -%% -%% @doc Returns the value subtree of a <code>record_field</code> node, -%% if any. If <code>Node</code> represents -%% "<code><em>Name</em></code>", <code>none</code> is -%% returned. Otherwise, if <code>Node</code> represents -%% "<code><em>Name</em> = <em>Value</em></code>", <code>Value</code> +%% @doc Returns the value subtree of a `record_field' node, +%% if any. If `Node' represents +%% "<code><em>Name</em></code>", `none' is +%% returned. Otherwise, if `Node' represents +%% "<code><em>Name</em> = <em>Value</em></code>", `Value' %% is returned. %% %% @see record_field/2 @@ -4352,15 +4026,12 @@ record_field_value(Node) -> %% ===================================================================== -%% @spec record_index_expr(Type::syntaxTree(), Field::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract record field index expression. The result %% represents "<code>#<em>Type</em>.<em>Field</em></code>". %% -%% <p>(Note: the function name <code>record_index/2</code> is reserved +%% (Note: the function name `record_index/2' is reserved %% by the Erlang compiler, which is why that name could not be used -%% for this constructor.)</p> +%% for this constructor.) %% %% @see record_index_expr_type/1 %% @see record_index_expr_field/1 @@ -4399,10 +4070,7 @@ revert_record_index_expr(Node) -> %% ===================================================================== -%% @spec record_index_expr_type(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the type subtree of a <code>record_index_expr</code> -%% node. +%% @doc Returns the type subtree of a `record_index_expr' node. %% %% @see record_index_expr/2 @@ -4418,10 +4086,7 @@ record_index_expr_type(Node) -> %% ===================================================================== -%% @spec record_index_expr_field(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the field subtree of a <code>record_index_expr</code> -%% node. +%% @doc Returns the field subtree of a `record_index_expr' node. %% %% @see record_index_expr/2 @@ -4437,7 +4102,6 @@ record_index_expr_field(Node) -> %% ===================================================================== -%% @spec record_access(Argument, Field) -> syntaxTree() %% @equiv record_access(Argument, none, Field) -spec record_access(syntaxTree(), syntaxTree()) -> syntaxTree(). @@ -4447,17 +4111,13 @@ record_access(Argument, Field) -> %% ===================================================================== -%% @spec record_access(Argument::syntaxTree(), Type, -%% Field::syntaxTree()) -> syntaxTree() -%% Type = none | syntaxTree() -%% %% @doc Creates an abstract record field access expression. If -%% <code>Type</code> is not <code>none</code>, the result represents +%% `Type' is not `none', the result represents %% "<code><em>Argument</em>#<em>Type</em>.<em>Field</em></code>". %% -%% <p>If <code>Type</code> is <code>none</code>, the result represents +%% If `Type' is `none', the result represents %% "<code><em>Argument</em>.<em>Field</em></code>". This is a special -%% form only allowed within Mnemosyne queries.</p> +%% form only allowed within Mnemosyne queries. %% %% @see record_access/2 %% @see record_access_argument/1 @@ -4512,10 +4172,7 @@ revert_record_access(Node) -> %% ===================================================================== -%% @spec record_access_argument(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the argument subtree of a <code>record_access</code> -%% node. +%% @doc Returns the argument subtree of a `record_access' node. %% %% @see record_access/3 @@ -4533,14 +4190,12 @@ record_access_argument(Node) -> %% ===================================================================== -%% @spec record_access_type(syntaxTree()) -> none | syntaxTree() -%% -%% @doc Returns the type subtree of a <code>record_access</code> node, -%% if any. If <code>Node</code> represents -%% "<code><em>Argument</em>.<em>Field</em></code>", <code>none</code> -%% is returned, otherwise if <code>Node</code> represents +%% @doc Returns the type subtree of a `record_access' node, +%% if any. If `Node' represents +%% "<code><em>Argument</em>.<em>Field</em></code>", `none' +%% is returned, otherwise if `Node' represents %% "<code><em>Argument</em>#<em>Type</em>.<em>Field</em></code>", -%% <code>Type</code> is returned. +%% `Type' is returned. %% %% @see record_access/3 @@ -4558,10 +4213,7 @@ record_access_type(Node) -> %% ===================================================================== -%% @spec record_access_field(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the field subtree of a <code>record_access</code> -%% node. +%% @doc Returns the field subtree of a `record_access' node. %% %% @see record_access/3 @@ -4579,7 +4231,6 @@ record_access_field(Node) -> %% ===================================================================== -%% @spec record_expr(Type, Fields) -> syntaxTree() %% @equiv record_expr(none, Type, Fields) -spec record_expr(syntaxTree(), [syntaxTree()]) -> syntaxTree(). @@ -4589,13 +4240,9 @@ record_expr(Type, Fields) -> %% ===================================================================== -%% @spec record_expr(Argument, Type::syntaxTree(), -%% Fields::[syntaxTree()]) -> syntaxTree() -%% Argument = none | syntaxTree() -%% -%% @doc Creates an abstract record expression. If <code>Fields</code> is -%% <code>[F1, ..., Fn]</code>, then if <code>Argument</code> is -%% <code>none</code>, the result represents +%% @doc Creates an abstract record expression. If `Fields' is +%% `[F1, ..., Fn]', then if `Argument' is +%% `none', the result represents %% "<code>#<em>Type</em>{<em>F1</em>, ..., <em>Fn</em>}</code>", %% otherwise it represents %% "<code><em>Argument</em>#<em>Type</em>{<em>F1</em>, ..., @@ -4661,14 +4308,12 @@ revert_record_expr(Node) -> %% ===================================================================== -%% @spec record_expr_argument(syntaxTree()) -> none | syntaxTree() -%% -%% @doc Returns the argument subtree of a <code>record_expr</code> node, -%% if any. If <code>Node</code> represents -%% "<code>#<em>Type</em>{...}</code>", <code>none</code> is returned. -%% Otherwise, if <code>Node</code> represents +%% @doc Returns the argument subtree of a `record_expr' node, +%% if any. If `Node' represents +%% "<code>#<em>Type</em>{...}</code>", `none' is returned. +%% Otherwise, if `Node' represents %% "<code><em>Argument</em>#<em>Type</em>{...}</code>", -%% <code>Argument</code> is returned. +%% `Argument' is returned. %% %% @see record_expr/3 @@ -4686,9 +4331,7 @@ record_expr_argument(Node) -> %% ===================================================================== -%% @spec record_expr_type(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the type subtree of a <code>record_expr</code> node. +%% @doc Returns the type subtree of a `record_expr' node. %% %% @see record_expr/3 @@ -4706,10 +4349,8 @@ record_expr_type(Node) -> %% ===================================================================== -%% @spec record_expr_fields(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of field subtrees of a -%% <code>record_expr</code> node. +%% `record_expr' node. %% %% @see record_expr/3 @@ -4727,15 +4368,11 @@ record_expr_fields(Node) -> %% ===================================================================== -%% @spec application(Module, Function::syntaxTree(), -%% Arguments::[syntaxTree()]) -> syntaxTree() -%% Module = none | syntaxTree() -%% %% @doc Creates an abstract function application expression. If -%% <code>Module</code> is <code>none</code>, this is call is equivalent -%% to <code>application(Function, Arguments)</code>, otherwise it is -%% equivalent to <code>application(module_qualifier(Module, Function), -%% Arguments)</code>. +%% `Module' is `none', this is call is equivalent +%% to `application(Function, Arguments)', otherwise it is +%% equivalent to `application(module_qualifier(Module, Function), +%% Arguments)'. %% %% (This is a utility function.) %% @@ -4752,11 +4389,8 @@ application(Module, Name, Arguments) -> %% ===================================================================== -%% @spec application(Operator::syntaxTree(), -%% Arguments::[syntaxTree()]) -> syntaxTree() -%% %% @doc Creates an abstract function application expression. If -%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result +%% `Arguments' is `[A1, ..., An]', the result %% represents "<code><em>Operator</em>(<em>A1</em>, ..., %% <em>An</em>)</code>". %% @@ -4794,14 +4428,11 @@ revert_application(Node) -> %% ===================================================================== -%% @spec application_operator(syntaxTree()) -> syntaxTree() +%% @doc Returns the operator subtree of an `application' node. %% -%% @doc Returns the operator subtree of an <code>application</code> -%% node. -%% -%% <p>Note: if <code>Node</code> represents +%% Note: if `Node' represents %% "<code><em>M</em>:<em>F</em>(...)</code>", then the result is the -%% subtree representing "<code><em>M</em>:<em>F</em></code>".</p> +%% subtree representing "<code><em>M</em>:<em>F</em></code>". %% %% @see application/2 %% @see module_qualifier/2 @@ -4818,10 +4449,8 @@ application_operator(Node) -> %% ===================================================================== -%% @spec application_arguments(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of argument subtrees of an -%% <code>application</code> node. +%% `application' node. %% %% @see application/2 @@ -4837,11 +4466,8 @@ application_arguments(Node) -> %% ===================================================================== -%% @spec list_comp(Template::syntaxTree(), Body::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract list comprehension. If <code>Body</code> is -%% <code>[E1, ..., En]</code>, the result represents +%% @doc Creates an abstract list comprehension. If `Body' is +%% `[E1, ..., En]', the result represents %% "<code>[<em>Template</em> || <em>E1</em>, ..., <em>En</em>]</code>". %% %% @see list_comp_template/1 @@ -4876,9 +4502,7 @@ revert_list_comp(Node) -> %% ===================================================================== -%% @spec list_comp_template(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the template subtree of a <code>list_comp</code> node. +%% @doc Returns the template subtree of a `list_comp' node. %% %% @see list_comp/2 @@ -4894,10 +4518,7 @@ list_comp_template(Node) -> %% ===================================================================== -%% @spec list_comp_body(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of body subtrees of a <code>list_comp</code> -%% node. +%% @doc Returns the list of body subtrees of a `list_comp' node. %% %% @see list_comp/2 @@ -4912,11 +4533,8 @@ list_comp_body(Node) -> end. %% ===================================================================== -%% @spec binary_comp(Template::syntaxTree(), Body::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract binary comprehension. If <code>Body</code> is -%% <code>[E1, ..., En]</code>, the result represents +%% @doc Creates an abstract binary comprehension. If `Body' is +%% `[E1, ..., En]', the result represents %% "<code><<<em>Template</em> || <em>E1</em>, ..., <em>En</em>>></code>". %% %% @see binary_comp_template/1 @@ -4951,9 +4569,7 @@ revert_binary_comp(Node) -> %% ===================================================================== -%% @spec binary_comp_template(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the template subtree of a <code>binary_comp</code> node. +%% @doc Returns the template subtree of a `binary_comp' node. %% %% @see binary_comp/2 @@ -4969,10 +4585,7 @@ binary_comp_template(Node) -> %% ===================================================================== -%% @spec binary_comp_body(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of body subtrees of a <code>binary_comp</code> -%% node. +%% @doc Returns the list of body subtrees of a `binary_comp' node. %% %% @see binary_comp/2 @@ -4988,8 +4601,6 @@ binary_comp_body(Node) -> %% ===================================================================== -%% @spec query_expr(Body::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract Mnemosyne query expression. The result %% represents "<code>query <em>Body</em> end</code>". %% @@ -5018,9 +4629,7 @@ revert_query_expr(Node) -> %% ===================================================================== -%% @spec query_expr_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>query_expr</code> node. +%% @doc Returns the body subtree of a `query_expr' node. %% %% @see query_expr/1 @@ -5036,13 +4645,10 @@ query_expr_body(Node) -> %% ===================================================================== -%% @spec rule(Name::syntaxTree(), Clauses::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract Mnemosyne rule. If <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code>, the results represents +%% @doc Creates an abstract Mnemosyne rule. If `Clauses' is +%% `[C1, ..., Cn]', the results represents %% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em> -%% <em>Cn</em>.</code>". More exactly, if each <code>Ci</code> +%% <em>Cn</em>.</code>". More exactly, if each `Ci' %% represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) <em>Gi</em> -> %% <em>Bi</em></code>", then the result represents %% "<code><em>Name</em>(<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> :- @@ -5097,9 +4703,7 @@ revert_rule(Node) -> %% ===================================================================== -%% @spec rule_name(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the name subtree of a <code>rule</code> node. +%% @doc Returns the name subtree of a `rule' node. %% %% @see rule/2 @@ -5114,9 +4718,7 @@ rule_name(Node) -> end. %% ===================================================================== -%% @spec rule_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of a <code>rule</code> node. +%% @doc Returns the list of clause subtrees of a `rule' node. %% %% @see rule/2 @@ -5131,16 +4733,14 @@ rule_clauses(Node) -> end. %% ===================================================================== -%% @spec rule_arity(Node::syntaxTree()) -> integer() -%% -%% @doc Returns the arity of a <code>rule</code> node. The result is the +%% @doc Returns the arity of a `rule' node. The result is the %% number of parameter patterns in the first clause of the rule; %% subsequent clauses are ignored. %% -%% <p>An exception is thrown if <code>rule_clauses(Node)</code> returns +%% An exception is thrown if `rule_clauses(Node)' returns %% an empty list, or if the first element of that list is not a syntax -%% tree <code>C</code> of type <code>clause</code> such that -%% <code>clause_patterns(C)</code> is a nonempty list.</p> +%% tree `C' of type `clause' such that +%% `clause_patterns(C)' is a nonempty list. %% %% @see rule/2 %% @see rule_clauses/1 @@ -5156,9 +4756,6 @@ rule_arity(Node) -> %% ===================================================================== -%% @spec generator(Pattern::syntaxTree(), Body::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract generator. The result represents %% "<code><em>Pattern</em> <- <em>Body</em></code>". %% @@ -5193,9 +4790,7 @@ revert_generator(Node) -> %% ===================================================================== -%% @spec generator_pattern(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the pattern subtree of a <code>generator</code> node. +%% @doc Returns the pattern subtree of a `generator' node. %% %% @see generator/2 @@ -5211,9 +4806,7 @@ generator_pattern(Node) -> %% ===================================================================== -%% @spec generator_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>generator</code> node. +%% @doc Returns the body subtree of a `generator' node. %% %% @see generator/2 @@ -5229,9 +4822,6 @@ generator_body(Node) -> %% ===================================================================== -%% @spec binary_generator(Pattern::syntaxTree(), Body::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract binary_generator. The result represents %% "<code><em>Pattern</em> <- <em>Body</em></code>". %% @@ -5266,9 +4856,7 @@ revert_binary_generator(Node) -> %% ===================================================================== -%% @spec binary_generator_pattern(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the pattern subtree of a <code>generator</code> node. +%% @doc Returns the pattern subtree of a `generator' node. %% %% @see binary_generator/2 @@ -5284,9 +4872,7 @@ binary_generator_pattern(Node) -> %% ===================================================================== -%% @spec binary_generator_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>generator</code> node. +%% @doc Returns the body subtree of a `generator' node. %% %% @see binary_generator/2 @@ -5302,10 +4888,8 @@ binary_generator_body(Node) -> %% ===================================================================== -%% @spec block_expr(Body::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract block expression. If <code>Body</code> is -%% <code>[B1, ..., Bn]</code>, the result represents "<code>begin +%% @doc Creates an abstract block expression. If `Body' is +%% `[B1, ..., Bn]', the result represents "<code>begin %% <em>B1</em>, ..., <em>Bn</em> end</code>". %% %% @see block_expr_body/1 @@ -5321,7 +4905,7 @@ binary_generator_body(Node) -> %% %% Body = [erl_parse()] \ [] --spec block_expr(Body::[syntaxTree()]) -> syntaxTree(). +-spec block_expr([syntaxTree()]) -> syntaxTree(). block_expr(Body) -> tree(block_expr, Body). @@ -5333,10 +4917,7 @@ revert_block_expr(Node) -> %% ===================================================================== -%% @spec block_expr_body(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of body subtrees of a <code>block_expr</code> -%% node. +%% @doc Returns the list of body subtrees of a `block_expr' node. %% %% @see block_expr/1 @@ -5352,12 +4933,10 @@ block_expr_body(Node) -> %% ===================================================================== -%% @spec if_expr(Clauses::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract if-expression. If <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code>, the result represents "<code>if +%% @doc Creates an abstract if-expression. If `Clauses' is +%% `[C1, ..., Cn]', the result represents "<code>if %% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each -%% <code>Ci</code> represents "<code>() <em>Gi</em> -> +%% `Ci' represents "<code>() <em>Gi</em> -> %% <em>Bi</em></code>", then the result represents "<code>if %% <em>G1</em> -> <em>B1</em>; ...; <em>Gn</em> -> <em>Bn</em> %% end</code>". @@ -5392,10 +4971,7 @@ revert_if_expr(Node) -> %% ===================================================================== -%% @spec if_expr_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of an <code>if_expr</code> -%% node. +%% @doc Returns the list of clause subtrees of an `if_expr' node. %% %% @see if_expr/1 @@ -5411,13 +4987,10 @@ if_expr_clauses(Node) -> %% ===================================================================== -%% @spec case_expr(Argument::syntaxTree(), Clauses::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract case-expression. If <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code>, the result represents "<code>case +%% @doc Creates an abstract case-expression. If `Clauses' is +%% `[C1, ..., Cn]', the result represents "<code>case %% <em>Argument</em> of <em>C1</em>; ...; <em>Cn</em> end</code>". More -%% exactly, if each <code>Ci</code> represents "<code>(<em>Pi</em>) +%% exactly, if each `Ci' represents "<code>(<em>Pi</em>) %% <em>Gi</em> -> <em>Bi</em></code>", then the result represents %% "<code>case <em>Argument</em> of <em>P1</em> <em>G1</em> -> %% <em>B1</em>; ...; <em>Pn</em> <em>Gn</em> -> <em>Bn</em> end</code>". @@ -5461,9 +5034,7 @@ revert_case_expr(Node) -> %% ===================================================================== -%% @spec case_expr_argument(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the argument subtree of a <code>case_expr</code> node. +%% @doc Returns the argument subtree of a `case_expr' node. %% %% @see case_expr/2 @@ -5479,10 +5050,7 @@ case_expr_argument(Node) -> %% ===================================================================== -%% @spec case_expr_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of a <code>case_expr</code> -%% node. +%% @doc Returns the list of clause subtrees of a `case_expr' node. %% %% @see case_expr/2 @@ -5498,12 +5066,10 @@ case_expr_clauses(Node) -> %% ===================================================================== -%% @spec cond_expr(Clauses::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract cond-expression. If <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code>, the result represents "<code>cond +%% @doc Creates an abstract cond-expression. If `Clauses' is +%% `[C1, ..., Cn]', the result represents "<code>cond %% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each -%% <code>Ci</code> represents "<code>() <em>Ei</em> -> +%% `Ci' represents "<code>() <em>Ei</em> -> %% <em>Bi</em></code>", then the result represents "<code>cond %% <em>E1</em> -> <em>B1</em>; ...; <em>En</em> -> <em>Bn</em> %% end</code>". @@ -5538,10 +5104,7 @@ revert_cond_expr(Node) -> %% ===================================================================== -%% @spec cond_expr_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of a <code>cond_expr</code> -%% node. +%% @doc Returns the list of clause subtrees of a `cond_expr' node. %% %% @see cond_expr/1 @@ -5557,7 +5120,6 @@ cond_expr_clauses(Node) -> %% ===================================================================== -%% @spec receive_expr(Clauses) -> syntaxTree() %% @equiv receive_expr(Clauses, none, []) -spec receive_expr([syntaxTree()]) -> syntaxTree(). @@ -5567,25 +5129,21 @@ receive_expr(Clauses) -> %% ===================================================================== -%% @spec receive_expr(Clauses::[syntaxTree()], Timeout, -%% Action::[syntaxTree()]) -> syntaxTree() -%% Timeout = none | syntaxTree() -%% -%% @doc Creates an abstract receive-expression. If <code>Timeout</code> -%% is <code>none</code>, the result represents "<code>receive -%% <em>C1</em>; ...; <em>Cn</em> end</code>" (the <code>Action</code> -%% argument is ignored). Otherwise, if <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code> and <code>Action</code> is <code>[A1, ..., -%% Am]</code>, the result represents "<code>receive <em>C1</em>; ...; +%% @doc Creates an abstract receive-expression. If `Timeout' +%% is `none', the result represents "<code>receive +%% <em>C1</em>; ...; <em>Cn</em> end</code>" (the `Action' +%% argument is ignored). Otherwise, if `Clauses' is +%% `[C1, ..., Cn]' and `Action' is `[A1, ..., +%% Am]', the result represents "<code>receive <em>C1</em>; ...; %% <em>Cn</em> after <em>Timeout</em> -> <em>A1</em>, ..., <em>Am</em> -%% end</code>". More exactly, if each <code>Ci</code> represents +%% end</code>". More exactly, if each `Ci' represents %% "<code>(<em>Pi</em>) <em>Gi</em> -> <em>Bi</em></code>", then the %% result represents "<code>receive <em>P1</em> <em>G1</em> -> %% <em>B1</em>; ...; <em>Pn</em> <em>Gn</em> -> <em>Bn</em> ... %% end</code>". %% -%% <p>Note that in Erlang, a receive-expression must have at least one -%% clause if no timeout part is specified.</p> +%% Note that in Erlang, a receive-expression must have at least one +%% clause if no timeout part is specified. %% %% @see receive_expr_clauses/1 %% @see receive_expr_timeout/1 @@ -5649,11 +5207,8 @@ revert_receive_expr(Node) -> %% ===================================================================== -%% @spec receive_expr_clauses(syntaxTree()) -> [syntaxTree()] -%% type(Node) = receive_expr -%% %% @doc Returns the list of clause subtrees of a -%% <code>receive_expr</code> node. +%% `receive_expr' node. %% %% @see receive_expr/3 @@ -5671,15 +5226,12 @@ receive_expr_clauses(Node) -> %% ===================================================================== -%% @spec receive_expr_timeout(Node::syntaxTree()) -> Timeout -%% Timeout = none | syntaxTree() -%% -%% @doc Returns the timeout subtree of a <code>receive_expr</code> node, -%% if any. If <code>Node</code> represents "<code>receive <em>C1</em>; -%% ...; <em>Cn</em> end</code>", <code>none</code> is returned. -%% Otherwise, if <code>Node</code> represents "<code>receive +%% @doc Returns the timeout subtree of a `receive_expr' node, +%% if any. If `Node' represents "<code>receive <em>C1</em>; +%% ...; <em>Cn</em> end</code>", `none' is returned. +%% Otherwise, if `Node' represents "<code>receive %% <em>C1</em>; ...; <em>Cn</em> after <em>Timeout</em> -> ... end</code>", -%% <code>Timeout</code> is returned. +%% `Timeout' is returned. %% %% @see receive_expr/3 @@ -5697,10 +5249,8 @@ receive_expr_timeout(Node) -> %% ===================================================================== -%% @spec receive_expr_action(Node::syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of action body subtrees of a -%% <code>receive_expr</code> node. If <code>Node</code> represents +%% `receive_expr' node. If `Node' represents %% "<code>receive <em>C1</em>; ...; <em>Cn</em> end</code>", this is the %% empty list. %% @@ -5720,8 +5270,6 @@ receive_expr_action(Node) -> %% ===================================================================== -%% @spec try_expr(Body::syntaxTree(), Handlers::[syntaxTree()]) -> -%% syntaxTree() %% @equiv try_expr(Body, [], Handlers) -spec try_expr([syntaxTree()], [syntaxTree()]) -> syntaxTree(). @@ -5731,8 +5279,6 @@ try_expr(Body, Handlers) -> %% ===================================================================== -%% @spec try_expr(Body::syntaxTree(), Clauses::[syntaxTree()], -%% Handlers::[syntaxTree()]) -> syntaxTree() %% @equiv try_expr(Body, Clauses, Handlers, []) -spec try_expr([syntaxTree()], [syntaxTree()], [syntaxTree()]) -> syntaxTree(). @@ -5742,8 +5288,6 @@ try_expr(Body, Clauses, Handlers) -> %% ===================================================================== -%% @spec try_after_expr(Body::syntaxTree(), After::[syntaxTree()]) -> -%% syntaxTree() %% @equiv try_expr(Body, [], [], After) -spec try_after_expr([syntaxTree()], [syntaxTree()]) -> syntaxTree(). @@ -5753,30 +5297,26 @@ try_after_expr(Body, After) -> %% ===================================================================== -%% @spec try_expr(Body::[syntaxTree()], Clauses::[syntaxTree()], -%% Handlers::[syntaxTree()], After::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract try-expression. If <code>Body</code> is -%% <code>[B1, ..., Bn]</code>, <code>Clauses</code> is <code>[C1, ..., -%% Cj]</code>, <code>Handlers</code> is <code>[H1, ..., Hk]</code>, and -%% <code>After</code> is <code>[A1, ..., Am]</code>, the result +%% @doc Creates an abstract try-expression. If `Body' is +%% `[B1, ..., Bn]', `Clauses' is `[C1, ..., +%% Cj]', `Handlers' is `[H1, ..., Hk]', and +%% `After' is `[A1, ..., Am]', the result %% represents "<code>try <em>B1</em>, ..., <em>Bn</em> of <em>C1</em>; %% ...; <em>Cj</em> catch <em>H1</em>; ...; <em>Hk</em> after %% <em>A1</em>, ..., <em>Am</em> end</code>". More exactly, if each -%% <code>Ci</code> represents "<code>(<em>CPi</em>) <em>CGi</em> -> -%% <em>CBi</em></code>", and each <code>Hi</code> represents +%% `Ci' represents "<code>(<em>CPi</em>) <em>CGi</em> -> +%% <em>CBi</em></code>", and each `Hi' represents %% "<code>(<em>HPi</em>) <em>HGi</em> -> <em>HBi</em></code>", then the %% result represents "<code>try <em>B1</em>, ..., <em>Bn</em> of %% <em>CP1</em> <em>CG1</em> -> <em>CB1</em>; ...; <em>CPj</em> %% <em>CGj</em> -> <em>CBj</em> catch <em>HP1</em> <em>HG1</em> -> %% <em>HB1</em>; ...; <em>HPk</em> <em>HGk</em> -> <em>HBk</em> after -%% <em>A1</em>, ..., <em>Am</em> end</code>"; cf. -%% <code>case_expr/2</code>. If <code>Clauses</code> is the empty list, -%% the <code>of ...</code> section is left out. If <code>After</code> is -%% the empty list, the <code>after ...</code> section is left out. If -%% <code>Handlers</code> is the empty list, and <code>After</code> is -%% nonempty, the <code>catch ...</code> section is left out. +%% <em>A1</em>, ..., <em>Am</em> end</code>"; see +%% {@link case_expr/2}. If `Clauses' is the empty list, +%% the `of ...' section is left out. If `After' is +%% the empty list, the `after ...' section is left out. If +%% `Handlers' is the empty list, and `After' is +%% nonempty, the `catch ...' section is left out. %% %% @see try_expr_body/1 %% @see try_expr_clauses/1 @@ -5834,10 +5374,7 @@ revert_try_expr(Node) -> %% ===================================================================== -%% @spec try_expr_body(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of body subtrees of a <code>try_expr</code> -%% node. +%% @doc Returns the list of body subtrees of a `try_expr' node. %% %% @see try_expr/4 @@ -5853,10 +5390,8 @@ try_expr_body(Node) -> %% ===================================================================== -%% @spec try_expr_clauses(Node::syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of case-clause subtrees of a -%% <code>try_expr</code> node. If <code>Node</code> represents +%% `try_expr' node. If `Node' represents %% "<code>try <em>Body</em> catch <em>H1</em>; ...; <em>Hn</em> %% end</code>", the result is the empty list. %% @@ -5874,10 +5409,8 @@ try_expr_clauses(Node) -> %% ===================================================================== -%% @spec try_expr_handlers(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of handler-clause subtrees of a -%% <code>try_expr</code> node. +%% `try_expr' node. %% %% @see try_expr/4 @@ -5893,10 +5426,7 @@ try_expr_handlers(Node) -> %% ===================================================================== -%% @spec try_expr_after(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of "after" subtrees of a <code>try_expr</code> -%% node. +%% @doc Returns the list of "after" subtrees of a `try_expr' node. %% %% @see try_expr/4 @@ -5912,9 +5442,6 @@ try_expr_after(Node) -> %% ===================================================================== -%% @spec class_qualifier(Class::syntaxTree(), Body::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract class qualifier. The result represents %% "<code><em>Class</em>:<em>Body</em></code>". %% @@ -5937,10 +5464,8 @@ class_qualifier(Class, Body) -> %% ===================================================================== -%% @spec class_qualifier_argument(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the argument (the class) subtree of a -%% <code>class_qualifier</code> node. +%% `class_qualifier' node. %% %% @see class_qualifier/2 @@ -5951,9 +5476,7 @@ class_qualifier_argument(Node) -> %% ===================================================================== -%% @spec class_qualifier_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>class_qualifier</code> node. +%% @doc Returns the body subtree of a `class_qualifier' node. %% %% @see class_qualifier/2 @@ -5964,13 +5487,10 @@ class_qualifier_body(Node) -> %% ===================================================================== -%% @spec implicit_fun(Name::syntaxTree(), Arity::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract "implicit fun" expression. If -%% <code>Arity</code> is <code>none</code>, this is equivalent to -%% <code>implicit_fun(Name)</code>, otherwise it is equivalent to -%% <code>implicit_fun(arity_qualifier(Name, Arity))</code>. +%% `Arity' is `none', this is equivalent to +%% `implicit_fun(Name)', otherwise it is equivalent to +%% `implicit_fun(arity_qualifier(Name, Arity))'. %% %% (This is a utility function.) %% @@ -5986,14 +5506,11 @@ implicit_fun(Name, Arity) -> %% ===================================================================== -%% @spec implicit_fun(Module::syntaxTree(), Name::syntaxTree(), -%% Arity::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract module-qualified "implicit fun" expression. -%% If <code>Module</code> is <code>none</code>, this is equivalent to -%% <code>implicit_fun(Name, Arity)</code>, otherwise it is equivalent to -%% <code>implicit_fun(module_qualifier(Module, arity_qualifier(Name, -%% Arity))</code>. +%% If `Module' is `none', this is equivalent to +%% `implicit_fun(Name, Arity)', otherwise it is equivalent to +%% `implicit_fun(module_qualifier(Module, arity_qualifier(Name, +%% Arity))'. %% %% (This is a utility function.) %% @@ -6010,10 +5527,8 @@ implicit_fun(Module, Name, Arity) -> %% ===================================================================== -%% @spec implicit_fun(Name::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract "implicit fun" expression. The result -%% represents "<code>fun <em>Name</em></code>". <code>Name</code> should +%% represents "<code>fun <em>Name</em></code>". `Name' should %% represent either <code><em>F</em>/<em>A</em></code> or %% <code><em>M</em>:<em>F</em>/<em>A</em></code> %% @@ -6072,15 +5587,13 @@ revert_implicit_fun(Node) -> %% ===================================================================== -%% @spec implicit_fun_name(Node::syntaxTree()) -> syntaxTree() +%% @doc Returns the name subtree of an `implicit_fun' node. %% -%% @doc Returns the name subtree of an <code>implicit_fun</code> node. -%% -%% <p>Note: if <code>Node</code> represents "<code>fun +%% Note: if `Node' represents "<code>fun %% <em>N</em>/<em>A</em></code>" or "<code>fun %% <em>M</em>:<em>N</em>/<em>A</em></code>", then the result is the %% subtree representing "<code><em>N</em>/<em>A</em></code>" or -%% "<code><em>M</em>:<em>N</em>/<em>A</em></code>", respectively.</p> +%% "<code><em>M</em>:<em>N</em>/<em>A</em></code>", respectively. %% %% @see implicit_fun/1 %% @see arity_qualifier/2 @@ -6110,12 +5623,10 @@ implicit_fun_name(Node) -> %% ===================================================================== -%% @spec fun_expr(Clauses::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract fun-expression. If <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code>, the result represents "<code>fun +%% @doc Creates an abstract fun-expression. If `Clauses' is +%% `[C1, ..., Cn]', the result represents "<code>fun %% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each -%% <code>Ci</code> represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) +%% `Ci' represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) %% <em>Gi</em> -> <em>Bi</em></code>", then the result represents %% "<code>fun (<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> -> %% <em>B1</em>; ...; (<em>Pn1</em>, ..., <em>Pnm</em>) <em>Gn</em> -> @@ -6152,10 +5663,7 @@ revert_fun_expr(Node) -> %% ===================================================================== -%% @spec fun_expr_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of a <code>fun_expr</code> -%% node. +%% @doc Returns the list of clause subtrees of a `fun_expr' node. %% %% @see fun_expr/1 @@ -6171,16 +5679,14 @@ fun_expr_clauses(Node) -> %% ===================================================================== -%% @spec fun_expr_arity(syntaxTree()) -> integer() -%% -%% @doc Returns the arity of a <code>fun_expr</code> node. The result is +%% @doc Returns the arity of a `fun_expr' node. The result is %% the number of parameter patterns in the first clause of the %% fun-expression; subsequent clauses are ignored. %% -%% <p>An exception is thrown if <code>fun_expr_clauses(Node)</code> +%% An exception is thrown if `fun_expr_clauses(Node)' %% returns an empty list, or if the first element of that list is not a -%% syntax tree <code>C</code> of type <code>clause</code> such that -%% <code>clause_patterns(C)</code> is a nonempty list.</p> +%% syntax tree `C' of type `clause' such that +%% `clause_patterns(C)' is a nonempty list. %% %% @see fun_expr/1 %% @see fun_expr_clauses/1 @@ -6194,8 +5700,6 @@ fun_expr_arity(Node) -> %% ===================================================================== -%% @spec parentheses(Body::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract parenthesised expression. The result %% represents "<code>(<em>Body</em>)</code>", independently of the %% context. @@ -6215,9 +5719,7 @@ revert_parentheses(Node) -> %% ===================================================================== -%% @spec parentheses_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>parentheses</code> node. +%% @doc Returns the body subtree of a `parentheses' node. %% %% @see parentheses/1 @@ -6228,7 +5730,6 @@ parentheses_body(Node) -> %% ===================================================================== -%% @spec macro(Name) -> syntaxTree() %% @equiv macro(Name, none) -spec macro(syntaxTree()) -> syntaxTree(). @@ -6238,25 +5739,22 @@ macro(Name) -> %% ===================================================================== -%% @spec macro(Name::syntaxTree(), Arguments) -> syntaxTree() -%% Arguments = none | [syntaxTree()] -%% -%% @doc Creates an abstract macro application. If <code>Arguments</code> -%% is <code>none</code>, the result represents -%% "<code>?<em>Name</em></code>", otherwise, if <code>Arguments</code> -%% is <code>[A1, ..., An]</code>, the result represents +%% @doc Creates an abstract macro application. If `Arguments' +%% is `none', the result represents +%% "<code>?<em>Name</em></code>", otherwise, if `Arguments' +%% is `[A1, ..., An]', the result represents %% "<code>?<em>Name</em>(<em>A1</em>, ..., <em>An</em>)</code>". %% -%% <p>Notes: if <code>Arguments</code> is the empty list, the result +%% Notes: if `Arguments' is the empty list, the result %% will thus represent "<code>?<em>Name</em>()</code>", including a pair -%% of matching parentheses.</p> +%% of matching parentheses. %% -%% <p>The only syntactical limitation imposed by the preprocessor on the +%% The only syntactical limitation imposed by the preprocessor on the %% arguments to a macro application (viewed as sequences of tokens) is %% that they must be balanced with respect to parentheses, brackets, -%% <code>begin ... end</code>, <code>case ... end</code>, etc. The -%% <code>text</code> node type can be used to represent arguments which -%% are not regular Erlang constructs.</p> +%% `begin ... end', `case ... end', etc. The +%% `text' node type can be used to represent arguments which +%% are not regular Erlang constructs. %% %% @see macro_name/1 %% @see macro_arguments/1 @@ -6278,9 +5776,7 @@ macro(Name, Arguments) -> %% ===================================================================== -%% @spec macro_name(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the name subtree of a <code>macro</code> node. +%% @doc Returns the name subtree of a `macro' node. %% %% @see macro/2 @@ -6291,14 +5787,12 @@ macro_name(Node) -> %% ===================================================================== -%% @spec macro_arguments(Node::syntaxTree()) -> none | [syntaxTree()] -%% -%% @doc Returns the list of argument subtrees of a <code>macro</code> -%% node, if any. If <code>Node</code> represents -%% "<code>?<em>Name</em></code>", <code>none</code> is returned. -%% Otherwise, if <code>Node</code> represents +%% @doc Returns the list of argument subtrees of a `macro' +%% node, if any. If `Node' represents +%% "<code>?<em>Name</em></code>", `none' is returned. +%% Otherwise, if `Node' represents %% "<code>?<em>Name</em>(<em>A1</em>, ..., <em>An</em>)</code>", -%% <code>[A1, ..., An]</code> is returned. +%% `[A1, ..., An]' is returned. %% %% @see macro/2 @@ -6309,15 +5803,13 @@ macro_arguments(Node) -> %% ===================================================================== -%% @spec abstract(Term::term()) -> syntaxTree() -%% %% @doc Returns the syntax tree corresponding to an Erlang term. -%% <code>Term</code> must be a literal term, i.e., one that can be +%% `Term' must be a literal term, i.e., one that can be %% represented as a source code literal. Thus, it may not contain a %% process identifier, port, reference, binary or function value as a %% subterm. The function recognises printable strings, in order to get a %% compact and readable representation. Evaluation fails with reason -%% <code>badarg</code> if <code>Term</code> is not a literal term. +%% `badarg' if `Term' is not a literal term. %% %% @see concrete/1 %% @see is_literal/1 @@ -6367,19 +5859,17 @@ abstract_tail(H, T) -> %% ===================================================================== -%% @spec concrete(Node::syntaxTree()) -> term() -%% %% @doc Returns the Erlang term represented by a syntax tree. Evaluation -%% fails with reason <code>badarg</code> if <code>Node</code> does not +%% fails with reason `badarg' if `Node' does not %% represent a literal term. %% -%% <p>Note: Currently, the set of syntax trees which have a concrete +%% Note: Currently, the set of syntax trees which have a concrete %% representation is larger than the set of trees which can be built -%% using the function <code>abstract/1</code>. An abstract character -%% will be concretised as an integer, while <code>abstract/1</code> does +%% using the function {@link abstract/1}. An abstract character +%% will be concretised as an integer, while {@link abstract/1} does %% not at present yield an abstract character for any input. (Use the -%% <code>char/1</code> function to explicitly create an abstract -%% character.)</p> +%% {@link char/1} function to explicitly create an abstract +%% character.) %% %% @see abstract/1 %% @see is_literal/1 @@ -6422,7 +5912,7 @@ concrete(Node) -> {value, concrete(F), []} end, [], true), B; - _ -> + _ -> erlang:error({badarg, Node}) end. @@ -6433,12 +5923,10 @@ concrete_list([]) -> %% ===================================================================== -%% @spec is_literal(Node::syntaxTree()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> represents a -%% literal term, otherwise <code>false</code>. This function returns -%% <code>true</code> if and only if the value of -%% <code>concrete(Node)</code> is defined. +%% @doc Returns `true' if `Node' represents a +%% literal term, otherwise `false'. This function returns +%% `true' if and only if the value of +%% `concrete(Node)' is defined. %% %% @see abstract/1 %% @see concrete/1 @@ -6469,21 +5957,19 @@ is_literal(T) -> %% ===================================================================== -%% @spec revert(Tree::syntaxTree()) -> syntaxTree() -%% -%% @doc Returns an <code>erl_parse</code>-compatible representation of a -%% syntax tree, if possible. If <code>Tree</code> represents a +%% @doc Returns an `erl_parse'-compatible representation of a +%% syntax tree, if possible. If `Tree' represents a %% well-formed Erlang program or expression, the conversion should work -%% without problems. Typically, <code>is_tree/1</code> yields -%% <code>true</code> if conversion failed (i.e., the result is still an -%% abstract syntax tree), and <code>false</code> otherwise. +%% without problems. Typically, {@link is_tree/1} yields +%% `true' if conversion failed (i.e., the result is still an +%% abstract syntax tree), and `false' otherwise. %% -%% <p>The <code>is_tree/1</code> test is not completely foolproof. For a -%% few special node types (e.g. <code>arity_qualifier</code>), if such a +%% The {@link is_tree/1} test is not completely foolproof. For a +%% few special node types (e.g. `arity_qualifier'), if such a %% node occurs in a context where it is not expected, it will be left %% unchanged as a non-reverted subtree of the result. This can only -%% happen if <code>Tree</code> does not actually represent legal Erlang -%% code.</p> +%% happen if `Tree' does not actually represent legal Erlang +%% code. %% %% @see revert_forms/1 %% @see //stdlib/erl_parse @@ -6615,16 +6101,12 @@ revert_root(Node) -> %% ===================================================================== -%% @spec revert_forms(Forms) -> [erl_parse()] -%% -%% Forms = syntaxTree() | [syntaxTree()] -%% %% @doc Reverts a sequence of Erlang source code forms. The sequence can -%% be given either as a <code>form_list</code> syntax tree (possibly +%% be given either as a `form_list' syntax tree (possibly %% nested), or as a list of "program form" syntax trees. If successful, -%% the corresponding flat list of <code>erl_parse</code>-compatible -%% syntax trees is returned (cf. <code>revert/1</code>). If some program -%% form could not be reverted, <code>{error, Form}</code> is thrown. +%% the corresponding flat list of `erl_parse'-compatible +%% syntax trees is returned (see {@link revert/1}). If some program +%% form could not be reverted, `{error, Form}' is thrown. %% Standalone comments in the form sequence are discarded. %% %% @see revert/1 @@ -6633,10 +6115,10 @@ revert_root(Node) -> -type forms() :: syntaxTree() | [syntaxTree()]. -%% -spec revert_forms(forms()) -> [erl_parse()]. +-spec revert_forms(forms()) -> [erl_parse()]. -revert_forms(L) when is_list(L) -> - revert_forms(form_list(L)); +revert_forms(Forms) when is_list(Forms) -> + revert_forms(form_list(Forms)); revert_forms(T) -> case type(T) of form_list -> @@ -6673,60 +6155,54 @@ revert_forms_1([]) -> %% ===================================================================== -%% @spec subtrees(Node::syntaxTree()) -> [[syntaxTree()]] -%% %% @doc Returns the grouped list of all subtrees of a syntax tree. If -%% <code>Node</code> is a leaf node (cf. <code>is_leaf/1</code>), this +%% `Node' is a leaf node (see {@link is_leaf/1}), this %% is the empty list, otherwise the result is always a nonempty list, -%% containing the lists of subtrees of <code>Node</code>, in +%% containing the lists of subtrees of `Node', in %% left-to-right order as they occur in the printed program text, and %% grouped by category. Often, each group contains only a single %% subtree. %% -%% <p>Depending on the type of <code>Node</code>, the size of some +%% Depending on the type of `Node', the size of some %% groups may be variable (e.g., the group consisting of all the %% elements of a tuple), while others always contain the same number of %% elements - usually exactly one (e.g., the group containing the %% argument expression of a case-expression). Note, however, that the %% exact structure of the returned list (for a given node type) should %% in general not be depended upon, since it might be subject to change -%% without notice.</p> +%% without notice. %% -%% <p>The function <code>subtrees/1</code> and the constructor functions -%% <code>make_tree/2</code> and <code>update_tree/2</code> can be a +%% The function {@link subtrees/1} and the constructor functions +%% {@link make_tree/2} and {@link update_tree/2} can be a %% great help if one wants to traverse a syntax tree, visiting all its %% subtrees, but treat nodes of the tree in a uniform way in most or all %% cases. Using these functions makes this simple, and also assures that %% your code is not overly sensitive to extensions of the syntax tree %% data type, because any node types not explicitly handled by your code -%% can be left to a default case.</p> +%% can be left to a default case. %% -%% <p>For example: -%% <pre> -%% postorder(F, Tree) -> +%% For example: +%% ```postorder(F, Tree) -> %% F(case subtrees(Tree) of %% [] -> Tree; %% List -> update_tree(Tree, %% [[postorder(F, Subtree) %% || Subtree <- Group] %% || Group <- List]) -%% end). -%% </pre> -%% maps the function <code>F</code> on <code>Tree</code> and all its +%% end).''' +%% maps the function `F' on `Tree' and all its %% subtrees, doing a post-order traversal of the syntax tree. (Note the -%% use of <code>update_tree/2</code> to preserve node attributes.) For a +%% use of {@link update_tree/2} to preserve node attributes.) For a %% simple function like: -%% <pre> -%% f(Node) -> +%% ```f(Node) -> %% case type(Node) of %% atom -> atom("a_" ++ atom_name(Node)); %% _ -> Node -%% end. -%% </pre> -%% the call <code>postorder(fun f/1, Tree)</code> will yield a new -%% representation of <code>Tree</code> in which all atom names have been +%% end.''' +%% the call `postorder(fun f/1, Tree)' will yield a new +%% representation of `Tree' in which all atom names have been %% extended with the prefix "a_", but nothing else (including comments, -%% annotations and line numbers) has been changed.</p> +%% annotations and line numbers) has been changed. %% %% @see make_tree/2 %% @see type/1 @@ -6896,12 +6372,9 @@ subtrees(T) -> %% ===================================================================== -%% @spec update_tree(Node::syntaxTree(), Groups::[[syntaxTree()]]) -> -%% syntaxTree() -%% %% @doc Creates a syntax tree with the same type and attributes as the -%% given tree. This is equivalent to <code>copy_attrs(Node, -%% make_tree(type(Node), Groups))</code>. +%% given tree. This is equivalent to `copy_attrs(Node, +%% make_tree(type(Node), Groups))'. %% %% @see make_tree/2 %% @see copy_attrs/2 @@ -6914,23 +6387,20 @@ update_tree(Node, Groups) -> %% ===================================================================== -%% @spec make_tree(Type::atom(), Groups::[[syntaxTree()]]) -> -%% syntaxTree() -%% %% @doc Creates a syntax tree with the given type and subtrees. -%% <code>Type</code> must be a node type name (cf. <code>type/1</code>) -%% that does not denote a leaf node type (cf. <code>is_leaf/1</code>). -%% <code>Groups</code> must be a <em>nonempty</em> list of groups of +%% `Type' must be a node type name (see {@link type/1}) +%% that does not denote a leaf node type (see {@link is_leaf/1}). +%% `Groups' must be a <em>nonempty</em> list of groups of %% syntax trees, representing the subtrees of a node of the given type, %% in left-to-right order as they would occur in the printed program -%% text, grouped by category as done by <code>subtrees/1</code>. +%% text, grouped by category as done by {@link subtrees/1}. %% -%% <p>The result of <code>copy_attrs(Node, make_tree(type(Node), -%% subtrees(Node)))</code> (cf. <code>update_tree/2</code>) represents -%% the same source code text as the original <code>Node</code>, assuming -%% that <code>subtrees(Node)</code> yields a nonempty list. However, it +%% The result of `copy_attrs(Node, make_tree(type(Node), +%% subtrees(Node)))' (see {@link update_tree/2}) represents +%% the same source code text as the original `Node', assuming +%% that `subtrees(Node)' yields a nonempty list. However, it %% does not necessarily have the same data representation as -%% <code>Node</code>.</p> +%% `Node'. %% %% @see update_tree/2 %% @see subtrees/1 @@ -6995,42 +6465,40 @@ make_tree(tuple, [E]) -> tuple(E). %% ===================================================================== -%% @spec meta(Tree::syntaxTree()) -> syntaxTree() -%% %% @doc Creates a meta-representation of a syntax tree. The result %% represents an Erlang expression "<code><em>MetaTree</em></code>" %% which, if evaluated, will yield a new syntax tree representing the -%% same source code text as <code>Tree</code> (although the actual data +%% same source code text as `Tree' (although the actual data %% representation may be different). The expression represented by -%% <code>MetaTree</code> is <em>implementation independent</em> with +%% `MetaTree' is <em>implementation independent</em> with %% regard to the data structures used by the abstract syntax tree -%% implementation. Comments attached to nodes of <code>Tree</code> will +%% implementation. Comments attached to nodes of `Tree' will %% be preserved, but other attributes are lost. %% -%% <p>Any node in <code>Tree</code> whose node type is -%% <code>variable</code> (cf. <code>type/1</code>), and whose list of -%% annotations (cf. <code>get_ann/1</code>) contains the atom -%% <code>meta_var</code>, will remain unchanged in the resulting tree, -%% except that exactly one occurrence of <code>meta_var</code> is -%% removed from its annotation list.</p> +%% Any node in `Tree' whose node type is +%% `variable' (see {@link type/1}), and whose list of +%% annotations (see {@link get_ann/1}) contains the atom +%% `meta_var', will remain unchanged in the resulting tree, +%% except that exactly one occurrence of `meta_var' is +%% removed from its annotation list. %% -%% <p>The main use of the function <code>meta/1</code> is to transform a -%% data structure <code>Tree</code>, which represents a piece of program +%% The main use of the function `meta/1' is to transform a +%% data structure `Tree', which represents a piece of program %% code, into a form that is <em>representation independent when -%% printed</em>. E.g., suppose <code>Tree</code> represents a variable -%% named "V". Then (assuming a function <code>print/1</code> for -%% printing syntax trees), evaluating <code>print(abstract(Tree))</code> -%% - simply using <code>abstract/1</code> to map the actual data +%% printed</em>. E.g., suppose `Tree' represents a variable +%% named "V". Then (assuming a function `print/1' for +%% printing syntax trees), evaluating `print(abstract(Tree))' +%% - simply using {@link abstract/1} to map the actual data %% structure onto a syntax tree representation - would output a string -%% that might look something like "<code>{tree, variable, ..., "V", -%% ...}</code>", which is obviously dependent on the implementation of +%% that might look something like "`{tree, variable, ..., "V", +%% ...}'", which is obviously dependent on the implementation of %% the abstract syntax trees. This could e.g. be useful for caching a %% syntax tree in a file. However, in some situations like in a program %% generator generator (with two "generator"), it may be unacceptable. -%% Using <code>print(meta(Tree))</code> instead would output a +%% Using `print(meta(Tree))' instead would output a %% <em>representation independent</em> syntax tree generating %% expression; in the above case, something like -%% "<code>erl_syntax:variable("V")</code>".</p> +%% "`erl_syntax:variable("V")'". %% %% @see abstract/1 %% @see type/1 @@ -7161,60 +6629,56 @@ meta_call(F, As) -> %% ===================================================================== -%% @spec tree(Type) -> syntaxTree() %% @equiv tree(Type, []) --spec tree(atom()) -> syntaxTree(). +-spec tree(atom()) -> #tree{}. tree(Type) -> tree(Type, []). %% ===================================================================== -%% @spec tree(Type::atom(), Data::term()) -> syntaxTree() -%% %% @doc <em>For special purposes only</em>. Creates an abstract syntax -%% tree node with type tag <code>Type</code> and associated data -%% <code>Data</code>. +%% tree node with type tag `Type' and associated data +%% `Data'. %% -%% <p>This function and the related <code>is_tree/1</code> and -%% <code>data/1</code> provide a uniform way to extend the set of -%% <code>erl_parse</code> node types. The associated data is any term, -%% whose format may depend on the type tag.</p> +%% This function and the related {@link is_tree/1} and +%% {@link data/1} provide a uniform way to extend the set of +%% `erl_parse' node types. The associated data is any term, +%% whose format may depend on the type tag. %% -%% <h4>Notes:</h4> +%% === Notes: === %% <ul> %% <li>Any nodes created outside of this module must have type tags %% distinct from those currently defined by this module; see -%% <code>type/1</code> for a complete list.</li> +%% {@link type/1} for a complete list.</li> %% <li>The type tag of a syntax tree node may also be used -%% as a primary tag by the <code>erl_parse</code> representation; +%% as a primary tag by the `erl_parse' representation; %% in that case, the selector functions for that node type %% <em>must</em> handle both the abstract syntax tree and the -%% <code>erl_parse</code> form. The function <code>type(T)</code> +%% `erl_parse' form. The function `type(T)' %% should return the correct type tag regardless of the -%% representation of <code>T</code>, so that the user sees no -%% difference between <code>erl_syntax</code> and -%% <code>erl_parse</code> nodes.</li> +%% representation of `T', so that the user sees no +%% difference between `erl_syntax' and +%% `erl_parse' nodes.</li> %% </ul> +%% %% @see is_tree/1 %% @see data/1 %% @see type/1 --spec tree(atom(), term()) -> syntaxTree(). +-spec tree(atom(), term()) -> #tree{}. tree(Type, Data) -> #tree{type = Type, data = Data}. %% ===================================================================== -%% @spec is_tree(Tree::syntaxTree()) -> boolean() -%% -%% @doc <em>For special purposes only</em>. Returns <code>true</code> if -%% <code>Tree</code> is an abstract syntax tree and <code>false</code> +%% @doc <em>For special purposes only</em>. Returns `true' if +%% `Tree' is an abstract syntax tree and `false' %% otherwise. %% -%% <p><em>Note</em>: this function yields <code>false</code> for all -%% "old-style" <code>erl_parse</code>-compatible "parse trees".</p> +%% <em>Note</em>: this function yields `false' for all +%% "old-style" `erl_parse'-compatible "parse trees". %% %% @see tree/2 @@ -7227,12 +6691,10 @@ is_tree(_) -> %% ===================================================================== -%% @spec data(Tree::syntaxTree()) -> term() -%% %% @doc <em>For special purposes only</em>. Returns the associated data %% of a syntax tree node. Evaluation fails with reason -%% <code>badarg</code> if <code>is_tree(Node)</code> does not yield -%% <code>true</code>. +%% `badarg' if `is_tree(Node)' does not yield +%% `true'. %% %% @see tree/2 @@ -7248,26 +6710,19 @@ data(T) -> erlang:error({badarg, T}). %% ===================================================================== -%% @spec wrap(Node::erl_parse()) -> syntaxTree() -%% -%% @type erl_parse() = erl_parse:parse_tree(). The "parse tree" -%% representation built by the Erlang standard library parser -%% <code>erl_parse</code>. This is a subset of the -%% <a href="#type-syntaxTree"><code>syntaxTree</code></a> type. -%% -%% @doc Creates a wrapper structure around an <code>erl_parse</code> +%% @doc Creates a wrapper structure around an `erl_parse' %% "parse tree". %% -%% <p>This function and the related <code>unwrap/1</code> and -%% <code>is_wrapper/1</code> provide a uniform way to attach arbitrary -%% information to an <code>erl_parse</code> tree. Some information about +%% This function and the related {@link unwrap/1} and +%% {@link is_wrapper/1} provide a uniform way to attach arbitrary +%% information to an `erl_parse' tree. Some information about %% the encapsuled tree may be cached in the wrapper, such as the node %% type. All functions on syntax trees must behave so that the user sees -%% no difference between wrapped and non-wrapped <code>erl_parse</code> +%% no difference between wrapped and non-wrapped `erl_parse' %% trees. <em>Attaching a wrapper onto another wrapper structure is an -%% error</em>.</p> +%% error</em>. -%%-spec wrap(erl_parse:parse_tree()) -> syntaxTree(). +-spec wrap(erl_parse()) -> #wrapper{}. wrap(Node) -> %% We assume that Node is an old-school `erl_parse' tree. @@ -7276,24 +6731,20 @@ wrap(Node) -> %% ===================================================================== -%% @spec unwrap(Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Removes any wrapper structure, if present. If <code>Node</code> +%% @doc Removes any wrapper structure, if present. If `Node' %% is a wrapper structure, this function returns the wrapped -%% <code>erl_parse</code> tree; otherwise it returns <code>Node</code> +%% `erl_parse' tree; otherwise it returns `Node' %% itself. --spec unwrap(syntaxTree()) -> syntaxTree(). +-spec unwrap(syntaxTree()) -> #tree{} | erl_parse(). unwrap(#wrapper{tree = Node}) -> Node; unwrap(Node) -> Node. % This could also be a new-form node. %% ===================================================================== -%% @spec is_wrapper(Term::term()) -> boolean() -%% -%% @doc Returns <code>true</code> if the argument is a wrapper -%% structure, otherwise <code>false</code>. +%% @doc Returns `true' if the argument is a wrapper +%% structure, otherwise `false'. -ifndef(NO_UNUSED). -spec is_wrapper(term()) -> boolean(). diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl index 97dfbfd7cd..36cd35f15d 100644 --- a/lib/syntax_tools/src/erl_syntax_lib.erl +++ b/lib/syntax_tools/src/erl_syntax_lib.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1997-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl index 09efc9c392..59cf6c0a92 100644 --- a/lib/syntax_tools/src/erl_tidy.erl +++ b/lib/syntax_tools/src/erl_tidy.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1999-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl index aa933eb54b..37e561cbbe 100644 --- a/lib/syntax_tools/src/igor.erl +++ b/lib/syntax_tools/src/igor.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1998-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/prettypr.erl b/lib/syntax_tools/src/prettypr.erl index c13fa30998..1b5ba6b05a 100644 --- a/lib/syntax_tools/src/prettypr.erl +++ b/lib/syntax_tools/src/prettypr.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 2000-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/test/Makefile b/lib/syntax_tools/test/Makefile index 3e31bdbd50..d4733b9a42 100644 --- a/lib/syntax_tools/test/Makefile +++ b/lib/syntax_tools/test/Makefile @@ -57,9 +57,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) syntax_tools.spec syntax_tools.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) syntax_tools.spec syntax_tools.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk index 2b9a08e192..8f774c5d75 100644 --- a/lib/syntax_tools/vsn.mk +++ b/lib/syntax_tools/vsn.mk @@ -1 +1 @@ -SYNTAX_TOOLS_VSN = 1.6.8 +SYNTAX_TOOLS_VSN = 1.6.9 diff --git a/lib/test_server/doc/src/Makefile b/lib/test_server/doc/src/Makefile index b32f3d3c59..3ce549f0e1 100644 --- a/lib/test_server/doc/src/Makefile +++ b/lib/test_server/doc/src/Makefile @@ -119,16 +119,16 @@ 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) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/test_server/doc/src/test_server_ctrl.xml b/lib/test_server/doc/src/test_server_ctrl.xml index 9028a67ecb..6b33591701 100644 --- a/lib/test_server/doc/src/test_server_ctrl.xml +++ b/lib/test_server/doc/src/test_server_ctrl.xml @@ -769,11 +769,13 @@ Optional, if not given the test server controller node constantly updated. The following can be reported: </p> <p><c>What = tests_start, Data = {Name,NumCases}</c><br></br> + <c>What = loginfo, Data = [{topdir,TestRootDir},{rundir,CurrLogDir}]</c><br></br> <c>What = tests_done, Data = {Ok,Failed,{UserSkipped,AutoSkipped}}</c><br></br> - <c>What = tc_start, Data = {Mod,Func}</c><br></br> + <c>What = tc_start, Data = {{Mod,Func},TCLogFile}</c><br></br> <c>What = tc_done, Data = {Mod,Func,Result}</c><br></br> <c>What = tc_user_skip, Data = {Mod,Func,Comment}</c><br></br> - <c>What = tc_auto_skip, Data = {Mod,Func,Comment}</c></p> + <c>What = tc_auto_skip, Data = {Mod,Func,Comment}</c><br></br> + <c>What = framework_error, Data = {{FWMod,FWFunc},Error}</c></p> </desc> </func> <func> diff --git a/lib/test_server/src/Makefile b/lib/test_server/src/Makefile index 4bc51873c2..a75855eaab 100644 --- a/lib/test_server/src/Makefile +++ b/lib/test_server/src/Makefile @@ -124,22 +124,22 @@ $(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) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_tests_spec: opt - $(INSTALL_DIR) $(RELEASE_PATH)/test_server + $(INSTALL_DIR) "$(RELEASE_PATH)/test_server" $(INSTALL_DATA) $(ERL_FILES) $(TS_ERL_FILES) \ $(HRL_FILES) $(INTERNAL_HRL_FILES) $(TS_HRL_FILES) \ $(TS_TARGET_FILES) \ $(AUTOCONF_FILES) $(C_FILES) $(COVER_FILES) $(CONFIG) \ - $(RELEASE_PATH)/test_server - $(INSTALL_SCRIPT) $(PROGRAMS) $(RELEASE_PATH)/test_server + "$(RELEASE_PATH)/test_server" + $(INSTALL_SCRIPT) $(PROGRAMS) "$(RELEASE_PATH)/test_server" release_docs_spec: diff --git a/lib/test_server/src/configure.in b/lib/test_server/src/configure.in index 097853bcfc..77bc993ccd 100644 --- a/lib/test_server/src/configure.in +++ b/lib/test_server/src/configure.in @@ -108,7 +108,7 @@ AC_CHECK_HEADER(poll.h, AC_DEFINE(HAVE_POLL_H)) # for the system. AC_MSG_CHECKING([system version (for dynamic loading)]) -system=`uname -s`-`uname -r` +system=`./config.sub $host` AC_MSG_RESULT($system) # Step 2: check for existence of -ldl library. This is needed because @@ -119,10 +119,9 @@ AC_CHECK_LIB(dl, dlopen, have_dl=yes, have_dl=no) # Step 3: set configuration options based on system name and version. SHLIB_LDLIBS= - fullSrcDir=`cd $srcdir; pwd` case $system in - Linux*) + *-linux-*) SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" if test "$have_dl" = yes; then @@ -136,7 +135,7 @@ case $system in fi SHLIB_EXTRACT_ALL="" ;; - NetBSD-*|FreeBSD-*|OpenBSD-*|DragonFly*) + *-netbsd*|*-freebsd*|*-openbsd*|*-dragonfly*) # Not available on all versions: check for include file. AC_CHECK_HEADER(dlfcn.h, [ SHLIB_CFLAGS="-fpic" @@ -153,28 +152,21 @@ case $system in ]) SHLIB_EXTRACT_ALL="" ;; - SunOS-4*) - SHLIB_CFLAGS="-PIC" - SHLIB_LD="ld" - SHLIB_LDFLAGS="$LDFLAGS" - SHLIB_SUFFIX=".so" - SHLIB_EXTRACT_ALL="" - ;; - SunOS-5*|UNIX_SV-4.2*) + *-solaris2*|*-sysv4*) SHLIB_CFLAGS="-KPIC" SHLIB_LD="/usr/ccs/bin/ld" SHLIB_LDFLAGS="$LDFLAGS -G -z text" SHLIB_SUFFIX=".so" SHLIB_EXTRACT_ALL="-z allextract" ;; - Darwin*) + *darwin*) SHLIB_CFLAGS="-fno-common" SHLIB_LD="cc" SHLIB_LDFLAGS="$LDFLAGS -bundle -flat_namespace -undefined suppress" SHLIB_SUFFIX=".so" SHLIB_EXTRACT_ALL="" ;; - OSF1*) + *osf1*) SHLIB_CFLAGS="-fPIC" SHLIB_LD="ld" SHLIB_LDFLAGS="$LDFLAGS -shared" @@ -206,19 +198,19 @@ esac if test "$CC" = "gcc" -o `$CC -v 2>&1 | grep -c gcc` != "0" ; then case $system in - AIX-*) + *-aix) ;; - BSD/OS*) + *-bsd*) ;; - IRIX*) + *-irix) ;; - NetBSD-*|FreeBSD-*|OpenBSD-*) + *-netbsd|*-freebsd|*-openbsd) ;; - RISCos-*) + *-riscos) ;; - ULTRIX-4.*) + *ultrix4.*) ;; - Darwin*) + *darwin*) ;; *) SHLIB_CFLAGS="-fPIC" diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl index 6e94e4861a..17c5f5b253 100644 --- a/lib/test_server/src/test_server.erl +++ b/lib/test_server/src/test_server.erl @@ -35,6 +35,7 @@ -export([fail/0,fail/1,format/1,format/2,format/3]). -export([capture_start/0,capture_stop/0,capture_get/0]). -export([messages_get/0]). +-export([permit_io/2]). -export([hours/1,minutes/1,seconds/1,sleep/1,adjusted_sleep/1,timecall/3]). -export([timetrap_scale_factor/0,timetrap/1,get_timetrap_info/0, timetrap_cancel/1,timetrap_cancel/0]). @@ -49,7 +50,7 @@ -export([run_on_shielded_node/2]). -export([is_cover/0,is_debug/0,is_commercial/0]). --export([break/1,continue/0]). +-export([break/1,break/2,break/3,continue/0,continue/1]). %%% DEBUGGER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -export([purify_new_leaks/0, purify_format/2, purify_new_fds_inuse/0, @@ -523,7 +524,7 @@ stick_all_sticky(Node,Sticky) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData) -> +%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData,RejectIoReqs) -> %% {Time,Value,Loc,Opts,Comment} | {died,Reason,unknown,Comment} %% %% Time = float() (seconds) @@ -558,8 +559,12 @@ stick_all_sticky(Node,Sticky) -> %% ScaleTimetrap indicates if test_server should attemp to automatically %% compensate timetraps for runtime delays introduced by e.g. tools like %% cover. +%% +%% RejectIoReqs (bool) is information about whether printouts to stdout +%% should be visible in the minor log file or not. -run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) -> +run_test_case_apply({CaseNum,Mod,Func,Args,Name, + RunInit,TimetrapData,RejectIoReqs}) -> purify_format("Test case #~w ~w:~w/1", [CaseNum, Mod, Func]), case os:getenv("TS_RUN_VALGRIND") of false -> @@ -570,17 +575,19 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) -> end, test_server_h:testcase({Mod,Func,1}), ProcBef = erlang:system_info(process_count), - Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData), + Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, + TimetrapData, RejectIoReqs), ProcAft = erlang:system_info(process_count), purify_new_leaks(), DetFail = get(test_server_detected_fail), {Result,DetFail,ProcBef,ProcAft}. -run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> +run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData, RejectIoReqs) -> case get(test_server_job_dir) of undefined -> %% i'm a local target - do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData); + do_run_test_case_apply(Mod, Func, Args, Name, RunInit, + TimetrapData, RejectIoReqs); JobDir -> %% i'm a remote target case Args of @@ -595,13 +602,14 @@ run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> Config2 = lists:keyreplace(priv_dir, 1, Config1, {priv_dir,TargetPrivDir}), do_run_test_case_apply(Mod, Func, [Config2], Name, RunInit, - TimetrapData); + TimetrapData, RejectIoReqs); _other -> do_run_test_case_apply(Mod, Func, Args, Name, RunInit, - TimetrapData) + TimetrapData, RejectIoReqs) end end. -do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> +do_run_test_case_apply(Mod, Func, Args, Name, RunInit, + TimetrapData, RejectIoReqs) -> {ok,Cwd} = file:get_cwd(), Args2Print = case Args of [Args1] when is_list(Args1) -> @@ -628,7 +636,8 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> end), group_leader(OldGLeader, self()), put(test_server_detected_fail, []), - run_test_case_msgloop(Ref, Pid, false, false, "", undefined, starting). + run_test_case_msgloop(Ref, Pid, false, RejectIoReqs, false, "", + undefined, starting). %% Ugly bug (pre R5A): %% If this process (group leader of the test case) terminates before @@ -639,7 +648,7 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> %% A test case is known to have failed if it returns {'EXIT', _} tuple, %% or sends a message {failed, File, Line} to it's group_leader %% -run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, +run_test_case_msgloop(Ref, Pid, CaptureStdout, RejectIoReqs, Terminate, Comment, CurrConf, Status) -> %% NOTE: Keep job_proxy_msgloop/0 up to date when changes %% are made in this function! @@ -655,7 +664,7 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, end, receive {test_case_initialized,Pid} -> - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,running); Abort = {abort_current_testcase,_,_} when Status == starting -> %% we're in init phase, must must postpone this operation @@ -663,7 +672,7 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, %% gets killed) self() ! Abort, erlang:yield(), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {abort_current_testcase,Reason,From} -> Line = case is_process_alive(Pid) of @@ -694,82 +703,92 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Error1 end end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, NewComment,CurrConf,Status); + {permit_io,FromPid} -> + put({permit_io,FromPid},true), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}} when is_list(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}} when is_atom(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,Bytes}} -> - run_test_case_msgloop_io( - ReplyAs,CaptureStdout,Bytes,From,put_chars), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Bytes,From,put_chars), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,unicode,io_lib,Func,[Format,Args]}} when is_list(Format) -> Msg = unicode_to_latin1(catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,latin1,io_lib,Func,[Format,Args]}} when is_list(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,unicode,io_lib,Func,[Format,Args]}} when is_atom(Format) -> Msg = unicode_to_latin1(catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,latin1,io_lib,Func,[Format,Args]}} when is_atom(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,unicode,Bytes}} -> - run_test_case_msgloop_io( - ReplyAs,CaptureStdout,unicode_to_latin1(Bytes),From,put_chars), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + unicode_to_latin1(Bytes),From,put_chars), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,latin1,Bytes}} -> - run_test_case_msgloop_io( - ReplyAs,CaptureStdout,Bytes,From,put_chars), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Bytes,From,put_chars), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); IoReq when element(1, IoReq) == io_request -> %% something else, just pass it on group_leader() ! IoReq, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {structured_io,ClientPid,Msg} -> output(Msg, ClientPid), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {capture,NewCapture} -> - run_test_case_msgloop(Ref,Pid,NewCapture,Terminate, + run_test_case_msgloop(Ref,Pid,NewCapture,RejectIoReqs,Terminate, Comment,CurrConf,Status); {sync_apply,From,MFA} -> sync_local_or_remote_apply(false,From,MFA), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {sync_apply_proxy,Proxy,From,MFA} -> sync_local_or_remote_apply(Proxy,From,MFA), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {printout,Detail,Format,Args} -> print(Detail,Format,Args), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {comment,NewComment} -> NewComment1 = test_server_ctrl:to_string(NewComment), @@ -783,18 +802,20 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Other -> Other end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate1, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate1, NewComment2,CurrConf,Status); {read_comment,From} -> From ! {self(),read_comment,Comment}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {set_curr_conf,From,NewCurrConf} -> From ! {self(),set_curr_conf,ok}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,NewCurrConf,Status); {make_priv_dir,From} when CurrConf == undefined -> - From ! {self(),make_priv_dir,{error,no_priv_dir_in_config}}; + From ! {self(),make_priv_dir,{error,no_priv_dir_in_config}}, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {make_priv_dir,From} -> Result = case proplists:get_value(priv_dir, element(2, CurrConf)) of @@ -811,12 +832,12 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, end end, From ! {self(),make_priv_dir,Result}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {'EXIT',Pid,{Ref,Time,Value,Loc,Opts}} -> RetVal = {Time/1000000,Value,mod_loc(Loc),Opts,Comment}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal}, - Comment,undefined,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + {true,RetVal},Comment,undefined,Status); {'EXIT',Pid,Reason} -> case Reason of {timetrap_timeout,TVal,Loc} -> @@ -827,7 +848,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, spawn_fw_call(FwMod,FwFunc,CurrConf,Pid, {framework_error,{timetrap,TVal}}, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout, + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, Terminate,Comment, undefined,Status); Loc1 -> @@ -860,7 +882,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Loc1,self()), undefined end, - run_test_case_msgloop(Ref,Pid,CaptureStdout, + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, Terminate,Comment, NewCurrConf,Status) end; @@ -877,15 +900,16 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, {timetrap_timeout,TVal}, Loc1,self()) end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {testcase_aborted,ErrorMsg={user_timetrap_error,_},AbortLoc} -> %% user timetrap function caused exit %% during start of test case {Mod,Func} = get_mf(mod_loc(AbortLoc)), spawn_fw_call(Mod,Func,CurrConf,Pid, ErrorMsg,unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout, + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, Terminate,Comment, undefined,Status); {testcase_aborted,AbortReason,AbortLoc} -> @@ -896,7 +920,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, spawn_fw_call(FwMod,FwFunc,CurrConf,Pid, {framework_error,ErrorMsg}, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout, + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, Terminate,Comment, undefined,Status); Loc1 -> @@ -928,7 +953,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, ErrorMsg,Loc1,self()), undefined end, - run_test_case_msgloop(Ref,Pid,CaptureStdout, + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, Terminate,Comment, NewCurrConf,Status) end; @@ -943,14 +969,14 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, spawn_fw_call(Mod,Func,CurrConf,Pid, testcase_aborted_or_killed, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {fw_error,{FwMod,FwFunc,FwError}} -> spawn_fw_call(FwMod,FwFunc,CurrConf,Pid, {framework_error,FwError}, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); _Other -> %% the testcase has terminated because of Reason (e.g. an exit %% because a linked process failed) @@ -960,8 +986,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, end, spawn_fw_call(Mod,Func,CurrConf,Pid, Reason,unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status) + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status) end; {EndConfPid,{call_end_conf,Data,_Result}} -> case CurrConf of @@ -969,11 +995,11 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, {_Mod,_Func,TCPid,TCExitReason,Loc} = Data, spawn_fw_call(Mod,Func,CurrConf,TCPid, TCExitReason,Loc,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,undefined,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,undefined,Status); _ -> - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status) + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status) end; {_FwCallPid,fw_notify_done,{T,Value,Loc,Opts,AddToComment}} -> %% the framework has been notified, we're finished @@ -993,8 +1019,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, end, {T,Value,Loc,Opts,Comment1} end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal}, - Comment,undefined,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + {true,RetVal},Comment,undefined,Status); {'EXIT',_FwCallPid,{fw_notify_done,Func,Error}} -> %% a framework function failed CB = os:getenv("TEST_SERVER_FRAMEWORK"), @@ -1005,13 +1031,13 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, {list_to_atom(CB),Func} end, RetVal = {died,{framework_error,Loc,Error},Loc,"Framework error"}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal}, - Comment,undefined,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + {true,RetVal},Comment,undefined,Status); {failed,File,Line} -> put(test_server_detected_fail, [{File, Line}| get(test_server_detected_fail)]), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {user_timetrap,Pid,_TrapTime,StartTime,E={user_timetrap_error,_},_} -> case update_user_timetraps(Pid, StartTime) of @@ -1020,8 +1046,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, ignore -> ok end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {user_timetrap,Pid,TrapTime,StartTime,ElapsedTime,Scale} -> %% a user timetrap is triggered, ignore it if new %% timetrap has been started since @@ -1036,49 +1062,57 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, ignore -> ok end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {timetrap_cancel_one,Handle,_From} -> timetrap_cancel_one(Handle, false), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {timetrap_cancel_all,TCPid,_From} -> timetrap_cancel_all(TCPid, false), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {get_timetrap_info,TCPid,From} -> Info = get_timetrap_info(TCPid, false), From ! {self(),get_timetrap_info,Info}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); _Other when not is_tuple(_Other) -> %% ignore anything not generated by test server - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); _Other when element(1, _Other) /= 'EXIT', element(1, _Other) /= started, element(1, _Other) /= finished, element(1, _Other) /= print -> %% ignore anything not generated by test server - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status) + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status) after Timeout -> ReturnValue end. -run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func) -> +run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func) -> case Msg of {'EXIT',_} -> From ! {io_reply,ReplyAs,{error,Func}}; _ -> From ! {io_reply,ReplyAs,ok} end, - if CaptureStdout /= false -> - CaptureStdout ! {captured,Msg}; - true -> + Proceed = if RejectIoReqs -> get({permit_io,From}); + true -> true + end, + if Proceed -> + if CaptureStdout /= false -> + CaptureStdout ! {captured,Msg}; + true -> + ok + end, + output({minor,Msg},From); + true -> ok - end, - output({minor,Msg},From). + end. output(Msg,Sender) -> local_or_remote_apply({test_server_ctrl,output,[Msg,Sender]}). @@ -1097,7 +1131,8 @@ call_end_conf(Mod,Func,TCPid,TCExitReason,Loc,Conf,TVal) -> {'EXIT',Why} -> timer:sleep(1), group_leader() ! {printout,12, - "WARNING! ~p:end_per_testcase(~p, ~p)" + "WARNING! " + "~p:end_per_testcase(~p, ~p)" " crashed!\n\tReason: ~p\n", [Mod,Func,Conf,Why]}; _ -> @@ -1213,13 +1248,18 @@ spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) -> end, spawn_link(FwCall); -spawn_fw_call(Mod,Func,_,Pid,Error,Loc,SendTo) -> +spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) -> + {Mod1,Func1} = + case {Mod,Func,CurrConf} of + {undefined,undefined,{{M,F},_}} -> {M,F}; + _ -> {Mod,Func} + end, FwCall = fun() -> %% set group leader so that printouts/comments %% from the framework get printed in the logs group_leader(SendTo, self()), - case catch fw_error_notify(Mod,Func,[], + case catch fw_error_notify(Mod1,Func1,[], Error,Loc) of {'EXIT',FwErrorNotifyErr} -> exit({fw_notify_done,error_notification, @@ -1228,7 +1268,7 @@ spawn_fw_call(Mod,Func,_,Pid,Error,Loc,SendTo) -> ok end, Conf = [{tc_status,{failed,timetrap_timeout}}], - case catch do_end_tc_call(Mod,Func, Loc, + case catch do_end_tc_call(Mod1,Func1, Loc, {Pid,Error,[Conf]},Error) of {'EXIT',FwEndTCErr} -> exit({fw_notify_done,end_tc,FwEndTCErr}); @@ -1959,6 +1999,13 @@ messages_get() -> test_server_sup:messages_get([]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% permit_io(GroupLeader, FromPid) -> ok +%% +%% Make sure proceeding IO from FromPid won't get rejected +permit_io(GroupLeader, FromPid) -> + GroupLeader ! {permit_io,FromPid}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sleep(Time) -> ok %% Time = integer() | float() | infinity %% @@ -2061,31 +2108,40 @@ fail() -> %% Break a test case so part of the test can be done manually. %% Use continue/0 to continue. break(Comment) -> - case erase(test_server_timetraps) of - undefined -> ok; - List -> lists:foreach(fun({Ref,_,_}) -> - timetrap_cancel(Ref) - end, List) - end, + break(?MODULE, Comment). + +break(CBM, Comment) -> + break(CBM, '', Comment). + +break(CBM, TestCase, Comment) -> + timetrap_cancel(), + {TCName,CntArg,PName} = + if TestCase == '' -> + {"", "", test_server_break_process}; + true -> + Str = atom_to_list(TestCase), + {[32 | Str], Str, + list_to_atom("test_server_break_process_" ++ Str)} + end, io:format(user, "\n\n\n--- SEMIAUTOMATIC TESTING ---" - "\nThe test case executes on process ~w" + "\nThe test case~s executes on process ~w" "\n\n\n~s" "\n\n\n-----------------------------\n\n" - "Continue with --> test_server:continue().\n", - [self(),Comment]), - case whereis(test_server_break_process) of + "Continue with --> ~w:continue(~s).\n", + [TCName,self(),Comment,CBM,CntArg]), + case whereis(PName) of undefined -> - spawn_break_process(self()); + spawn_break_process(self(), PName); OldBreakProcess -> OldBreakProcess ! cancel, - spawn_break_process(self()) + spawn_break_process(self(), PName) end, receive continue -> ok end. -spawn_break_process(Pid) -> +spawn_break_process(Pid, PName) -> spawn(fun() -> - register(test_server_break_process,self()), + register(PName, self()), receive continue -> continue(Pid); cancel -> ok @@ -2094,13 +2150,19 @@ spawn_break_process(Pid) -> continue() -> case whereis(test_server_break_process) of - undefined -> - ok; - BreakProcess -> - BreakProcess ! continue + undefined -> ok; + BreakProcess -> BreakProcess ! continue end. -continue(Pid) -> +continue(TestCase) when is_atom(TestCase) -> + PName = list_to_atom("test_server_break_process_" ++ + atom_to_list(TestCase)), + case whereis(PName) of + undefined -> ok; + BreakProcess -> BreakProcess ! continue + end; + +continue(Pid) when is_pid(Pid) -> Pid ! continue. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl index 5ed296d215..df2187bc04 100644 --- a/lib/test_server/src/test_server_ctrl.erl +++ b/lib/test_server/src/test_server_ctrl.erl @@ -162,7 +162,7 @@ -export([jobs/0, run_test/1, wait_finish/0, idle_notify/1, abort_current_testcase/1, abort/0]). -export([start_get_totals/1, stop_get_totals/0]). --export([get_levels/0, set_levels/3]). +-export([reject_io_reqs/1, get_levels/0, set_levels/3]). -export([multiply_timetraps/1, scale_timetraps/1, get_timetrap_parameters/0]). -export([create_priv_dir/1]). -export([cover/2, cover/3, cover/7, @@ -218,8 +218,9 @@ -define(auto_skip_color, "#FFA64D"). -define(user_skip_color, "#FF8000"). +-define(sortable_table_name, "SortableTable"). --record(state,{jobs=[],levels={1,19,10}, +-record(state,{jobs=[], levels={1,19,10}, reject_io_reqs=false, multiply_timetraps=1, scale_timetraps=true, create_priv_dir=auto_per_run, finish=false, target_info, trc=false, cover=false, wait_for_node=[], @@ -498,6 +499,9 @@ get_levels() -> set_levels(Show, Major, Minor) -> controller_call({set_levels,Show,Major,Minor}). +reject_io_reqs(Bool) -> + controller_call({reject_io_reqs,Bool}). + multiply_timetraps(N) -> controller_call({multiply_timetraps,N}). @@ -815,6 +819,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) -> [SpecName,{State#state.multiply_timetraps, State#state.scale_timetraps}], LogDir, Name, State#state.levels, + State#state.reject_io_reqs, State#state.create_priv_dir, State#state.testcase_callback, ExtraTools1), NewJobs = [{Name,Pid}|State#state.jobs], @@ -825,6 +830,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) -> [SpecList,{State#state.multiply_timetraps, State#state.scale_timetraps}], LogDir, Name, State#state.levels, + State#state.reject_io_reqs, State#state.create_priv_dir, State#state.testcase_callback, ExtraTools1), NewJobs = [{Name,Pid}|State#state.jobs], @@ -843,6 +849,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) -> {State#state.multiply_timetraps, State#state.scale_timetraps}], LogDir, Name, State#state.levels, + State#state.reject_io_reqs, State#state.create_priv_dir, State#state.testcase_callback, ExtraTools1), NewJobs = [{Name,Pid}|State#state.jobs], @@ -975,6 +982,15 @@ handle_call({set_levels,Show,Major,Minor}, _From, State) -> {reply,ok,State#state{levels={Show,Major,Minor}}}; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% handle_call({reject_io_reqs,Bool}, _, State) -> ok +%% Bool = bool() +%% +%% May be used to switch off stdout printouts to the minor log file + +handle_call({reject_io_reqs,Bool}, _From, State) -> + {reply,ok,State#state{reject_io_reqs=Bool}}; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% handle_call({multiply_timetraps,N}, _, State) -> ok %% N = integer() | infinity %% @@ -1340,14 +1356,15 @@ kill_all_jobs([]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% spawn_tester(Mod, Func, Args, Dir, Name, Levels, CreatePrivDir, -%% TestCaseCallback, ExtraTools) -> Pid +%% spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs, +%% CreatePrivDir, TestCaseCallback, ExtraTools) -> Pid %% Mod = atom() %% Func = atom() %% Args = [term(),...] %% Dir = string() %% Name = string() %% Levels = {integer(),integer(),integer()} +%% RejectIoReqs = bool() %% CreatePrivDir = auto_per_run | manual_per_tc | auto_per_tc %% TestCaseCallback = {CBMod,CBFunc} | undefined %% ExtraTools = [ExtraTool,...] @@ -1359,14 +1376,14 @@ kill_all_jobs([]) -> %% When the named function is done executing, a summary of the results %% is printed to the log files. -spawn_tester(Mod, Func, Args, Dir, Name, Levels, +spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) -> spawn_link( - fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels, + fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) end). -init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, +init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) -> process_flag(trap_exit, true), put(test_server_name, Name), @@ -1378,6 +1395,7 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, put(test_server_summary_level, SumLev), put(test_server_major_level, MajLev), put(test_server_minor_level, MinLev), + put(test_server_reject_io_reqs, RejectIoReqs), put(test_server_create_priv_dir, CreatePrivDir), put(test_server_random_seed, proplists:get_value(random_seed, ExtraTools)), put(test_server_testcase_callback, TCCallback), @@ -1424,8 +1442,10 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, end, OkN = get(test_server_ok), FailedN = get(test_server_failed), - print(html,"<tr><td></td><td><b>TOTAL</b></td><td></td><td></td><td></td>" - "<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n", + print(html,"\n</tbody>\n<tfoot>\n" + "<tr><td></td><td><b>TOTAL</b></td><td></td><td></td><td></td>" + "<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n" + "</tfoot>\n", [Time,SuccessStr,OkN,FailedN,SkipStr,OkN+FailedN+SkippedN]). %% timer:tc/3 @@ -1486,7 +1506,7 @@ stop_extra_tools([], _) -> %% Reads the named test suite specification file, and executes it. %% %% This function is meant to be called by a process created by -%% spawn_tester/7, which sets up some necessary dictionary values. +%% spawn_tester/10, which sets up some necessary dictionary values. do_spec(SpecName, TimetrapSpec) when is_list(SpecName) -> case file:consult(SpecName) of @@ -1535,7 +1555,7 @@ do_spec(SpecName, TimetrapSpec) when is_list(SpecName) -> %% should not be used. Use a configuration test case instead. %% %% This function is meant to be called by a process created by -%% spawn_tester/7, which sets up some necessary dictionary values. +%% spawn_tester/10, which sets up some necessary dictionary values. do_spec_list(TermList0, TimetrapSpec) -> Nodes = [], @@ -1702,7 +1722,7 @@ add_mod(Mod, Mods) -> %% configuration information into the log files. %% %% This function is meant to be called by a process created by -%% spawn_tester/7, which sets up some necessary dictionary values. +%% spawn_tester/10, which sets up some necessary dictionary values. do_test_cases(TopCases, SkipCases, Config, MultiplyTimetrap) when is_integer(MultiplyTimetrap); MultiplyTimetrap == infinity -> @@ -1741,7 +1761,8 @@ do_test_cases(TopCases, SkipCases, test_server_sup:framework_call(report, [tests_start,{Test,N}]), {Header,Footer} = case test_server_sup:framework_call(get_html_wrapper, - [TestDescr,true,TestDir], "") of + [TestDescr,true,TestDir, + {[],[2,3,4,7,8],[1,6]}], "") of Empty when (Empty == "") ; (element(2,Empty) == "") -> put(basic_html, true), {["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", @@ -1803,14 +1824,15 @@ do_test_cases(TopCases, SkipCases, [?suitelog_name,?coverlog_name]), print(html, "<p>~s</p>\n" ++ - xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">", - "<table>") ++ - "<tr><th>Num</th><th>Module</th><th>Group</th>" ++ - "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++ - "<th>Comment</th></tr>\n", - [print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>\n",[N]}, + xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">", + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n"]) ++ + "<tr><th>Num</th><th>Module</th><th>Group</th>" ++ + "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++ + "<th>Comment</th></tr>\n</thead>\n<tbody>\n", + [print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>" ++ + xhtml("\n<br>\n", "\n<br />\n"),[N]}, {"",[]})]), - print(html, xhtml("<br>", "<br />")), print(major, "=cases ~p", [get(test_server_cases)]), print(major, "=user ~s", [TI#target_info.username]), @@ -1964,7 +1986,8 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName) -> {Header,Footer} = case test_server_sup:framework_call(get_html_wrapper, [TestDescr,false, - filename:dirname(AbsName)], "") of + filename:dirname(AbsName), + undefined], "") of Empty when (Empty == "") ; (element(2,Empty) == "") -> put(basic_html, true), {["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", @@ -2094,7 +2117,7 @@ html_possibly_convert(Src, SrcInfo, Dest) -> Header = case test_server_sup:framework_call(get_html_wrapper, ["Module "++Src,false, - OutDir], "") of + OutDir,undefined], "") of Empty when (Empty == "") ; (element(2,Empty) == "") -> ["<!DOCTYPE HTML PUBLIC", "\"-//W3C//DTD HTML 3.2 Final//EN\">\n", @@ -3809,10 +3832,12 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where, MinorBase,MinorBase]), do_if_parallel(Main, ok, fun erlang:yield/0), + + RejectIoReqs = get(test_server_reject_io_reqs), %% run the test case {Result,DetectedFail,ProcsBefore,ProcsAfter} = run_test_case_apply(Num, Mod, Func, [UpdatedArgs], get_name(Mode), - RunInit, Where, TimetrapData), + RunInit, Where, TimetrapData, RejectIoReqs), {Time,RetVal,Loc,Opts,Comment} = case Result of Normal={_Time,_RetVal,_Loc,_Opts,_Comment} -> Normal; @@ -4431,7 +4456,7 @@ do_format_exception(Reason={Error,Stack}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, -%% Where, TimetrapData) -> +%% Where, TimetrapData, RejectIoReqs) -> %% {{Time,RetVal,Loc,Opts,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} | %% {{died,Reason,unknown,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} %% Name = atom() @@ -4450,19 +4475,21 @@ do_format_exception(Reason={Error,Stack}) -> %% sent over socket to target, and test_server runs the case and sends the %% result back over the socket. Else test_server runs the case directly on host. -run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, host, TimetrapData) -> +run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, host, + TimetrapData, RejectIoReqs) -> test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit, - TimetrapData}); -run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, TimetrapData) -> + TimetrapData,RejectIoReqs}); +run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, + TimetrapData, RejectIoReqs) -> case get(test_server_ctrl_job_sock) of undefined -> %% local target test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit, - TimetrapData}); + TimetrapData,RejectIoReqs}); JobSock -> %% remote target request(JobSock, {test_case,{CaseNum,Mod,Func,Args,Name,RunInit, - TimetrapData}}), + TimetrapData,RejectIoReqs}}), read_job_sock_loop(JobSock) end. diff --git a/lib/test_server/src/test_server_node.erl b/lib/test_server/src/test_server_node.erl index e8498a43f2..6358efa764 100644 --- a/lib/test_server/src/test_server_node.erl +++ b/lib/test_server/src/test_server_node.erl @@ -407,7 +407,7 @@ start_node_peer(SlaveName, OptList, From, TI) -> % Support for erl_crash_dump files.. CrashFile = filename:join([TI#target_info.test_server_dir, "erl_crash_dump."++cast_to_list(SlaveName)]), - CrashArgs = lists:concat([" -env ERL_CRASH_DUMP ",CrashFile," "]), + CrashArgs = lists:concat([" -env ERL_CRASH_DUMP \"",CrashFile,"\" "]), FailOnError = start_node_get_option_value(fail_on_error, OptList, true), Pa = TI#target_info.test_server_dir, Prog0 = start_node_get_option_value(erl, OptList, default), @@ -420,7 +420,7 @@ start_node_peer(SlaveName, OptList, From, TI) -> Cmd = lists:concat([Prog, " -detached ", TI#target_info.naming, " ", SlaveName, - " -pa ", Pa, + " -pa \"", Pa,"\"", NodeStarted, CrashArgs, " ", Args]), @@ -472,9 +472,9 @@ start_node_slave(SlaveName, OptList, From, TI) -> CrashFile = filename:join([TI#target_info.test_server_dir, "erl_crash_dump."++cast_to_list(SlaveName)]), - CrashArgs = lists:concat([" -env ERL_CRASH_DUMP ",CrashFile," "]), + CrashArgs = lists:concat([" -env ERL_CRASH_DUMP \"",CrashFile,"\" "]), Pa = TI#target_info.test_server_dir, - Args = lists:concat([" -pa ", Pa, " ", SuppliedArgs, CrashArgs]), + Args = lists:concat([" -pa \"", Pa, "\" ", SuppliedArgs, CrashArgs]), Prog0 = start_node_get_option_value(erl, OptList, default), Prog = pick_erl_program(Prog0), diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl index cb06264adb..4899f38d2b 100644 --- a/lib/test_server/src/ts.erl +++ b/lib/test_server/src/ts.erl @@ -27,9 +27,10 @@ -export([run/0, run/1, run/2, run/3, run/4, clean/0, clean/1, tests/0, tests/1, - install/0, install/1, install/2, index/0, + install/0, install/1, index/0, estone/0, estone/1, cross_cover_analyse/1, + compile_testcases/0, compile_testcases/1, help/0]). -export([i/0, l/1, r/0, r/1, r/2, r/3]). @@ -88,35 +89,25 @@ -define( install_help, [ - " ts:install() - Install TS for local target with no Options.\n" - " ts:install([Options])\n", - " - Install TS for local target with Options\n" - " ts:install({Architecture, Target_name})\n", - " - Install TS for a remote target architecture.\n", - " and target network name (e.g. {vxworks_cpu32, sauron}).\n", - " ts:install({Architecture, Target_name}, [Options])\n", - " - Install TS as above, and with Options.\n", + " ts:install() - Install TS with no Options.\n" + " ts:install([Options]) - Install TS with Options\n" "\n", "Installation options supported:\n", " {longnames, true} - Use fully qualified hostnames\n", - " {hosts, [HostList]}\n" - " - Use theese hosts for distributed testing.\n" " {verbose, Level} - Sets verbosity level for TS output (0,1,2), 0 is\n" " quiet(default).\n" - " {slavetargets, SlaveTarges}\n" - " - Available hosts for starting slave nodes for\n" - " platforms which cannot have more than one erlang\n" - " node per host.\n" - " {crossroot, TargetErlRoot}\n" - " - Erlang root directory on target host\n" - " Mandatory for remote targets\n" - " {master, {MasterHost, MasterCookie}}\n" - " - Master host and cookie for targets which are\n" - " started as slave nodes.\n" - " erl_boot_server must be started on master before\n" - " test is run.\n" - " Optional, default is controller host and then\n" - " erl_boot_server is started autmatically\n" + " {crossroot, ErlTop}\n" + " - Erlang root directory on build host, ~n" + " normally same value as $ERL_TOP\n" + " {crossenv, [{Key,Val}]}\n" + " - Environmentals used by test configure on build host\n" + " {crossflags, FlagsString}\n" + " - Flags used by test configure on build host\n" + " {xcomp, XCompFile}\n" + " - The xcomp file to use for cross compiling the~n" + " testcases. Using this option will override any~n" + " cross* configurations given to ts. Note that you~n" + " have to have a correct ERL_TOP as well.~n" ]). help() -> @@ -183,26 +174,24 @@ help(installed) -> " cover_details. Analyses modules specified in\n" " cross.cover.\n" " Level can be 'overview' or 'details'.\n", + " ts:compile_testcases()~n" + " ts:compile_testcases(Apps)~n" + " - Compile all testcases for usage in a cross ~n" + " compile environment." " \n" "Installation (already done):\n" ], show_help([H,?install_help]). show_help(H) -> - io:put_chars(lists:flatten(H)). + io:format(lists:flatten(H)). %% Installs tests. install() -> ts_install:install(install_local,[]). -install({Architecture, Target_name}) -> - ts_install:install({ts_lib:maybe_atom_to_list(Architecture), - ts_lib:maybe_atom_to_list(Target_name)}, []); install(Options) when is_list(Options) -> ts_install:install(install_local,Options). -install({Architecture, Target_name}, Options) when is_list(Options)-> - ts_install:install({ts_lib:maybe_atom_to_list(Architecture), - ts_lib:maybe_atom_to_list(Target_name)}, Options). %% Updates the local index page. @@ -728,3 +717,23 @@ cover_type(cover_details) -> details. do_load(Mod) -> code:purge(Mod), code:load_file(Mod). + + +compile_testcases() -> + compile_datadirs("../*/*_data"). + +compile_testcases(App) when is_atom(App) -> + compile_testcases([App]); +compile_testcases([App | T]) -> + compile_datadirs(io_lib:format("../~s_test/*_data", [App])), + compile_testcases(T); +compile_testcases([]) -> + ok. + +compile_datadirs(DataDirs) -> + {ok,Variables} = file:consult("variables"), + + lists:foreach(fun(Dir) -> + ts_lib:make_non_erlang(Dir, Variables) + end, + filelib:wildcard(DataDirs)). diff --git a/lib/test_server/src/ts.hrl b/lib/test_server/src/ts.hrl index 885a726c54..db804d23a1 100644 --- a/lib/test_server/src/ts.hrl +++ b/lib/test_server/src/ts.hrl @@ -28,6 +28,7 @@ -define(run_summary, "suite.summary"). -define(cover_total,"total_cover.log"). -define(variables, "variables"). +-define(cross_variables, "variables-cross"). -define(LF, [10]). % Newline in VxWorks script -define(CHAR_PER_LINE, 60). % Characters per VxWorks script building line -define(CROSS_COOKIE, "cross"). % cookie used when cross platform testing diff --git a/lib/test_server/src/ts_autoconf_win32.erl b/lib/test_server/src/ts_autoconf_win32.erl index 9103542fd2..258040b39e 100644 --- a/lib/test_server/src/ts_autoconf_win32.erl +++ b/lib/test_server/src/ts_autoconf_win32.erl @@ -67,6 +67,7 @@ system_type(Vars) -> {5,1,_} -> "Windows XP"; {5,2,_} -> "Windows 2003"; {6,0,_} -> "Windows Vista"; + {6,1,_} -> "Windows 7"; {_,_,_} -> "Windows NCC-1701-D" end; {win32, windows} -> diff --git a/lib/test_server/src/ts_erl_config.erl b/lib/test_server/src/ts_erl_config.erl index 5585e8ccd3..45d88016a4 100644 --- a/lib/test_server/src/ts_erl_config.erl +++ b/lib/test_server/src/ts_erl_config.erl @@ -128,15 +128,15 @@ erts_lib(Vars,OsType) -> ErtsLibInternal} end, [{erts_lib_include, - filename:nativename(ErtsLibInclude)}, + quote(filename:nativename(ErtsLibInclude))}, {erts_lib_include_generated, - filename:nativename(ErtsLibIncludeGenerated)}, + quote(filename:nativename(ErtsLibIncludeGenerated))}, {erts_lib_include_internal, - filename:nativename(ErtsLibIncludeInternal)}, + quote(filename:nativename(ErtsLibIncludeInternal))}, {erts_lib_include_internal_generated, - filename:nativename(ErtsLibIncludeInternalGenerated)}, - {erts_lib_path, filename:nativename(ErtsLibPath)}, - {erts_lib_internal_path, filename:nativename(ErtsLibInternalPath)}, + quote(filename:nativename(ErtsLibIncludeInternalGenerated))}, + {erts_lib_path, quote(filename:nativename(ErtsLibPath))}, + {erts_lib_internal_path, quote(filename:nativename(ErtsLibInternalPath))}, {erts_lib_multi_threaded, erts_lib_name(multi_threaded, OsType)}, {erts_lib_single_threaded, erts_lib_name(single_threaded, OsType)} | Vars]. @@ -145,13 +145,13 @@ erl_include(Vars) -> Include = case erl_root(Vars) of {installed, Root} -> - filename:join([Root, "usr", "include"]); + quote(filename:join([Root, "usr", "include"])); {srctree, Root, Target} -> - filename:join([Root, "erts", "emulator", "beam"]) - ++ " -I" ++ filename:join([Root, "erts", "emulator"]) + quote(filename:join([Root, "erts", "emulator", "beam"])) + ++ " -I" ++ quote(filename:join([Root, "erts", "emulator"])) ++ system_include(Root, Vars) - ++ " -I" ++ filename:join([Root, "erts", "include"]) - ++ " -I" ++ filename:join([Root, "erts", "include", Target]) + ++ " -I" ++ quote(filename:join([Root, "erts", "include"])) + ++ " -I" ++ quote(filename:join([Root, "erts", "include", Target])) end, [{erl_include, filename:nativename(Include)}|Vars]. @@ -163,7 +163,7 @@ system_include(Root, Vars) -> "VxWorks" -> "sys.vxworks"; _ -> "sys/unix" end, - " -I" ++ filename:nativename(filename:join([Root, "erts", "emulator", SysDir])). + " -I" ++ quote(filename:nativename(filename:join([Root, "erts", "emulator", SysDir]))). erl_interface(Vars,OsType) -> {Incl, {LibPath, MkIncl}} = @@ -220,20 +220,16 @@ erl_interface(Vars,OsType) -> _ -> "" % VxWorks end, - CrossCompile = case OsType of - vxworks -> "true"; - _ -> "false" - end, - [{erl_interface_libpath, filename:nativename(LibPath)}, + [{erl_interface_libpath, quote(filename:nativename(LibPath))}, {erl_interface_sock_libs, sock_libraries(OsType)}, {erl_interface_lib, Lib}, {erl_interface_eilib, Lib1}, {erl_interface_lib_drv, LibDrv}, {erl_interface_eilib_drv, Lib1Drv}, {erl_interface_threadlib, ThreadLib}, - {erl_interface_include, filename:nativename(Incl)}, - {erl_interface_mk_include, filename:nativename(MkIncl)}, - {erl_interface_cross_compile, CrossCompile} | Vars]. + {erl_interface_include, quote(filename:nativename(Incl))}, + {erl_interface_mk_include, quote(filename:nativename(MkIncl))} + | Vars]. ic(Vars, OsType) -> {ClassPath, LibPath, Incl} = @@ -250,10 +246,10 @@ ic(Vars, OsType) -> end, filename:join(Dir, "include")} end, - [{ic_classpath, filename:nativename(ClassPath)}, - {ic_libpath, filename:nativename(LibPath)}, + [{ic_classpath, quote(filename:nativename(ClassPath))}, + {ic_libpath, quote(filename:nativename(LibPath))}, {ic_lib, link_library("ic", OsType)}, - {ic_include_path, filename:nativename(Incl)}|Vars]. + {ic_include_path, quote(filename:nativename(Incl))}|Vars]. jinterface(Vars, _OsType) -> ClassPath = @@ -263,7 +259,7 @@ jinterface(Vars, _OsType) -> Dir -> filename:join([Dir, "priv", "OtpErlang.jar"]) end, - [{jinterface_classpath, filename:nativename(ClassPath)}|Vars]. + [{jinterface_classpath, quote(filename:nativename(ClassPath))}|Vars]. lib_dir(Vars, Lib) -> LibLibDir = case Lib of @@ -276,8 +272,6 @@ lib_dir(Vars, Lib) -> case {get_var(crossroot, Vars), LibLibDir} of {{error, _}, _} -> %no crossroot LibLibDir; - {_, {error, _}} -> %no lib - LibLibDir; {CrossRoot, _} -> %% XXX: Ugly. So ugly I won't comment it %% /Patrik @@ -299,18 +293,16 @@ lib_dir(Vars, Lib) -> end. erl_root(Vars) -> - Root = code:root_dir(), - case ts_lib:erlang_type() of + Root = case get_var(crossroot,Vars) of + {error, notfound} -> code:root_dir(); + CrossRoot -> CrossRoot + end, + case ts_lib:erlang_type(Root) of {srctree, _Version} -> Target = get_var(target, Vars), {srctree, Root, Target}; {_, _Version} -> - case get_var(crossroot,Vars) of - {error, notfound} -> - {installed, Root}; - CrossRoot -> - {installed, CrossRoot} - end + {installed, Root} end. @@ -362,10 +354,17 @@ ssl(Vars, _OsType) -> {error, bad_name} -> throw({cannot_find_app, ssl}); Dir -> - [{ssl_libdir, filename:nativename(Dir)}| Vars] + [{ssl_libdir, quote(filename:nativename(Dir))}| Vars] end. separators(Vars, {win32,_}) -> [{'DS',"\\"},{'PS',";"}|Vars]; separators(Vars, _) -> [{'DS',"/"},{'PS',":"}|Vars]. + +quote([$ |R]) -> + "\\ "++quote(R); +quote([C|R]) -> + [C|quote(R)]; +quote([]) -> + []. diff --git a/lib/test_server/src/ts_install.erl b/lib/test_server/src/ts_install.erl index 9703478f20..99ccfbc9bc 100644 --- a/lib/test_server/src/ts_install.erl +++ b/lib/test_server/src/ts_install.erl @@ -28,12 +28,25 @@ install(install_local, Options) -> install(os:type(), Options); install(TargetSystem, Options) -> - io:format("Running configure for cross architecture, network target name~n" - "~p~n", [TargetSystem]), - case autoconf(TargetSystem) of + case file:consult(?variables) of + {ok, Vars} -> + case proplists:get_value(cross,Vars) of + "yes" when Options == []-> + target_install(Vars); + _ -> + build_install(TargetSystem, Options) + end; + _ -> + build_install(TargetSystem, Options) + end. + + +build_install(TargetSystem, Options) -> + XComp = parse_xcomp_file(proplists:get_value(xcomp,Options)), + case autoconf(TargetSystem, XComp++Options) of {ok, Vars0} -> OsType = os_type(TargetSystem), - Vars1 = ts_erl_config:variables(merge(Vars0,Options),OsType), + Vars1 = ts_erl_config:variables(Vars0++XComp++Options,OsType), {Options1, Vars2} = add_vars(Vars1, Options), Vars3 = lists:flatten([Options1|Vars2]), write_terms(?variables, Vars3); @@ -45,32 +58,43 @@ os_type({unix,_}=OsType) -> OsType; os_type({win32,_}=OsType) -> OsType; os_type(_Other) -> vxworks. -merge(Vars,[]) -> - Vars; -merge(Vars,[{crossroot,X}| Tail]) -> - merge([{crossroot, X} | Vars], Tail); -merge(Vars,[_X | Tail]) -> - merge(Vars,Tail). +target_install(CrossVars) -> + io:format("Cross installation detected, skipping configure and data_dir make~n"), + case file:rename(?variables,?cross_variables) of + ok -> + ok; + _ -> + io:format("Could not find variables file from cross make~n"), + throw(cross_installation_failed) + end, + CPU = proplists:get_value('CPU',CrossVars), + OS = proplists:get_value(os,CrossVars), + {Options,Vars} = add_vars([{cross,"yes"},{'CPU',CPU},{os,OS}],[]), + Variables = lists:flatten([Options|Vars]), + write_terms(?variables, Variables). %% Autoconf for various platforms. %% unix uses the configure script %% win32 uses ts_autoconf_win32 %% VxWorks uses ts_autoconf_vxworks. -autoconf(TargetSystem) -> - case autoconf1(TargetSystem) of +autoconf(TargetSystem, XComp) -> + case autoconf1(TargetSystem, XComp) of ok -> autoconf2(file:read_file("conf_vars")); Error -> Error end. -autoconf1({win32, _}) -> +autoconf1({win32, _},[{cross,"no"}]) -> ts_autoconf_win32:configure(); -autoconf1({unix, _}) -> - unix_autoconf(); -autoconf1(Other) -> - ts_autoconf_vxworks:configure(Other). +autoconf1({unix, _},XCompFile) -> + unix_autoconf(XCompFile); +autoconf1(Other,[{cross,"no"}]) -> + ts_autoconf_vxworks:configure(Other); +autoconf1(_,_) -> + io:format("cross compilation not supported for that this platform~n"), + throw(cross_installation_failed). autoconf2({ok, Bin}) -> get_vars(binary_to_list(Bin), name, [], []); @@ -92,27 +116,40 @@ get_vars([], name, [], Result) -> get_vars(_, _, _, _) -> {error, fatal_bad_conf_vars}. -unix_autoconf() -> +unix_autoconf(XConf) -> Configure = filename:absname("configure"), - Args = case catch erlang:system_info(threads) of - false -> ""; - _ -> " --enable-shlib-thread-safety" - end - ++ case catch string:str(erlang:system_info(system_version), - "debug") > 0 of - false -> ""; - _ -> " --enable-debug-mode" - end, + Flags = proplists:get_value(crossflags,XConf,[]), + Env = proplists:get_value(crossenv,XConf,[]), + Host = get_xcomp_flag("host", Flags), + Build = get_xcomp_flag("build", Flags), + Threads = [" --enable-shlib-thread-safety" || + erlang:system_info(threads) /= false], + Debug = [" --enable-debug-mode" || + string:str(erlang:system_info(system_version),"debug") > 0], + Args = Host ++ Build ++ Threads ++ Debug, case filelib:is_file(Configure) of true -> - Env = macosx_cflags(), - Port = open_port({spawn, Configure ++ Args}, - [stream, eof, {env,Env}]), + OSXEnv = macosx_cflags(), + io:format("Running ~sEnv: ~p~n", + [lists:flatten(Configure ++ Args),Env++OSXEnv]), + Port = open_port({spawn, lists:flatten(["\"",Configure,"\"",Args])}, + [stream, eof, {env,Env++OSXEnv}]), ts_lib:print_data(Port); false -> {error, no_configure_script} end. + +get_xcomp_flag(Flag, Flags) -> + get_xcomp_flag(Flag, Flag, Flags). +get_xcomp_flag(Flag, Tag, Flags) -> + case proplists:get_value(Flag,Flags) of + undefined -> ""; + "guess" -> [" --",Tag,"=",os:cmd("$ERL_TOP/erts/autoconf/config.guess")]; + HostVal -> [" --",Tag,"=",HostVal] + end. + + macosx_cflags() -> case os:type() of {unix, darwin} -> @@ -125,10 +162,33 @@ macosx_cflags() -> [] end. +parse_xcomp_file(undefined) -> + [{cross,"no"}]; +parse_xcomp_file(Filepath) -> + {ok,Bin} = file:read_file(Filepath), + Lines = binary:split(Bin,<<"\n">>,[global,trim]), + {Envs,Flags} = parse_xcomp_file(Lines,[],[]), + [{cross,"yes"},{crossroot,os:getenv("ERL_TOP")}, + {crossenv,Envs},{crossflags,Flags}]. + +parse_xcomp_file([<<A:8,_/binary>> = Line|R],Envs,Flags) + when $A =< A, A =< $Z -> + [Var,Value] = binary:split(Line,<<"=">>), + parse_xcomp_file(R,[{binary_to_list(Var), + binary_to_list(Value)}|Envs],Flags); +parse_xcomp_file([<<"erl_xcomp_",Line/binary>>|R],Envs,Flags) -> + [Var,Value] = binary:split(Line,<<"=">>), + parse_xcomp_file(R,Envs,[{binary_to_list(Var), + binary_to_list(Value)}|Flags]); +parse_xcomp_file([_|R],Envs,Flags) -> + parse_xcomp_file(R,Envs,Flags); +parse_xcomp_file([],Envs,Flags) -> + {lists:reverse(Envs),lists:reverse(Flags)}. + write_terms(Name, Terms) -> case file:open(Name, [write]) of {ok, Fd} -> - Result = write_terms1(Fd, Terms), + Result = write_terms1(Fd, remove_duplicates(Terms)), file:close(Fd), Result; {error, Reason} -> @@ -141,6 +201,17 @@ write_terms1(Fd, [Term|Rest]) -> write_terms1(_, []) -> ok. +remove_duplicates(List) -> + lists:reverse( + lists:foldl(fun({Key,Val},Acc) -> + R = make_ref(), + case proplists:get_value(Key,Acc,R) of + R -> [{Key,Val}|Acc]; + _Else -> + Acc + end + end,[],List)). + add_vars(Vars0, Opts0) -> {Opts,LongNames} = case lists:keymember(longnames, 1, Opts0) of @@ -209,12 +280,11 @@ platform(Vars) -> LC = lock_checking(), MT = modified_timing(), AsyncThreads = async_threads(), - HeapType = heap_type_label(), Debug = debug(), CpuBits = word_size(), Common = lists:concat([Hostname,"/",OsType,"/",CpuType,CpuBits,LinuxDist, Schedulers,BindType,KP,IOTHR,LC,MT,AsyncThreads, - HeapType,Debug,ExtraLabel]), + Debug,ExtraLabel]), PlatformId = lists:concat([ErlType, " ", Version, Common]), PlatformLabel = ErlType ++ Common, PlatformFilename = platform_as_filename(PlatformId), @@ -272,12 +342,6 @@ hostname() -> "/localhost" end. -heap_type_label() -> - case catch erlang:system_info(heap_type) of - hybrid -> "/Hybrid"; - _ -> "" %private - end. - async_threads() -> case catch erlang:system_info(threads) of true -> "/A"++integer_to_list(erlang:system_info(thread_pool_size)); diff --git a/lib/test_server/src/ts_install_cth.erl b/lib/test_server/src/ts_install_cth.erl index a41916fd0a..67f2df0cae 100644 --- a/lib/test_server/src/ts_install_cth.erl +++ b/lib/test_server/src/ts_install_cth.erl @@ -95,17 +95,12 @@ pre_init_per_suite(_Suite,Config,State) -> try {ok,Variables} = file:consult(filename:join(State#state.ts_conf_dir,"variables")), - - %% Make the stuff in all_SUITE_data if it exists - AllDir = filename:join(DataDir,"../all_SUITE_data"), - case filelib:is_dir(AllDir) of - true -> - make_non_erlang(AllDir,Variables); - false -> - ok + case proplists:get_value(cross,Variables) of + "yes" -> + ct:log("Not making data dir as tests have been cross compiled"); + _ -> + ts_lib:make_non_erlang(DataDir, Variables) end, - - make_non_erlang(DataDir, Variables), {add_node_name(Config, State), State} catch Error:Reason -> @@ -219,39 +214,6 @@ terminate(_State) -> %%% ============================================================================ %%% Local functions %%% ============================================================================ -%% Configure and run all the Makefiles in the data dirs of the suite -%% in question -make_non_erlang(DataDir, Variables) -> - {ok,CurrWD} = file:get_cwd(), - try - file:set_cwd(DataDir), - MakeCommand = proplists:get_value(make_command,Variables), - - FirstMakefile = filename:join(DataDir,"Makefile.first"), - case filelib:is_regular(FirstMakefile) of - true -> - ct:log("Making ~p",[FirstMakefile]), - ok = ts_make:make( - MakeCommand, DataDir, filename:basename(FirstMakefile)); - false -> - ok - end, - - MakefileSrc = filename:join(DataDir,"Makefile.src"), - MakefileDest = filename:join(DataDir,"Makefile"), - case filelib:is_regular(MakefileSrc) of - true -> - ok = ts_lib:subst_file(MakefileSrc,MakefileDest,Variables), - ct:log("Making ~p",[MakefileDest]), - ok = ts_make:make([{makefile,"Makefile"},{data_dir,DataDir} - | Variables]); - false -> - ok - end - after - file:set_cwd(CurrWD), - timer:sleep(100) - end. %% Add a nodename to config if it does not exist add_node_name(Config, State) -> diff --git a/lib/test_server/src/ts_lib.erl b/lib/test_server/src/ts_lib.erl index 2f0a4ea8c0..d521d2beda 100644 --- a/lib/test_server/src/ts_lib.erl +++ b/lib/test_server/src/ts_lib.erl @@ -24,10 +24,12 @@ %% Avoid warning for local function error/1 clashing with autoimported BIF. -compile({no_auto_import,[error/1]}). -export([error/1, var/2, erlang_type/0, + erlang_type/1, initial_capital/1, interesting_logs/1, specs/1, suites/2, last_test/1, force_write_file/2, force_delete/1, subst_file/3, subst/2, print_data/1, + make_non_erlang/2, maybe_atom_to_list/1, progress/4 ]). @@ -73,8 +75,10 @@ progress(Vars, Level, Format, Args) -> %% Returns: {Type, Version} where Type is otp|src erlang_type() -> + erlang_type(code:root_dir()). +erlang_type(RootDir) -> {_, Version} = init:script_id(), - RelDir = filename:join(code:root_dir(), "releases"), % Only in installed + RelDir = filename:join(RootDir, "releases"), % Only in installed case filelib:is_file(RelDir) of true -> {otp,Version}; % installed OTP false -> {srctree,Version} % source code tree @@ -333,3 +337,45 @@ maybe_atom_to_list(To_list) when is_list(To_list) -> maybe_atom_to_list(To_list) when is_atom(To_list)-> atom_to_list(To_list). + +%% Configure and run all the Makefiles in the data dir of the suite +%% in question +make_non_erlang(DataDir, Variables) -> + %% Make the stuff in all_SUITE_data if it exists + AllDir = filename:join(DataDir,"../all_SUITE_data"), + case filelib:is_dir(AllDir) of + true -> + make_non_erlang_do(AllDir,Variables); + false -> + ok + end, + make_non_erlang_do(DataDir, Variables). + +make_non_erlang_do(DataDir, Variables) -> + try + MakeCommand = proplists:get_value(make_command,Variables), + + FirstMakefile = filename:join(DataDir,"Makefile.first"), + case filelib:is_regular(FirstMakefile) of + true -> + io:format("Making ~p",[FirstMakefile]), + ok = ts_make:make( + MakeCommand, DataDir, filename:basename(FirstMakefile)); + false -> + ok + end, + + MakefileSrc = filename:join(DataDir,"Makefile.src"), + MakefileDest = filename:join(DataDir,"Makefile"), + case filelib:is_regular(MakefileSrc) of + true -> + ok = ts_lib:subst_file(MakefileSrc,MakefileDest,Variables), + io:format("Making ~p",[MakefileDest]), + ok = ts_make:make([{makefile,"Makefile"},{data_dir,DataDir} + | Variables]); + false -> + ok + end + after + timer:sleep(100) %% maybe unnecessary now when we don't do set_cwd anymore + end. diff --git a/lib/test_server/src/ts_make.erl b/lib/test_server/src/ts_make.erl index 3df66111a3..1d8ef230c7 100644 --- a/lib/test_server/src/ts_make.erl +++ b/lib/test_server/src/ts_make.erl @@ -25,12 +25,12 @@ %% Functions to be called from make test cases. make(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), - Makefile = ?config(makefile, Config), - Make = ?config(make_command, Config), + DataDir = proplists:get_value(data_dir, Config), + Makefile = proplists:get_value(makefile, Config), + Make = proplists:get_value(make_command, Config), case make(Make, DataDir, Makefile) of ok -> ok; - {error,Reason} -> ?t:fail({make_failed,Reason}) + {error,Reason} -> exit({make_failed,Reason}) end. unmake(Config) when is_list(Config) -> @@ -85,7 +85,7 @@ run_make_script({win32, _}, Make, Dir, Makefile) -> {"run_make.bat", ".\\run_make", ["@echo off\r\n", - "cd ", filename:nativename(Dir), "\r\n", + "cd \"", filename:nativename(Dir), "\"\r\n", Make, " -f ", Makefile, " \r\n", "if errorlevel 1 echo *error*\r\n", "if not errorlevel 1 echo *ok*\r\n"]}; @@ -93,7 +93,7 @@ run_make_script({unix, _}, Make, Dir, Makefile) -> {"run_make", "/bin/sh ./run_make", ["#!/bin/sh\n", - "cd ", Dir, "\n", + "cd \"", Dir, "\"\n", Make, " -f ", Makefile, " 2>&1\n", "case $? in\n", " 0) echo '*ok*';;\n", diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl index 885a3c9b96..2e8c092400 100644 --- a/lib/test_server/src/ts_run.erl +++ b/lib/test_server/src/ts_run.erl @@ -229,7 +229,7 @@ make_command(Vars, Spec, State) -> %% uncomment the line below to disable exception formatting %% " -test_server_format_exception false", " -boot start_sasl -sasl errlog_type error", - " -pz ",Cwd, + " -pz \"",Cwd,"\"", " -ct_test_vars ",TestVars, " -eval \"file:set_cwd(\\\"",TestDir,"\\\")\" " " -eval \"ct:run_test(", @@ -334,9 +334,9 @@ path_separator() -> end. -make_common_test_args(Args0, Options, _Vars) -> +make_common_test_args(Args0, Options0, _Vars) -> Trace = - case lists:keysearch(trace,1,Options) of + case lists:keysearch(trace,1,Options0) of {value,{trace,TI}} when is_tuple(TI); is_tuple(hd(TI)) -> ok = file:write_file(?tracefile,io_lib:format("~p.~n",[TI])), [{ct_trace,?tracefile}]; @@ -348,7 +348,7 @@ make_common_test_args(Args0, Options, _Vars) -> [] end, Cover = - case lists:keysearch(cover,1,Options) of + case lists:keysearch(cover,1,Options0) of {value,{cover, App, none, _Analyse}} -> io:format("No cover file found for ~p~n",[App]), []; @@ -358,7 +358,7 @@ make_common_test_args(Args0, Options, _Vars) -> [] end, - Logdir = case lists:keysearch(logdir, 1, Options) of + Logdir = case lists:keysearch(logdir, 1, Options0) of {value,{logdir, _}} -> []; false -> @@ -373,15 +373,16 @@ make_common_test_args(Args0, Options, _Vars) -> {scale_timetraps, true}] end, - ConfigPath = case {os:getenv("TEST_CONFIG_PATH"), - lists:keysearch(config, 1, Options)} of - {false,{value, {config, Path}}} -> - Path; - {false,false} -> - "../test_server"; - {Path,_} -> - Path - end, + {ConfigPath, + Options} = case {os:getenv("TEST_CONFIG_PATH"), + lists:keysearch(config, 1, Options0)} of + {_,{value, {config, Path}}} -> + {Path,lists:keydelete(config, 1, Options0)}; + {false,false} -> + {"../test_server",Options0}; + {Path,_} -> + {Path,Options0} + end, ConfigFiles = [{config,[filename:join(ConfigPath,File) || File <- get_config_files()]}], io_lib:format("~100000p",[Args0++Trace++Cover++Logdir++ diff --git a/lib/test_server/test/Makefile b/lib/test_server/test/Makefile index 198440bb17..b2ac95afaa 100644 --- a/lib/test_server/test/Makefile +++ b/lib/test_server/test/Makefile @@ -83,10 +83,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(COVERFILE) $(RELSYSDIR) - $(INSTALL_DATA) test_server_test_lib.hrl test_server.spec test_server.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(COVERFILE) "$(RELSYSDIR)" + $(INSTALL_DATA) test_server_test_lib.hrl test_server.spec test_server.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/test_server/vsn.mk b/lib/test_server/vsn.mk index a1f4559083..aecf595f3f 100644 --- a/lib/test_server/vsn.mk +++ b/lib/test_server/vsn.mk @@ -1 +1 @@ -TEST_SERVER_VSN = 3.5.1 +TEST_SERVER_VSN = 3.5.2 diff --git a/lib/toolbar/doc/src/Makefile b/lib/toolbar/doc/src/Makefile index 76147c111e..af907859d7 100644 --- a/lib/toolbar/doc/src/Makefile +++ b/lib/toolbar/doc/src/Makefile @@ -102,14 +102,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/toolbar/src/Makefile b/lib/toolbar/src/Makefile index 14e1451609..91ce305e26 100644 --- a/lib/toolbar/src/Makefile +++ b/lib/toolbar/src/Makefile @@ -84,10 +84,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: 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..a76ce98ad6 100644 --- a/lib/tools/doc/src/Makefile +++ b/lib/tools/doc/src/Makefile @@ -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/eprof.xml b/lib/tools/doc/src/eprof.xml index 8b614d8860..1c5e38109b 100644 --- a/lib/tools/doc/src/eprof.xml +++ b/lib/tools/doc/src/eprof.xml @@ -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/xref_chapter.xml b/lib/tools/doc/src/xref_chapter.xml index 39c5545af9..169313cb9c 100644 --- a/lib/tools/doc/src/xref_chapter.xml +++ b/lib/tools/doc/src/xref_chapter.xml @@ -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.el b/lib/tools/emacs/erlang.el index bc7a190fb4..8f98d6c85c 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -1516,7 +1516,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 +1525,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 +2988,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))) diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented index 2948ccf1b5..45d9593000 100644 --- a/lib/tools/emacs/test.erl.indented +++ b/lib/tools/emacs/test.erl.indented @@ -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..e123150dd1 100644 --- a/lib/tools/emacs/test.erl.orig +++ b/lib/tools/emacs/test.erl.orig @@ -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/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..4c0cc8cde7 100644 --- a/lib/tools/src/Makefile +++ b/lib/tools/src/Makefile @@ -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/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/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..b9a42041a2 100644 --- a/lib/tools/test/Makefile +++ b/lib/tools/test/Makefile @@ -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..0cd53a05db 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -574,14 +574,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), 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 diff --git a/lib/tv/doc/src/Makefile b/lib/tv/doc/src/Makefile index 21478d0931..e36dd84b73 100644 --- a/lib/tv/doc/src/Makefile +++ b/lib/tv/doc/src/Makefile @@ -125,14 +125,14 @@ debug opt: 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) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/tv/priv/Makefile b/lib/tv/priv/Makefile index 69e3e32c8a..73ea8f4ee9 100644 --- a/lib/tv/priv/Makefile +++ b/lib/tv/priv/Makefile @@ -64,8 +64,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(TOOLBAR_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(TOOLBAR_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/tv/src/Makefile b/lib/tv/src/Makefile index 457b9d38c4..f078e5dee1 100644 --- a/lib/tv/src/Makefile +++ b/lib/tv/src/Makefile @@ -126,10 +126,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile index 620b3ebb69..c3fae0ad42 100644 --- a/lib/typer/src/Makefile +++ b/lib/typer/src/Makefile @@ -101,10 +101,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/src" $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(YRL_FILES) \ - $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(INSTALL_FILES) $(RELSYSDIR)/ebin + "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(INSTALL_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 6392f5765f..1e40b8926e 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -83,6 +83,7 @@ start() -> {Args, Analysis} = process_cl_args(), %% io:format("Args: ~p\n", [Args]), %% io:format("Analysis: ~p\n", [Analysis]), + Timer = dialyzer_timing:init(false), TrustedFiles = filter_fd(Args#args.trusted, [], fun is_erl_file/1), Analysis2 = extract(Analysis, TrustedFiles), All_Files = get_all_files(Args), @@ -91,6 +92,7 @@ start() -> Analysis4 = collect_info(Analysis3), %% io:format("Final: ~p\n", [Analysis4#analysis.fms]), TypeInfo = get_type_info(Analysis4), + dialyzer_timing:stop(Timer), show_or_annotate(TypeInfo), %% io:format("\nTyper analysis finished\n"), erlang:halt(0). @@ -181,7 +183,6 @@ get_type_info(#analysis{callgraph = CallGraph, remove_external(CallGraph, PLT) -> {StrippedCG0, Ext} = dialyzer_callgraph:remove_external(CallGraph), - StrippedCG = dialyzer_callgraph:finalize(StrippedCG0), case get_external(Ext, PLT) of [] -> ok; Externals -> @@ -192,7 +193,7 @@ remove_external(CallGraph, PLT) -> _ -> msg(io_lib:format(" Unknown types: ~p\n", [ExtTypes])) end end, - StrippedCG. + StrippedCG0. -spec get_external([{mfa(), mfa()}], plt()) -> [mfa()]. @@ -901,8 +902,9 @@ analyze_core_tree(Core, Records, SpecInfo, CbInfo, ExpTypes, Analysis, File) -> MergedExpTypes = sets:union(ExpTypes, OldExpTypes), CS6 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, CS5), Ex_Funcs = [{0,F,A} || {_,_,{F,A}} <- cerl:module_exports(Tree)], - TmpCG = Analysis#analysis.callgraph, - CG = dialyzer_callgraph:scan_core_tree(Tree, TmpCG), + CG = Analysis#analysis.callgraph, + {V, E} = dialyzer_callgraph:scan_core_tree(Tree, CG), + dialyzer_callgraph:add_edges(E, V, CG), Fun = fun analyze_one_function/2, All_Defs = cerl:module_defs(Tree), Acc = lists:foldl(Fun, #tmpAcc{file = File, module = Module}, All_Defs), diff --git a/lib/webtool/doc/src/Makefile b/lib/webtool/doc/src/Makefile index 7b86752738..32269e9424 100644 --- a/lib/webtool/doc/src/Makefile +++ b/lib/webtool/doc/src/Makefile @@ -112,16 +112,16 @@ debug opt: 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/man1 - $(INSTALL_DATA) $(MAN1_FILES) $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/webtool/priv/Makefile b/lib/webtool/priv/Makefile index 6e1c6606fe..6af997c2bb 100644 --- a/lib/webtool/priv/Makefile +++ b/lib/webtool/priv/Makefile @@ -67,14 +67,14 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DIR) $(RELSYSDIR)/priv/root - $(INSTALL_DIR) $(RELSYSDIR)/priv/root/conf - $(INSTALL_DIR) $(RELSYSDIR)/priv/root/doc - $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/priv/root/doc - $(INSTALL_DATA) $(WEBSERVER_CONFIG_FILES) $(RELSYSDIR)/priv/root/conf - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(SCRIPTS) $(RELSYSDIR)/priv/bin + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/root" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/root/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/root/doc" + $(INSTALL_DATA) $(HTDOCS_FILES) "$(RELSYSDIR)/priv/root/doc" + $(INSTALL_DATA) $(WEBSERVER_CONFIG_FILES) "$(RELSYSDIR)/priv/root/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_SCRIPT) $(SCRIPTS) "$(RELSYSDIR)/priv/bin" release_docs_spec: diff --git a/lib/webtool/src/Makefile b/lib/webtool/src/Makefile index 62845cd370..45e8b2a6f0 100644 --- a/lib/webtool/src/Makefile +++ b/lib/webtool/src/Makefile @@ -87,11 +87,11 @@ $(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) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(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/wx/aclocal.m4 b/lib/wx/aclocal.m4 index 339a15a2bb..a76594d86f 100644 --- a/lib/wx/aclocal.m4 +++ b/lib/wx/aclocal.m4 @@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us dnl Cross compilation variables AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)]) +AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)]) @@ -606,6 +607,103 @@ ifelse([$5], , , [$5 fi ]) +dnl ---------------------------------------------------------------------- +dnl +dnl AC_DOUBLE_MIDDLE_ENDIAN +dnl +dnl Checks whether doubles are represented in "middle-endian" format. +dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly, +dnl as well as DOUBLE_MIDDLE_ENDIAN. +dnl +dnl + +AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN], +[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian, +[# It does not; compile a test program. +AC_RUN_IFELSE( +[AC_LANG_SOURCE([[#include <stdlib.h> + +int +main(void) +{ + int i = 0; + int zero = 0; + int bigendian; + int zero_index = 0; + + union + { + long int l; + char c[sizeof (long int)]; + } u; + + /* we'll use the one with 32-bit words */ + union + { + double d; + unsigned int c[2]; + } vint; + + union + { + double d; + unsigned long c[2]; + } vlong; + + union + { + double d; + unsigned short c[2]; + } vshort; + + + /* Are we little or big endian? From Harbison&Steele. */ + u.l = 1; + bigendian = (u.c[sizeof (long int) - 1] == 1); + + zero_index = bigendian ? 1 : 0; + + vint.d = 1.0; + vlong.d = 1.0; + vshort.d = 1.0; + + if (sizeof(unsigned int) == 4) + { + if (vint.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned long) == 4) + { + if (vlong.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned short) == 4) + { + if (vshort.c[zero_index] != 0) + zero = 1; + } + + exit (zero); +} +]])], + [ac_cv_c_double_middle_endian=no], + [ac_cv_c_double_middle_endian=yes], + [ac_cv_c_double_middle=unknown])]) +case $ac_cv_c_double_middle_endian in + yes) + m4_default([$1], + [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1, + [Define to 1 if your processor stores the words in a double in + middle-endian format (like some ARMs).])]) ;; + no) + $2 ;; + *) + m4_default([$3], + [AC_MSG_WARN([unknown double endianness +presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;; +esac +])# AC_C_DOUBLE_MIDDLE_ENDIAN + dnl ---------------------------------------------------------------------- dnl @@ -1337,6 +1435,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian]) fi +case X$erl_xcomp_double_middle_endian in + X) ;; + Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;; + *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);; +esac + +AC_C_DOUBLE_MIDDLE_ENDIAN + AC_ARG_ENABLE(native-ethr-impls, AS_HELP_STRING([--disable-native-ethr-impls], [disable native ethread implementations]), diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl index 23a34225ca..c5802af679 100644 --- a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl +++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl @@ -61,7 +61,7 @@ connect(This, EventType) -> %% {userData, term()} An erlang term that will be sent with the event. Default: []. -spec connect(This::wxEvtHandler(), EventType::wxEventType(), [Option]) -> ok when Option :: {id, integer()} | {lastId, integer()} | {skip, boolean()} | - {callback, function()} | {userData, term()}. + callback | {callback, function()} | {userData, term()}. connect(This=#wx_ref{type=ThisT}, EventType, Options) -> EvH = parse_opts(Options, #evh{et=EventType}), ?CLASS(ThisT,wxEvtHandler), diff --git a/lib/wx/c_src/Makefile.in b/lib/wx/c_src/Makefile.in index ce7f4bf53e..1497ac4d16 100644 --- a/lib/wx/c_src/Makefile.in +++ b/lib/wx/c_src/Makefile.in @@ -176,10 +176,10 @@ $(TARGET_DIR)/erl_gl$(SO_EXT): $(GL_OBJECTS) # ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) ../priv/erlang-logo32.png $(RELSYSDIR)/priv/ - $(INSTALL_DATA) ../priv/erlang-logo64.png $(RELSYSDIR)/priv/ - $(INSTALL_PROGRAM) $(TARGET_DIR)/wxe_driver$(SO_EXT) $(RELSYSDIR)/priv/ - $(INSTALL_PROGRAM) $(TARGET_DIR)/erl_gl$(SO_EXT) $(RELSYSDIR)/priv/ + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) ../priv/erlang-logo32.png "$(RELSYSDIR)/priv/" + $(INSTALL_DATA) ../priv/erlang-logo64.png "$(RELSYSDIR)/priv/" + $(INSTALL_PROGRAM) $(TARGET_DIR)/wxe_driver$(SO_EXT) "$(RELSYSDIR)/priv/" + $(INSTALL_PROGRAM) $(TARGET_DIR)/erl_gl$(SO_EXT) "$(RELSYSDIR)/priv/" release_docs_spec: diff --git a/lib/wx/configure.in b/lib/wx/configure.in index b27e114a3d..8e5f696bc7 100755 --- a/lib/wx/configure.in +++ b/lib/wx/configure.in @@ -571,7 +571,7 @@ if test X"$WX_HAVE_STATIC_LIBS" = X"true" ; then LIBS=$WX_LIBS_STATIC fi -AC_LINK_IFELSE([ +AC_LINK_IFELSE([AC_LANG_SOURCE([ #ifdef WIN32 # include <windows.h> # include <gl/gl.h> @@ -583,6 +583,7 @@ AC_LINK_IFELSE([ #include "wx/wx.h" #include "wx/stc/stc.h" #include "wx/glcanvas.h" + ]) class MyApp : public wxApp { diff --git a/lib/wx/doc/src/Makefile b/lib/wx/doc/src/Makefile index 834f887076..4e6727b6a6 100644 --- a/lib/wx/doc/src/Makefile +++ b/lib/wx/doc/src/Makefile @@ -95,13 +95,13 @@ ref_man.xml: ref_man.xml.src @echo "</application>" >> ref_man.xml @echo -$(ErlMods:%.erl=%.xml): +$(ErlMods:%.erl=%.xml): ../../src/$(@:%.xml=%.erl) escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(VSN) -preprocess true -sort_functions false ../../src/$(@:%.xml=%.erl) -$(GenMods:%.erl=%.xml): +$(GenMods:%.erl=%.xml): ../../src/gen/$(@:%.xml=%.erl) escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(VSN) -i ../../src -preprocess true -sort_functions false ../../src/gen/$(@:%.xml=%.erl) -$(XML_CHAPTER_FILES): +$(XML_CHAPTER_FILES): ../overview.edoc escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(VSN) -chapter ../overview.edoc debug opt: @@ -120,14 +120,14 @@ 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) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/wx/examples/demo/Makefile b/lib/wx/examples/demo/Makefile index 872723fb98..c60e9c3d50 100644 --- a/lib/wx/examples/demo/Makefile +++ b/lib/wx/examples/demo/Makefile @@ -69,7 +69,7 @@ docs: run: opt erl -smp -detached -pa $(TOPDIR)/ebin -s demo -EXRELSYSDIR = $(RELSYSDIR)/examples/demo +EXRELSYSDIR = "$(RELSYSDIR)/examples/demo" include $(ERL_TOP)/make/otp_release_targets.mk docs: diff --git a/lib/wx/examples/simple/Makefile b/lib/wx/examples/simple/Makefile index 0178aea890..3f2bf01556 100644 --- a/lib/wx/examples/simple/Makefile +++ b/lib/wx/examples/simple/Makefile @@ -40,7 +40,7 @@ docs: run: opt erl -smp -detached -pa $(TOPDIR)/ebin -s hello -EXRELSYSDIR = $(RELSYSDIR)/examples/simple +EXRELSYSDIR = "$(RELSYSDIR)/examples/simple" include $(ERL_TOP)/make/otp_release_targets.mk docs: diff --git a/lib/wx/examples/sudoku/Makefile b/lib/wx/examples/sudoku/Makefile index 89919ff50c..ec900f37e2 100644 --- a/lib/wx/examples/sudoku/Makefile +++ b/lib/wx/examples/sudoku/Makefile @@ -40,7 +40,7 @@ docs: run: opt erl -smp -detached -pa $(TOPDIR)/ebin -s sudoku -EXRELSYSDIR = $(RELSYSDIR)/examples/sudoku +EXRELSYSDIR = "$(RELSYSDIR)/examples/sudoku" include $(ERL_TOP)/make/otp_release_targets.mk docs: diff --git a/lib/wx/examples/xrc/Makefile b/lib/wx/examples/xrc/Makefile index 341fdbb2cc..9a976628a7 100644 --- a/lib/wx/examples/xrc/Makefile +++ b/lib/wx/examples/xrc/Makefile @@ -49,7 +49,7 @@ docs: run: opt erl -smp -detached -pa $(TOPDIR)/ebin -s xrc -EXRELSYSDIR = $(RELSYSDIR)/examples/xrc +EXRELSYSDIR = "$(RELSYSDIR)/examples/xrc" include $(ERL_TOP)/make/otp_release_targets.mk docs: diff --git a/lib/wx/src/Makefile b/lib/wx/src/Makefile index 16b425e6f0..777fb7d998 100644 --- a/lib/wx/src/Makefile +++ b/lib/wx/src/Makefile @@ -120,15 +120,15 @@ $(EBIN)/%.beam: $(EGEN)/%.erl $(HEADER_FILES) # ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/gen - $(INSTALL_DATA) $(GEN_HRL) $(GEN_FILES) $(RELSYSDIR)/src/gen - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXT_HRL) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/gen" + $(INSTALL_DATA) $(GEN_HRL) $(GEN_FILES) "$(RELSYSDIR)/src/gen" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXT_HRL) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" # $(INSTALL_DATA) ../$(ARCHIVE) $(RELEASE_PATH)/lib release_docs_spec: diff --git a/lib/wx/src/gen/wxEvtHandler.erl b/lib/wx/src/gen/wxEvtHandler.erl index cf4a72da5a..22c203392c 100644 --- a/lib/wx/src/gen/wxEvtHandler.erl +++ b/lib/wx/src/gen/wxEvtHandler.erl @@ -80,7 +80,7 @@ connect(This, EventType) -> %% {userData, term()} An erlang term that will be sent with the event. Default: []. -spec connect(This::wxEvtHandler(), EventType::wxEventType(), [Option]) -> ok when Option :: {id, integer()} | {lastId, integer()} | {skip, boolean()} | - {callback, function()} | {userData, term()}. + callback | {callback, function()} | {userData, term()}. connect(This=#wx_ref{type=ThisT}, EventType, Options) -> EvH = parse_opts(Options, #evh{et=EventType}), ?CLASS(ThisT,wxEvtHandler), diff --git a/lib/wx/test/Makefile b/lib/wx/test/Makefile index ac8a4bdd60..836885a308 100644 --- a/lib/wx/test/Makefile +++ b/lib/wx/test/Makefile @@ -53,8 +53,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) wx.spec wx.cover wx_test_lib.hrl $(ErlSrc) $(ErlTargets) $(RELSYSDIR) - $(INSTALL_SCRIPT) wxt $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) wx.spec wx.cover wx_test_lib.hrl $(ErlSrc) $(ErlTargets) "$(RELSYSDIR)" + $(INSTALL_SCRIPT) wxt "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/xmerl/doc/src/Makefile b/lib/xmerl/doc/src/Makefile index 100a2feb0a..4de06249df 100644 --- a/lib/xmerl/doc/src/Makefile +++ b/lib/xmerl/doc/src/Makefile @@ -161,15 +161,15 @@ info: 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) $(EXAMPLE_FILES) $(HTML_EXAMPLE_FILES) $(HTML_STYLESHEET_FILES) $(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) $(EXAMPLE_FILES) $(HTML_EXAMPLE_FILES) $(HTML_STYLESHEET_FILES) "$(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/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index 585d8bb688..bd12e688d0 100644 --- a/lib/xmerl/doc/src/notes.xml +++ b/lib/xmerl/doc/src/notes.xml @@ -31,6 +31,21 @@ <p>This document describes the changes made to the Xmerl application.</p> +<section><title>Xmerl 1.3.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a continuation bug when a new block of bytes is + to be read during parsing of a default declaration. </p> + <p> + Own Id: OTP-10063 Aux Id: seq12049 </p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.3.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/xmerl/src/Makefile b/lib/xmerl/src/Makefile index a5dbe45909..ce1aa11fba 100644 --- a/lib/xmerl/src/Makefile +++ b/lib/xmerl/src/Makefile @@ -214,14 +214,14 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(APP_SRC) $(APPUP_SRC) $(RELSYSDIR)/src - $(INSTALL_DATA) xmerl_xpath_parse.yrl $(RELSYSDIR)/src - $(INSTALL_DATA) xmerl_b64Bin.yrl $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(APP_SRC) $(APPUP_SRC) "$(RELSYSDIR)/src" + $(INSTALL_DATA) xmerl_xpath_parse.yrl "$(RELSYSDIR)/src" + $(INSTALL_DATA) xmerl_b64Bin.yrl "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc index df0970ef14..d38045f2a5 100644 --- a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc +++ b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc @@ -155,16 +155,16 @@ parse_xml_decl(?BYTE_ORDER_MARK_2, State) -> cf(?BYTE_ORDER_MARK_2, State, fun parse_xml_decl/2); parse_xml_decl(?BYTE_ORDER_MARK_REST(Rest), State) -> cf(Rest, State, fun parse_xml_decl/2); -parse_xml_decl(?STRING("<"), State) -> - cf(?STRING("<"), State, fun parse_xml_decl/2); -parse_xml_decl(?STRING("<?"), State) -> - cf(?STRING("<?"), State, fun parse_xml_decl/2); -parse_xml_decl(?STRING("<?x"), State) -> - cf(?STRING("<?x"), State, fun parse_xml_decl/2); -parse_xml_decl(?STRING("<?xm"), State) -> - cf(?STRING("<?xm"), State, fun parse_xml_decl/2); -parse_xml_decl(?STRING("<?xml"), State) -> - cf(?STRING("<?xml"), State, fun parse_xml_decl/2); +parse_xml_decl(?STRING("<") = Bytes, State) -> + cf(Bytes, State, fun parse_xml_decl/2); +parse_xml_decl(?STRING("<?") = Bytes, State) -> + cf(Bytes, State, fun parse_xml_decl/2); +parse_xml_decl(?STRING("<?x") = Bytes, State) -> + cf(Bytes, State, fun parse_xml_decl/2); +parse_xml_decl(?STRING("<?xm") = Bytes, State) -> + cf(Bytes, State, fun parse_xml_decl/2); +parse_xml_decl(?STRING("<?xml") = Bytes, State) -> + cf(Bytes, State, fun parse_xml_decl/2); parse_xml_decl(?STRING_REST("<?xml", Rest1), State) -> parse_xml_decl_1(Rest1, State); parse_xml_decl(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State) when is_binary(Bytes) -> @@ -204,8 +204,8 @@ parse_xml_decl_1(Bytes, State) -> %%---------------------------------------------------------------------- parse_prolog(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_prolog/2); -parse_prolog(?STRING("<"), State) -> - cf(?STRING("<"), State, fun parse_prolog/2); +parse_prolog(?STRING("<") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog/2); parse_prolog(?STRING_REST("<?", Rest), State) -> {Rest1, State1} = parse_pi(Rest, State), parse_prolog(Rest1, State1); @@ -223,18 +223,18 @@ parse_prolog(Bytes, State) -> parse_prolog_1(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("D"), State) -> - cf(?STRING("D"), State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("DO"), State) -> - cf(?STRING("DO"), State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("DOC"), State) -> - cf(?STRING("DOC"), State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("DOCT"), State) -> - cf(?STRING("DOCT"), State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("DOCTY"), State) -> - cf(?STRING("DOCTY"), State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("DOCTYP"), State) -> - cf(?STRING("DOCTYP"), State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("D") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("DO") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("DOC") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("DOCT") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("DOCTY") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("DOCTYP") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); parse_prolog_1(?STRING_REST("DOCTYPE", Rest), State) -> {Rest1, State1} = parse_doctype(Rest, State), State2 = event_callback(endDTD, State1), @@ -512,10 +512,10 @@ parse_ns_name(Bytes, State, Prefix, Name) -> %%---------------------------------------------------------------------- parse_pi_data(?STRING_EMPTY, State, Acc) -> cf(?STRING_EMPTY, State, Acc, fun parse_pi_data/3); -parse_pi_data(?STRING("?"), State, Acc) -> - cf(?STRING("?"), State, Acc, fun parse_pi_data/3); -parse_pi_data(?STRING("\r"), State, Acc) -> - cf(?STRING("\r"), State, Acc, fun parse_pi_data/3); +parse_pi_data(?STRING("?") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_pi_data/3); +parse_pi_data(?STRING("\r") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_pi_data/3); parse_pi_data(?STRING_REST("?>", Rest), State, Acc) -> {lists:reverse(Acc), Rest, State}; parse_pi_data(?STRING_REST("\n", Rest), #xmerl_sax_parser_state{line_no=N} = State, Acc) -> @@ -544,23 +544,23 @@ parse_pi_data(Bytes, State, Acc) -> %%---------------------------------------------------------------------- parse_cdata(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_cdata/2); -parse_cdata(?STRING("["), State) -> - cf(?STRING("["), State, fun parse_cdata/2); -parse_cdata(?STRING("[C"), State) -> - cf(?STRING("[C"), State, fun parse_cdata/2); -parse_cdata(?STRING("[CD"), State) -> - cf(?STRING("[CD"), State, fun parse_cdata/2); -parse_cdata(?STRING("[CDA"), State) -> - cf(?STRING("[CDA"), State, fun parse_cdata/2); -parse_cdata(?STRING("[CDAT"), State) -> - cf(?STRING("[CDAT"), State, fun parse_cdata/2); -parse_cdata(?STRING("[CDATA"), State) -> - cf(?STRING("[CDATA"), State, fun parse_cdata/2); -parse_cdata(?STRING_REST("[CDATA[", Rest), State) -> - State1 = event_callback(startCDATA, State), +parse_cdata(?STRING("[") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING("[C") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING("[CD") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING("[CDA") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING("[CDAT") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING("[CDATA") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING_REST("[CDATA[", Rest), State) -> + State1 = event_callback(startCDATA, State), parse_cdata(Rest, State1, []); -parse_cdata(Bytes, State) -> - unicode_incomplete_check([Bytes, State, fun parse_cdata/2], +parse_cdata(Bytes, State) -> + unicode_incomplete_check([Bytes, State, fun parse_cdata/2], "expecting comment or CDATA"). @@ -574,12 +574,12 @@ parse_cdata(Bytes, State) -> %%---------------------------------------------------------------------- parse_cdata(?STRING_EMPTY, State, Acc) -> cf(?STRING_EMPTY, State, Acc, fun parse_cdata/3); -parse_cdata(?STRING("\r"), State, Acc) -> - cf(?STRING("\r"), State, Acc, fun parse_cdata/3); -parse_cdata(?STRING("]"), State, Acc) -> - cf(?STRING("]"), State, Acc, fun parse_cdata/3); -parse_cdata(?STRING("]]"), State, Acc) -> - cf(?STRING("]]"), State, Acc, fun parse_cdata/3); +parse_cdata(?STRING("\r") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_cdata/3); +parse_cdata(?STRING("]") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_cdata/3); +parse_cdata(?STRING("]]") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_cdata/3); parse_cdata(?STRING_REST("]]>", Rest), State, Acc) -> State1 = event_callback({characters, lists:reverse(Acc)}, State), State2 = event_callback(endCDATA, State1), @@ -610,12 +610,12 @@ parse_cdata(Bytes, State, Acc) -> %%---------------------------------------------------------------------- parse_comment(?STRING_EMPTY, State, Acc) -> cf(?STRING_EMPTY, State, Acc, fun parse_comment/3); -parse_comment(?STRING("\r"), State, Acc) -> - cf(?STRING("\r"), State, Acc, fun parse_comment/3); -parse_comment(?STRING("-"), State, Acc) -> - cf(?STRING("-"), State, Acc, fun parse_comment/3); -parse_comment(?STRING("--"), State, Acc) -> - cf(?STRING("--"), State, Acc, fun parse_comment/3); +parse_comment(?STRING("\r") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_comment/3); +parse_comment(?STRING("-") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_comment/3); +parse_comment(?STRING("--") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_comment/3); parse_comment(?STRING_REST("-->", Rest), State, Acc) -> State1 = event_callback({comment, lists:reverse(Acc)}, State), {Rest, State1}; @@ -713,8 +713,8 @@ parse_stag(Bytes, State) -> %%---------------------------------------------------------------------- parse_attributes(?STRING_EMPTY, State, CurrentTag) -> cf(?STRING_EMPTY, State, CurrentTag, fun parse_attributes/3); -parse_attributes(?STRING("/"), State, CurrentTag) -> - cf(?STRING("/"), State, CurrentTag, fun parse_attributes/3); +parse_attributes(?STRING("/") = Bytes, State, CurrentTag) -> + cf(Bytes, State, CurrentTag, fun parse_attributes/3); parse_attributes(?STRING_REST("/>", Rest), State, {Tag, AttList, NewNsList}) -> CompleteNsList = NewNsList ++ State#xmerl_sax_parser_state.ns, {Uri, LocalName, QName, Attributes} = fix_ns(Tag, AttList, CompleteNsList), @@ -911,20 +911,20 @@ parse_att_value(?STRING_EMPTY, State, undefined, Acc) -> {Acc, [], State}; %% stop clause when parsing references parse_att_value(?STRING_EMPTY, State, Stop, Acc) -> cf(?STRING_EMPTY, State, Stop, Acc, fun parse_att_value/4); -parse_att_value(?STRING("\r"), State, Stop, Acc) -> - cf(?STRING("\r"), State, Stop, Acc, fun parse_att_value/4); +parse_att_value(?STRING("\r") = Bytes, State, Stop, Acc) -> + cf(Bytes, State, Stop, Acc, fun parse_att_value/4); parse_att_value(?STRING_REST("\n", Rest), #xmerl_sax_parser_state{line_no=N} = State, Stop, Acc) -> parse_att_value(Rest, - State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); + State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); parse_att_value(?STRING_REST("\r\n", Rest), #xmerl_sax_parser_state{line_no=N} = State, Stop, Acc) -> parse_att_value(Rest, - State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); + State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); parse_att_value(?STRING_REST("\r", Rest), #xmerl_sax_parser_state{line_no=N} = State, Stop, Acc) -> parse_att_value(Rest, - State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); + State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); parse_att_value(?STRING_REST("\t", Rest), #xmerl_sax_parser_state{line_no=N} = State, Stop, Acc) -> parse_att_value(Rest, - State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); + State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); parse_att_value(?STRING_REST("&", Rest), State, Stop, Acc) -> {Ref, Rest1, State1} = parse_reference(Rest, State, true), case Ref of @@ -1046,17 +1046,17 @@ parse_content(?STRING_EMPTY, State, Acc, IgnorableWS) -> Other -> throw(Other) end; -parse_content(?STRING("\r"), State, Acc, IgnorableWS) -> - cf(?STRING("\r"), State, Acc, IgnorableWS, fun parse_content/4); -parse_content(?STRING("<"), State, Acc, IgnorableWS) -> - cf(?STRING("<"), State, Acc, IgnorableWS, fun parse_content/4); +parse_content(?STRING("\r") = Bytes, State, Acc, IgnorableWS) -> + cf(Bytes, State, Acc, IgnorableWS, fun parse_content/4); +parse_content(?STRING("<") = Bytes, State, Acc, IgnorableWS) -> + cf(Bytes, State, Acc, IgnorableWS, fun parse_content/4); parse_content(?STRING_REST("</", Rest), State, Acc, IgnorableWS) -> State1 = send_character_event(length(Acc), IgnorableWS, lists:reverse(Acc), State), parse_etag(Rest, State1); -parse_content(?STRING("<!"), State, _Acc, IgnorableWS) -> - cf(?STRING("<!"), State, [], IgnorableWS, fun parse_content/4); -parse_content(?STRING("<!-"), State, _Acc, IgnorableWS) -> - cf(?STRING("<!-"), State, [], IgnorableWS, fun parse_content/4); +parse_content(?STRING("<!") = Bytes, State, _Acc, IgnorableWS) -> + cf(Bytes, State, [], IgnorableWS, fun parse_content/4); +parse_content(?STRING("<!-") = Bytes, State, _Acc, IgnorableWS) -> + cf(Bytes, State, [], IgnorableWS, fun parse_content/4); parse_content(?STRING_REST("<!--", Rest), State, Acc, IgnorableWS) -> State1 = send_character_event(length(Acc), IgnorableWS, lists:reverse(Acc), State), {Rest1, State2} = parse_comment(Rest, State1, []), @@ -1227,8 +1227,8 @@ whitespace(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State, Acc) when is_bi %%---------------------------------------------------------------------- parse_reference(?STRING_EMPTY, State, HaveToExist) -> cf(?STRING_EMPTY, State, HaveToExist, fun parse_reference/3); -parse_reference(?STRING("#"), State, HaveToExist) -> - cf(?STRING("#"), State, HaveToExist, fun parse_reference/3); +parse_reference(?STRING("#") = Bytes, State, HaveToExist) -> + cf(Bytes, State, HaveToExist, fun parse_reference/3); parse_reference(?STRING_REST("#x", Rest), State, _HaveToExist) -> {CharValue, RefString, Rest1, State1} = parse_hex(Rest, State, []), if @@ -1702,16 +1702,16 @@ parse_external_entity_1(?BYTE_ORDER_MARK_2, State) -> cf(?BYTE_ORDER_MARK_2, State, fun parse_external_entity_1/2); parse_external_entity_1(?BYTE_ORDER_MARK_REST(Rest), State) -> parse_external_entity_1(Rest, State); -parse_external_entity_1(?STRING("<"), State) -> - cf(?STRING("<"), State, fun parse_external_entity_1/2); -parse_external_entity_1(?STRING("<?"), State) -> - cf(?STRING("<?"), State, fun parse_external_entity_1/2); -parse_external_entity_1(?STRING("<?x"), State) -> - cf(?STRING("<?x"), State, fun parse_external_entity_1/2); -parse_external_entity_1(?STRING("<?xm"), State) -> - cf(?STRING("<?xm"), State, fun parse_external_entity_1/2); -parse_external_entity_1(?STRING("<?xml"), State) -> - cf(?STRING("<?xml"), State, fun parse_external_entity_1/2); +parse_external_entity_1(?STRING("<") = Bytes, State) -> + cf(Bytes, State, fun parse_external_entity_1/2); +parse_external_entity_1(?STRING("<?") = Bytes, State) -> + cf(Bytes, State, fun parse_external_entity_1/2); +parse_external_entity_1(?STRING("<?x") = Bytes, State) -> + cf(Bytes, State, fun parse_external_entity_1/2); +parse_external_entity_1(?STRING("<?xm") = Bytes, State) -> + cf(Bytes, State, fun parse_external_entity_1/2); +parse_external_entity_1(?STRING("<?xml") = Bytes, State) -> + cf(Bytes, State, fun parse_external_entity_1/2); parse_external_entity_1(?STRING_REST("<?xml", Rest) = Bytes, #xmerl_sax_parser_state{file_type=Type} = State) -> {Rest1, State1} = @@ -1781,29 +1781,29 @@ is_next_char_whitespace(Bytes, State) -> %%---------------------------------------------------------------------- parse_external_id(?STRING_EMPTY, State, OptionalSystemId) -> cf(?STRING_EMPTY, State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("S"), State,OptionalSystemId) -> - cf(?STRING("S"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("SY"), State, OptionalSystemId) -> - cf(?STRING("SY"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("SYS"), State, OptionalSystemId) -> - cf(?STRING("SYS"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("SYST"), State, OptionalSystemId) -> - cf(?STRING("SYST"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("SYSTE"), State, OptionalSystemId) -> - cf(?STRING("SYSTE"), State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("S") = Bytes, State,OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("SY") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("SYS") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("SYST") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("SYSTE") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); parse_external_id(?STRING_REST("SYSTEM", Rest), State, _) -> {SysId, Rest1, State1} = parse_system_id(Rest, State, false), {"", SysId, Rest1, State1}; -parse_external_id(?STRING("P"), State, OptionalSystemId) -> - cf(?STRING("P"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("PU"), State, OptionalSystemId) -> - cf(?STRING("PU"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("PUB"), State, OptionalSystemId) -> - cf(?STRING("PUB"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("PUBL"), State, OptionalSystemId) -> - cf(?STRING("PUBL"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("PUBLI"), State, OptionalSystemId) -> - cf(?STRING("PUBLI"), State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("P") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("PU") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("PUB") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("PUBL") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("PUBLI") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); parse_external_id(?STRING_REST("PUBLIC", Rest), State, OptionalSystemId) -> parse_public_id(Rest, State, OptionalSystemId); parse_external_id(Bytes, State, OptionalSystemId) -> @@ -1923,70 +1923,70 @@ parse_doctype_decl(Bytes, State) -> parse_doctype_decl_1(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("E"), State) -> - cf(?STRING("E"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("EL"), State) -> - cf(?STRING("EL"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ELE"), State) -> - cf(?STRING("ELE"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ELEM"), State) -> - cf(?STRING("ELEM"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ELEME"), State) -> - cf(?STRING("ELEME"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ELEMEN"), State) -> - cf(?STRING("ELEMEN"), State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("E") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("EL") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ELE") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ELEM") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ELEME") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ELEMEN") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); parse_doctype_decl_1(?STRING_REST("ELEMENT", Rest), State) -> {Rest1, State1} = parse_element_decl(Rest, State), parse_doctype_decl(Rest1, State1); -parse_doctype_decl_1(?STRING("A"), State) -> - cf(?STRING("A"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("AT"), State) -> - cf(?STRING("AT"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ATT"), State) -> - cf(?STRING("ATT"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ATTL"), State) -> - cf(?STRING("ATTL"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ATTLI"), State) -> - cf(?STRING("ATTLI"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ATTLIS"), State) -> - cf(?STRING("ATTLIS"), State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("A") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("AT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ATT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ATTL") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ATTLI") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ATTLIS") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); parse_doctype_decl_1(?STRING_REST("ATTLIST", Rest), State) -> {Rest1, State1} = parse_att_list_decl(Rest, State), parse_doctype_decl(Rest1, State1); %% E clause not needed here because already taken care of above. -parse_doctype_decl_1(?STRING("EN"), State) -> - cf(?STRING("EN"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ENT"), State) -> - cf(?STRING("ENT"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ENTI"), State) -> - cf(?STRING("ENTI"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ENTIT"), State) -> - cf(?STRING("ENTIT"), State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("EN") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ENT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ENTI") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ENTIT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); parse_doctype_decl_1(?STRING_REST("ENTITY", Rest), State) -> {Rest1, State1} = parse_entity_decl(Rest, State), parse_doctype_decl(Rest1, State1); -parse_doctype_decl_1(?STRING("N"), State) -> - cf(?STRING("N"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NO"), State) -> - cf(?STRING("NO"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NOT"), State) -> - cf(?STRING("NOT"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NOTA"), State) -> - cf(?STRING("NOTA"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NOTAT"), State) -> - cf(?STRING("NOTAT"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NOTATI"), State) -> - cf(?STRING("NOTATI"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NOTATIO"), State) -> - cf(?STRING("NOTATIO"), State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("N") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NO") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NOT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NOTA") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NOTAT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NOTATI") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NOTATIO") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); parse_doctype_decl_1(?STRING_REST("NOTATION", Rest), State) -> {Rest1, State1} = parse_notation_decl(Rest, State), parse_doctype_decl(Rest1, State1); -parse_doctype_decl_1(?STRING("-"), State) -> - cf(?STRING("-"), State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("-") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); parse_doctype_decl_1(?STRING_REST("--", Rest), State) -> {Rest1, State1} = parse_comment(Rest, State, []), parse_doctype_decl(Rest1, State1); @@ -2264,52 +2264,52 @@ parse_default_decl(Bytes, State) -> %%---------------------------------------------------------------------- parse_default_decl_1(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_default_decl_1/2); -parse_default_decl_1(?STRING_REST("#", Rest), State) -> - case Rest of - ?STRING("R") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("RE") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("REQ") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("REQU") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("REQUI") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("REQUIR") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("REQUIRE") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING_REST("REQUIRED", Rest1) -> +parse_default_decl_1(?STRING_REST("#", _Rest) = Bytes, State) -> + case Bytes of + ?STRING("#R") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#RE") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#REQ") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#REQU") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#REQUI") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#REQUIR") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#REQUIRE") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING_REST("#REQUIRED", Rest1) -> {"#REQUIRED", undefined, Rest1, State}; - ?STRING("I") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("IM") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("IMP") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("IMPL") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("IMPLI") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("IMPLIE") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING_REST("IMPLIED", Rest1) -> + ?STRING("#I") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#IM") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#IMP") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#IMPL") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#IMPLI") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#IMPLIE") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING_REST("#IMPLIED", Rest1) -> {"#IMPLIED", undefined, Rest1, State}; - ?STRING("F") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("FI") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("FIX") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("FIXE") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING_REST("FIXED", Rest1) -> + ?STRING("#F") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#FI") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#FIX") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#FIXE") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING_REST("#FIXED", Rest1) -> parse_fixed(Rest1, State); _ -> - ?fatal_error(State, "REQUIRED, IMPLIED or FIXED expected") + ?fatal_error(State, "REQUIRED, IMPLIED or FIXED expected after #") end; parse_default_decl_1(?STRING_UNBOUND_REST(C, Rest), State) when C == $'; C == $" -> {DefaultValue, Rest1, State1} = parse_att_value(Rest, State, C, []), @@ -2560,14 +2560,14 @@ parse_ndata_decl(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_ndata_decl/2); parse_ndata_decl(?STRING_REST(">", Rest), State) -> {undefined, Rest, State}; -parse_ndata_decl(?STRING("N") = Rest, State) -> - cf(Rest, State, fun parse_ndata_decl/2); -parse_ndata_decl(?STRING("ND") = Rest, State) -> - cf(Rest, State, fun parse_ndata_decl/2); -parse_ndata_decl(?STRING("NDA") = Rest, State) -> - cf(Rest, State, fun parse_ndata_decl/2); -parse_ndata_decl(?STRING("NDAT") = Rest, State) -> - cf(Rest, State, fun parse_ndata_decl/2); +parse_ndata_decl(?STRING("N") = Bytes, State) -> + cf(Bytes, State, fun parse_ndata_decl/2); +parse_ndata_decl(?STRING("ND") = Bytes, State) -> + cf(Bytes, State, fun parse_ndata_decl/2); +parse_ndata_decl(?STRING("NDA") = Bytes, State) -> + cf(Bytes, State, fun parse_ndata_decl/2); +parse_ndata_decl(?STRING("NDAT") = Bytes, State) -> + cf(Bytes, State, fun parse_ndata_decl/2); parse_ndata_decl(?STRING_REST("NDATA", Rest), State) -> parse_ndata_decl_1(Rest, State); parse_ndata_decl(Bytes, State) -> diff --git a/lib/xmerl/test/Makefile b/lib/xmerl/test/Makefile index 5a2a585841..1fdbb7ca0d 100644 --- a/lib/xmerl/test/Makefile +++ b/lib/xmerl/test/Makefile @@ -113,15 +113,15 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_FILES) $(RELSYSDIR) - $(INSTALL_DATA) xmerl.spec xmerl.cover $(RELSYSDIR) - cp $(XML_FILES) $(RELSYSDIR) - @tar cfh - xmerl_SUITE_data | (cd $(RELSYSDIR); tar xf -) - @tar cfh - xmerl_std_SUITE_data | (cd $(RELSYSDIR); tar xf -) - @tar cfh - xmerl_xsd_SUITE_data | (cd $(RELSYSDIR); tar xf -) - @tar cfh - xmerl_xsd_MS2002-01-16_SUITE_data | (cd $(RELSYSDIR); tar xf -) - @tar cfh - xmerl_xsd_NIST2002-01-16_SUITE_data | (cd $(RELSYSDIR); tar xf -) - @tar cfh - xmerl_xsd_Sun2002-01-16_SUITE_data | (cd $(RELSYSDIR); tar xf -) - chmod -R u+w $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) xmerl.spec xmerl.cover "$(RELSYSDIR)" + cp $(XML_FILES) "$(RELSYSDIR)" + @tar cfh - xmerl_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + @tar cfh - xmerl_std_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + @tar cfh - xmerl_xsd_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + @tar cfh - xmerl_xsd_MS2002-01-16_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + @tar cfh - xmerl_xsd_NIST2002-01-16_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + @tar cfh - xmerl_xsd_Sun2002-01-16_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + chmod -R u+w "$(RELSYSDIR)" diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk index 399e5b3602..599bc0b9d3 100644 --- a/lib/xmerl/vsn.mk +++ b/lib/xmerl/vsn.mk @@ -1 +1 @@ -XMERL_VSN = 1.3.1 +XMERL_VSN = 1.3.2 diff --git a/make/otp_release_targets.mk b/make/otp_release_targets.mk index f2387e4b74..fa87a4306a 100644 --- a/make/otp_release_targets.mk +++ b/make/otp_release_targets.mk @@ -110,12 +110,12 @@ endif ifeq ($(TESTROOT),) release release_docs release_tests release_html: - $(MAKE) $(MFLAGS) RELEASE_PATH=$(ERL_TOP)/release/$(TARGET) \ + $(MAKE) $(MFLAGS) RELEASE_PATH="$(ERL_TOP)/release/$(TARGET)" \ $(TARGET_MAKEFILE) $@_spec else release release_docs release_tests release_html: - $(MAKE) $(MFLAGS) RELEASE_PATH=$(TESTROOT) $(TARGET_MAKEFILE) $@_spec + $(MAKE) $(MFLAGS) RELEASE_PATH="$(TESTROOT)" $(TARGET_MAKEFILE) $@_spec endif diff --git a/make/run_make.mk b/make/run_make.mk index 39c3668e50..b7a5a64847 100644 --- a/make/run_make.mk +++ b/make/run_make.mk @@ -32,7 +32,7 @@ include $(ERL_TOP)/make/target.mk opt debug purify quantify purecov valgrind gcov gprof lcnt: $(MAKE) -f $(TARGET)/Makefile TYPE=$@ -plain smp hybrid frag smp_frag: +plain smp frag smp_frag: $(MAKE) -f $(TARGET)/Makefile FLAVOR=$@ clean generate depend docs release release_spec release_docs release_docs_spec \ @@ -51,7 +51,6 @@ usage () echo " release <target_dir> - creates a small release to <target_dir>" echo " release [-a] <target_dir> - creates full release to <target_dir>" echo " smp [-a] - build an Erlang system, smp flavor only" - echo " hybrid [-a] - build an Erlang system, hybrid flavor only" echo " tests <dir> - Build testsuites to <dir>" echo "" echo "These are for cleaning up an open source distribution" @@ -84,7 +83,7 @@ usage () ;; git) echo "" - echo "update_primary - build and commit a new primary bootstrap" + echo "update_primary [--no-commit] - build and maybe commit a new primary bootstrap" ;; esac @@ -93,7 +92,7 @@ usage () ;; git) echo "" - echo "update_preloaded - build and commit the preloaded modules" + echo "update_preloaded [--no-commit] - build and maybe commit the preloaded modules" ;; esac } @@ -1094,33 +1093,61 @@ setup_make () fi export MAKE } - + +get_do_commit () +{ + if [ "x$1" = "x" ]; then + do_commit=true + elif [ "$1" = "--no-commit" ]; then + do_commit=false + else + echo "Unknown option '$1'" 1>&2 + exit 1 + fi +} + do_primary_git () { + get_do_commit $1 setup_make if [ "x$OVERRIDE_TARGET" != "x" -a "x$OVERRIDE_TARGET" != "xwin32" ]; then do_primary_cross else $MAKE MAKE="$MAKE" BOOTSTRAP_ROOT=$BOOTSTRAP_ROOT TARGET=$TARGET primary_bootstrap || exit 1; fi - git add -A bootstrap/lib/kernel \ - bootstrap/lib/stdlib \ - bootstrap/lib/compiler \ - bootstrap/bin - find bootstrap -name egen -o -name '*.script' -o \ - -name '*.app' -o -name '*.appup' | + if [ $do_commit = true ]; then + git add -A bootstrap/lib/kernel \ + bootstrap/lib/stdlib \ + bootstrap/lib/compiler \ + bootstrap/bin + find bootstrap -name egen -o -name '*.script' -o \ + -name '*.app' -o -name '*.appup' | xargs git reset HEAD - git commit --no-verify -m 'Update primary bootstrap' + git commit --no-verify -m 'Update primary bootstrap' + echo "Primary bootstrap updated and commited." + else + echo "" + echo "Primary bootstrap rebuilt. Use \"git add bootstrap/...\" to stage changed files." + echo "" + fi } do_update_prel_git () { + get_do_commit $1 setup_make (cd $ERL_TOP/erts/preloaded/src && $MAKE MAKE="$MAKE" BOOTSTRAP_ROOT=$BOOTSTRAP_ROOT TARGET=$TARGET clean) $MAKE MAKE="$MAKE" BOOTSTRAP_ROOT=$BOOTSTRAP_ROOT TARGET=$TARGET preloaded || exit 1 (cd $ERL_TOP/erts/preloaded/src && $MAKE MAKE="$MAKE" BOOTSTRAP_ROOT=$BOOTSTRAP_ROOT TARGET=$TARGET copy) - git add -A $ERL_TOP/erts/preloaded/ebin/*.beam - git commit -m 'Update preloaded modules' + if [ $do_commit = true ]; then + git add -A $ERL_TOP/erts/preloaded/ebin/*.beam + git commit -m 'Update preloaded modules' + echo "Preloaded updated and commited." + else + echo "" + echo "Preloaded rebuilt. Use \"git add erts/preloaded/ebin/...\" to stage changed beam files." + echo "" + fi } do_boot () @@ -1281,8 +1308,8 @@ cd $ERL_TOP determine_version_controller -# Unset ERL_FLAGS and ERL_<Release>_FLAGS to prevent, for instance, -# a value of "-hybrid" to run the hybrid emulator during bootstrap. +# Unset ERL_FLAGS and ERL_<Release>_FLAGS during bootstrap to +# prevent potential problems sys_vsn=`awk '/SYSTEM_VSN = / {print $3}' < erts/vsn.mk` sys_erl_flags="ERL_${sys_vsn}_FLAGS" unset ERL_FLAGS @@ -1311,7 +1338,9 @@ case $TARGET in if [ X"$OVERRIDE_TARGET" = X"" -a X"$1" != X"env_win32" -a X"$1" != X"env_msys32" -a X"$1" != X"env_msys64" ];then echo "Building for windows, you should do the " \ "following first:" >&2 - echo 'eval `./otp_build env_msys64`' >&2 + echo 'eval `./otp_build env_win32`' >&2 + echo 'or' >&2 + echo 'eval `./otp_build env_win32 x64`' >&2 echo 'please note that there are backticks (``) in' \ 'the command' exit 1 @@ -1390,7 +1419,7 @@ case "$1" in do_lazy_configure_target_clean;; opt) do_boot;; - plain|smp|hybrid) + plain|smp) if [ $minus_a_flag = false ]; then TYPE=opt fi; @@ -1401,12 +1430,12 @@ case "$1" in do_boot;; update_primary) case $version_controller in - git) do_primary_git ;; + git) do_primary_git "$2";; none) git_required ;; esac ;; update_preloaded) case $version_controller in - git) do_update_prel_git ;; + git) do_update_prel_git "$2";; none) git_required ;; esac ;; primary) diff --git a/system/doc/design_principles/Makefile b/system/doc/design_principles/Makefile index bffff75be2..a9d4665ee4 100644 --- a/system/doc/design_principles/Makefile +++ b/system/doc/design_principles/Makefile @@ -29,7 +29,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/design_principles +RELSYSDIR = "$(RELEASE_PATH)/doc/design_principles" # ---------------------------------------------------- # Target Specs @@ -105,8 +105,8 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs -# $(INSTALL_DIR) $(RELEASE_PATH)/pdf -# $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf +# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf" +# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf" $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \ $(RELSYSDIR) diff --git a/system/doc/efficiency_guide/Makefile b/system/doc/efficiency_guide/Makefile index c08a9d2f69..1fd7b36453 100644 --- a/system/doc/efficiency_guide/Makefile +++ b/system/doc/efficiency_guide/Makefile @@ -30,7 +30,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/efficiency_guide +RELSYSDIR = "$(RELEASE_PATH)/doc/efficiency_guide" # ---------------------------------------------------- # Target Specs @@ -106,8 +106,8 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs -# $(INSTALL_DIR) $(RELEASE_PATH)/pdf -# $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf +# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf" +# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf" $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \ $(RELSYSDIR) diff --git a/system/doc/embedded/Makefile b/system/doc/embedded/Makefile index 7dda0eaaa7..825fb23cfe 100644 --- a/system/doc/embedded/Makefile +++ b/system/doc/embedded/Makefile @@ -30,7 +30,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/embedded +RELSYSDIR = "$(RELEASE_PATH)/doc/embedded" # ---------------------------------------------------- # Target Specs @@ -94,8 +94,8 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs -# $(INSTALL_DIR) $(RELEASE_PATH)/pdf -# $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf +# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf" +# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf" $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \ $(RELSYSDIR) diff --git a/system/doc/getting_started/Makefile b/system/doc/getting_started/Makefile index 62b92613e5..972a51d3a5 100644 --- a/system/doc/getting_started/Makefile +++ b/system/doc/getting_started/Makefile @@ -30,7 +30,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/getting_started +RELSYSDIR = "$(RELEASE_PATH)/doc/getting_started" # ---------------------------------------------------- # Target Specs @@ -93,8 +93,8 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs -# $(INSTALL_DIR) $(RELEASE_PATH)/pdf -# $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf +# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf" +# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf" $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \ $(RELSYSDIR) diff --git a/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile index a47d77604d..4636650570 100644 --- a/system/doc/installation_guide/Makefile +++ b/system/doc/installation_guide/Makefile @@ -30,7 +30,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/installation_guide +RELSYSDIR = "$(RELEASE_PATH)/doc/installation_guide" REDIRECT_HTML_RELSYSDIR = $(RELSYSDIR)/source # ---------------------------------------------------- diff --git a/system/doc/oam/Makefile b/system/doc/oam/Makefile index 510877b189..f459f4349f 100644 --- a/system/doc/oam/Makefile +++ b/system/doc/oam/Makefile @@ -29,7 +29,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/oam +RELSYSDIR = "$(RELEASE_PATH)/doc/oam" # ---------------------------------------------------- # Target Specs diff --git a/system/doc/programming_examples/Makefile b/system/doc/programming_examples/Makefile index 41a65273db..fcc51e3b18 100644 --- a/system/doc/programming_examples/Makefile +++ b/system/doc/programming_examples/Makefile @@ -30,7 +30,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/programming_examples +RELSYSDIR = "$(RELEASE_PATH)/doc/programming_examples" # ---------------------------------------------------- # Target Specs diff --git a/system/doc/reference_manual/Makefile b/system/doc/reference_manual/Makefile index 8de9c39d03..87a477d14e 100644 --- a/system/doc/reference_manual/Makefile +++ b/system/doc/reference_manual/Makefile @@ -30,7 +30,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/reference_manual +RELSYSDIR = "$(RELEASE_PATH)/doc/reference_manual" # ---------------------------------------------------- # Target Specs @@ -103,8 +103,8 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs -# $(INSTALL_DIR) $(RELEASE_PATH)/pdf -# $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf +# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf" +# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf" $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \ $(RELSYSDIR) diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml index 5fca7225bb..d564b20ca6 100644 --- a/system/doc/reference_manual/expressions.xml +++ b/system/doc/reference_manual/expressions.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2011</year> + <year>2003</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -685,7 +685,7 @@ Expr1 <input>op</input> Expr2</pre> 8> <input>2#10 bor 2#01.</input> 3 9> <input>a + 10.</input> -** exception error: bad argument in an arithmetic expression +** exception error: an error occurred when evaluating an arithmetic expression in operator +/2 called as a + 10 10> <input>1 bsl (1 bsl 64).</input> diff --git a/system/doc/system_architecture_intro/Makefile b/system/doc/system_architecture_intro/Makefile index 9cf135a6f9..2de67473c1 100644 --- a/system/doc/system_architecture_intro/Makefile +++ b/system/doc/system_architecture_intro/Makefile @@ -30,7 +30,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/system_architecture_intro +RELSYSDIR = "$(RELEASE_PATH)/doc/system_architecture_intro" # ---------------------------------------------------- # Target Specs @@ -88,8 +88,8 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs -# $(INSTALL_DIR) $(RELEASE_PATH)/pdf -# $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf +# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf" +# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf" $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \ $(RELSYSDIR) diff --git a/system/doc/system_principles/Makefile b/system/doc/system_principles/Makefile index da37bd53cf..4b7fc8f177 100644 --- a/system/doc/system_principles/Makefile +++ b/system/doc/system_principles/Makefile @@ -30,7 +30,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/system_principles +RELSYSDIR = "$(RELEASE_PATH)/doc/system_principles" # ---------------------------------------------------- # Target Specs @@ -87,8 +87,8 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs -# $(INSTALL_DIR) $(RELEASE_PATH)/pdf -# $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf +# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf" +# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf" $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \ $(RELSYSDIR) diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile index 9f69a7210b..bbdb4d7055 100644 --- a/system/doc/top/Makefile +++ b/system/doc/top/Makefile @@ -30,7 +30,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc +RELSYSDIR = "$(RELEASE_PATH)/doc" GIF_FILES = @@ -233,8 +233,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELEASE_PATH) - $(INSTALL_DATA) $(INFO_FILES) $(RELEASE_PATH) + $(INSTALL_DIR) "$(RELEASE_PATH)" + $(INSTALL_DATA) $(INFO_FILES) "$(RELEASE_PATH)" $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DIR) $(RELSYSDIR)/pdf $(INSTALL_DATA) \ diff --git a/system/doc/tutorial/Makefile b/system/doc/tutorial/Makefile index d91d98481d..e9eb800e46 100644 --- a/system/doc/tutorial/Makefile +++ b/system/doc/tutorial/Makefile @@ -30,7 +30,7 @@ APPLICATION=otp-system-documentation # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/doc/tutorial +RELSYSDIR = "$(RELEASE_PATH)/doc/tutorial" # ---------------------------------------------------- # Target Specs @@ -114,8 +114,8 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs -# $(INSTALL_DIR) $(RELEASE_PATH)/pdf -# $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf +# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf" +# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf" $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTMLDIR)/*.html \ $(RELSYSDIR) diff --git a/xcomp/README.md b/xcomp/README.md index 2d79107283..7217fea5fb 100644 --- a/xcomp/README.md +++ b/xcomp/README.md @@ -291,6 +291,38 @@ and then do the cross build of the system. `otp_build release -a` will do the same as (5), and you will after this have to do a manual install either by doing (6), or (7). +Testing the cross compiled system +-------------------------------------- +Some of the tests that come with erlang use native code to test. This means +that when cross compiling erlang you also have to cross compile test suites +in order to run tests on the target host. To do this you first have to release +the tests as usual. + + $ make release_tests + +or + + $ ./otp_build tests + +The tests will be released into `$ERL_TOP/release/tests`. After releasing the +tests you have to install the tests on the build machine. You supply the same +xcomp file as to `./otp_build` in (9). + + $ cd $ERL_TOP/release/tests/test_server/ + $ $ERL_TOP/bootstrap/bin/erl -eval 'ts:install([{xcomp,"<FILE>"}])' -s ts compile_testcases -s init stop + +You should get a lot of printouts as the testcases are compiled. Once done you +should copy the entire `$ERL_TOP/release/tests` folder to the cross host system. + +Then go to the cross host system and setup the erlang installed in (4) or (5) +to be in your `$PATH`. Then go to what previously was +`$ERL_TOP/release/tests/test_server` and issue the following command. + + $ erl -s ts install -s ts run all_tests -s init stop + +The configure should be skipped and all tests should hopefully pass. For more +details about how to use ts run `erl -s ts help -s init stop` + Currently Used Configuration Variables -------------------------------------- @@ -430,6 +462,10 @@ When a variable has been set, no warning will be issued. `configure` will fail unless this variable is set. Since no default value is used, `configure` will try to figure this out automatically. +* `erl_xcomp_double_middle` - `yes|no`. Defaults to `no`. + If `yes`, the target system must have doubles in "middle-endian" format. If + `no`, it has "regular" endianness. + * `erl_xcomp_clock_gettime_cpu_time` - `yes|no`. Defaults to `no`. If `yes`, the target system must have a working `clock_gettime()` implementation that can be used for retrieving process CPU time. diff --git a/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf b/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf index f9fff0fa8e..287cb787d9 100644 --- a/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf +++ b/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf @@ -187,6 +187,15 @@ erl_xcomp_sysroot="$TILERA_ROOT/tile" # value is used, `configure' will try to figure this out automatically. #erl_xcomp_bigendian= +# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the +# target system must have doubles in "middle-endian" format. If +# `no`, it has "regular" endianness. This can often be automatically +# detected, but not always. If not automatically detected, +# `configure` will fail unless this variable is set. Since no +# default value is used, `configure` will try to figure this out +# automatically. +#erl_xcomp_double_middle_endian + # * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes', # the target system must have a working `clock_gettime()' implementation # that can be used for retrieving process CPU time. diff --git a/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf b/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf index be22d498d6..e86043f5c8 100644 --- a/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf +++ b/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf @@ -190,6 +190,15 @@ erl_xcomp_sysroot="$ATMEL_SYSROOT_AVR32" # value is used, `configure' will try to figure this out automatically. #erl_xcomp_bigendian= +# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the +# target system must have doubles in "middle-endian" format. If +# `no`, it has "regular" endianness. This can often be automatically +# detected, but not always. If not automatically detected, +# `configure` will fail unless this variable is set. Since no +# default value is used, `configure` will try to figure this out +# automatically. +#erl_xcomp_double_middle_endian + # * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes', # the target system must have a working `clock_gettime()' implementation # that can be used for retrieving process CPU time. diff --git a/xcomp/erl-xcomp-mips-linux.conf b/xcomp/erl-xcomp-mips-linux.conf index 3ac057bb4c..2195b7d6e9 100644 --- a/xcomp/erl-xcomp-mips-linux.conf +++ b/xcomp/erl-xcomp-mips-linux.conf @@ -187,6 +187,15 @@ CFLAGS=-Os # value is used, `configure' will try to figure this out automatically. #erl_xcomp_bigendian= +# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the +# target system must have doubles in "middle-endian" format. If +# `no`, it has "regular" endianness. This can often be automatically +# detected, but not always. If not automatically detected, +# `configure` will fail unless this variable is set. Since no +# default value is used, `configure` will try to figure this out +# automatically. +#erl_xcomp_double_middle_endian + # * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes', # the target system must have a working `clock_gettime()' implementation # that can be used for retrieving process CPU time. diff --git a/xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf b/xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf new file mode 100644 index 0000000000..735c986486 --- /dev/null +++ b/xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf @@ -0,0 +1,258 @@ +## -*-shell-script-*- +## +## %CopyrightBegin% +## +## Copyright Ericsson AB 2009-2010. 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% +## +## File: erl-xcomp.conf.template +## Author: Rickard Green +## +## ----------------------------------------------------------------------------- +## When cross compiling Erlang/OTP using `otp_build', copy this file and set +## the variables needed below. Then pass the path to the copy of this file as +## an argument to `otp_build' in the configure stage: +## `otp_build configure --xcomp-conf=<FILE>' +## ----------------------------------------------------------------------------- + +## Note that you cannot define arbitrary variables in a cross compilation +## configuration file. Only the ones listed below will be guaranteed to be +## visible throughout the whole execution of all `configure' scripts. Other +## variables needs to be defined as arguments to `configure' or exported in +## the environment. + +## -- Variables for `otp_build' Only ------------------------------------------- + +## Variables in this section are only used, when configuring Erlang/OTP for +## cross compilation using `$ERL_TOP/otp_build configure'. + +## *NOTE*! These variables currently have *no* effect if you configure using +## the `configure' script directly. + +# * `erl_xcomp_build' - The build system used. This value will be passed as +# `--build=$erl_xcomp_build' argument to the `configure' script. It does +# not have to be a full `CPU-VENDOR-OS' triplet, but can be. The full +# `CPU-VENDOR-OS' triplet will be created by +# `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_build'. If set to `guess', +# the build system will be guessed using +# `$ERL_TOP/erts/autoconf/config.guess'. +erl_xcomp_build=guess + +# * `erl_xcomp_host' - Cross host/target system to build for. This value will +# be passed as `--host=$erl_xcomp_host' argument to the `configure' script. +# It does not have to be a full `CPU-VENDOR-OS' triplet, but can be. The +# full `CPU-VENDOR-OS' triplet will be created by +# `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_host'. +erl_xcomp_host=powerpc-dso-linux-gnu + +# * `erl_xcomp_configure_flags' - Extra configure flags to pass to the +# `configure' script. +erl_xcomp_configure_flags=--without-termcap + +## -- Cross Compiler and Other Tools ------------------------------------------- + +## If the cross compilation tools are prefixed by `<HOST>-' you probably do +## not need to set these variables (where `<HOST>' is what has been passed as +## `--host=<HOST>' argument to `configure'). + +## All variables in this section can also be used when native compiling. + +# * `CC' - C compiler. +CC=powerpc-dso-linux-gnu-gcc + +# * `CFLAGS' - C compiler flags. +#CFLAGS= + +# * `STATIC_CFLAGS' - Static C compiler flags. +#STATIC_CFLAGS= + +# * `CFLAG_RUNTIME_LIBRARY_PATH' - This flag should set runtime library +# search path for the shared libraries. Note that this actually is a +# linker flag, but it needs to be passed via the compiler. +#CFLAG_RUNTIME_LIBRARY_PATH= + +# * `CPP' - C pre-processor. +#CPP= + +# * `CPPFLAGS' - C pre-processor flags. +#CPPFLAGS= + +# * `CXX' - C++ compiler. +CXX=powerpc-dso-linux-gnu-g++ + +# * `CXXFLAGS' - C++ compiler flags. +#CXXFLAGS= + +# * `LD' - Linker. +LD=powerpc-dso-linux-gnu-ld + +# * `LDFLAGS' - Linker flags. +#LDFLAGS= + +# * `LIBS' - Libraries. +#LIBS= + +## -- *D*ynamic *E*rlang *D*river Linking -- + +## *NOTE*! Either set all or none of the `DED_LD*' variables. + +# * `DED_LD' - Linker for Dynamically loaded Erlang Drivers. +#DED_LD= + +# * `DED_LDFLAGS' - Linker flags to use with `DED_LD'. +#DED_LDFLAGS= + +# * `DED_LD_FLAG_RUNTIME_LIBRARY_PATH' - This flag should set runtime library +# search path for shared libraries when linking with `DED_LD'. +#DED_LD_FLAG_RUNTIME_LIBRARY_PATH= + +## -- Large File Support -- + +## *NOTE*! Either set all or none of the `LFS_*' variables. + +# * `LFS_CFLAGS' - Large file support C compiler flags. +#LFS_CFLAGS= + +# * `LFS_LDFLAGS' - Large file support linker flags. +#LFS_LDFLAGS= + +# * `LFS_LIBS' - Large file support libraries. +#LFS_LIBS= + +## -- Other Tools -- + +# * `RANLIB' - `ranlib' archive index tool. +RANLIB=powerpc-dso-linux-gnu-ranlib + +# * `AR' - `ar' archiving tool. +AR=powerpc-dso-linux-gnu-ar + +# * `GETCONF' - `getconf' system configuration inspection tool. `getconf' is +# currently used for finding out large file support flags to use, and +# on Linux systems for finding out if we have an NPTL thread library or +# not. +#GETCONF= + +## -- Cross System Root Locations ---------------------------------------------- + +# * `erl_xcomp_sysroot' - The absolute path to the system root of the cross +# compilation environment. Currently, the `crypto', `odbc', `ssh' and +# `ssl' applications need the system root. These applications will be +# skipped if the system root has not been set. The system root might be +# needed for other things too. If this is the case and the system root +# has not been set, `configure' will fail and request you to set it. +erl_xcomp_sysroot="$DSO_SYSROOT_POWERPC" + +# * `erl_xcomp_isysroot' - The absolute path to the system root for includes +# of the cross compilation environment. If not set, this value defaults +# to `$erl_xcomp_sysroot', i.e., only set this value if the include system +# root path is not the same as the system root path. +#erl_xcomp_isysroot= + +## -- Optional Feature, and Bug Tests ------------------------------------------ + +## These tests cannot (always) be done automatically when cross compiling. You +## usually do not need to set these variables. Only set these if you really +## know what you are doing. + +## Note that some of these values will override results of tests performed +## by `configure', and some will not be used until `configure' is sure that +## it cannot figure the result out. + +## The `configure' script will issue a warning when a default value is used. +## When a variable has been set, no warning will be issued. + +# * `erl_xcomp_after_morecore_hook' - `yes|no'. Defaults to `no'. If `yes', +# the target system must have a working `__after_morecore_hook' that can be +# used for tracking used `malloc()' implementations core memory usage. +# This is currently only used by unsupported features. +erl_xcomp_after_morecore_hook=yes + +# * `erl_xcomp_bigendian' - `yes|no'. No default. If `yes', the target system +# must be big endian. If `no', little endian. This can often be +# automatically detected, but not always. If not automatically detected, +# `configure' will fail unless this variable is set. Since no default +# value is used, `configure' will try to figure this out automatically. +#erl_xcomp_bigendian= + +# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes', +# the target system must have a working `clock_gettime()' implementation +# that can be used for retrieving process CPU time. +#erl_xcomp_clock_gettime_cpu_time= + +# * `erl_xcomp_getaddrinfo' - `yes|no'. Defaults to `no'. If `yes', the target +# system must have a working `getaddrinfo()' implementation that can +# handle both IPv4 and IPv6. +erl_xcomp_getaddrinfo=yes + +# * `erl_xcomp_gethrvtime_procfs_ioctl' - `yes|no'. Defaults to `no'. If `yes', +# the target system must have a working `gethrvtime()' implementation and +# is used with procfs `ioctl()'. +#erl_xcomp_gethrvtime_procfs_ioctl= + +# * `erl_xcomp_dlsym_brk_wrappers' - `yes|no'. Defaults to `no'. If `yes', the +# target system must have a working `dlsym(RTLD_NEXT, <S>)' implementation +# that can be used on `brk' and `sbrk' symbols used by the `malloc()' +# implementation in use, and by this track the `malloc()' implementations +# core memory usage. This is currently only used by unsupported features. +#erl_xcomp_dlsym_brk_wrappers= + +# * `erl_xcomp_kqueue' - `yes|no'. Defaults to `no'. If `yes', the target +# system must have a working `kqueue()' implementation that returns a file +# descriptor which can be used by `poll()' and/or `select()'. If `no' and +# the target system has not got `epoll()' or `/dev/poll', the kernel-poll +# feature will be disabled. +#erl_xcomp_kqueue= + +# * `erl_xcomp_linux_clock_gettime_correction' - `yes|no'. Defaults to `yes' on +# Linux; otherwise, `no'. If `yes', `clock_gettime(CLOCK_MONOTONIC, _)' on +# the target system must work. This variable is recommended to be set to +# `no' on Linux systems with kernel versions less than 2.6. +#erl_xcomp_linux_clock_gettime_correction= + +# * `erl_xcomp_linux_nptl' - `yes|no'. Defaults to `yes' on Linux; otherwise, +# `no'. If `yes', the target system must have NPTL (Native POSIX Thread +# Library). Older Linux systems have LinuxThreads instead of NPTL (Linux +# kernel versions typically less than 2.6). +#erl_xcomp_linux_nptl= + +# * `erl_xcomp_linux_usable_sigaltstack' - `yes|no'. Defaults to `yes' on Linux; +# otherwise, `no'. If `yes', `sigaltstack()' must be usable on the target +# system. `sigaltstack()' on Linux kernel versions less than 2.4 are +# broken. +#erl_xcomp_linux_usable_sigaltstack= + +# * `erl_xcomp_linux_usable_sigusrx' - `yes|no'. Defaults to `yes'. If `yes', +# the `SIGUSR1' and `SIGUSR2' signals must be usable by the ERTS. Old +# LinuxThreads thread libraries (Linux kernel versions typically less than +# 2.2) used these signals and made them unusable by the ERTS. +#erl_xcomp_linux_usable_sigusrx= + +# * `erl_xcomp_poll' - `yes|no'. Defaults to `no' on Darwin/MacOSX; otherwise, +# `yes'. If `yes', the target system must have a working `poll()' +# implementation that also can handle devices. If `no', `select()' will be +# used instead of `poll()'. +#erl_xcomp_poll= + +# * `erl_xcomp_putenv_copy' - `yes|no'. Defaults to `no'. If `yes', the target +# system must have a `putenv()' implementation that stores a copy of the +# key/value pair. +#erl_xcomp_putenv_copy= + +# * `erl_xcomp_reliable_fpe' - `yes|no'. Defaults to `no'. If `yes', the target +# system must have reliable floating point exceptions. +#erl_xcomp_reliable_fpe= + +## ----------------------------------------------------------------------------- diff --git a/xcomp/erl-xcomp-vars.sh b/xcomp/erl-xcomp-vars.sh index d9972eb3db..168b25262a 100644 --- a/xcomp/erl-xcomp-vars.sh +++ b/xcomp/erl-xcomp-vars.sh @@ -26,4 +26,4 @@ # and precious variables in $ERL_TOP/erts/aclocal.m4. # -erl_xcomp_vars="erl_xcomp_sysroot erl_xcomp_isysroot erl_xcomp_bigendian erl_xcomp_linux_clock_gettime_correction erl_xcomp_linux_nptl erl_xcomp_linux_usable_sigusrx erl_xcomp_linux_usable_sigaltstack erl_xcomp_poll erl_xcomp_kqueue erl_xcomp_putenv_copy erl_xcomp_reliable_fpe erl_xcomp_getaddrinfo erl_xcomp_gethrvtime_procfs_ioctl erl_xcomp_clock_gettime_cpu_time erl_xcomp_after_morecore_hook erl_xcomp_dlsym_brk_wrappers" +erl_xcomp_vars="erl_xcomp_sysroot erl_xcomp_isysroot erl_xcomp_bigendian erl_xcomp_double_middle_endian erl_xcomp_linux_clock_gettime_correction erl_xcomp_linux_nptl erl_xcomp_linux_usable_sigusrx erl_xcomp_linux_usable_sigaltstack erl_xcomp_poll erl_xcomp_kqueue erl_xcomp_putenv_copy erl_xcomp_reliable_fpe erl_xcomp_getaddrinfo erl_xcomp_gethrvtime_procfs_ioctl erl_xcomp_clock_gettime_cpu_time erl_xcomp_after_morecore_hook erl_xcomp_dlsym_brk_wrappers" diff --git a/xcomp/erl-xcomp-vxworks_ppc32.conf b/xcomp/erl-xcomp-vxworks_ppc32.conf index ed8305ea93..bea4b3e374 100644 --- a/xcomp/erl-xcomp-vxworks_ppc32.conf +++ b/xcomp/erl-xcomp-vxworks_ppc32.conf @@ -187,6 +187,15 @@ AR="$WIND_BASE/gnu/3.4.4-vxworks-6.3/$WIND_HOST_TYPE/bin/arppc" # value is used, `configure' will try to figure this out automatically. erl_xcomp_bigendian=yes +# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the +# target system must have doubles in "middle-endian" format. If +# `no`, it has "regular" endianness. This can often be automatically +# detected, but not always. If not automatically detected, +# `configure` will fail unless this variable is set. Since no +# default value is used, `configure` will try to figure this out +# automatically. +#erl_xcomp_double_middle_endian + # * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes', # the target system must have a working `clock_gettime()' implementation # that can be used for retrieving process CPU time. diff --git a/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf b/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf index f645ddbd96..de9118ae30 100644 --- a/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf +++ b/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf @@ -187,6 +187,15 @@ erl_xcomp_after_morecore_hook=yes # value is used, `configure' will try to figure this out automatically. #erl_xcomp_bigendian= +# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the +# target system must have doubles in "middle-endian" format. If +# `no`, it has "regular" endianness. This can often be automatically +# detected, but not always. If not automatically detected, +# `configure` will fail unless this variable is set. Since no +# default value is used, `configure` will try to figure this out +# automatically. +#erl_xcomp_double_middle_endian + # * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes', # the target system must have a working `clock_gettime()' implementation # that can be used for retrieving process CPU time. diff --git a/xcomp/erl-xcomp.conf.template b/xcomp/erl-xcomp.conf.template index eb331ffe92..c215f0fb13 100644 --- a/xcomp/erl-xcomp.conf.template +++ b/xcomp/erl-xcomp.conf.template @@ -187,6 +187,15 @@ # value is used, `configure' will try to figure this out automatically. #erl_xcomp_bigendian= +# * `erl_xcomp_double_middle` - `yes|no`. No default. If `yes`, the +# target system must have doubles in "middle-endian" format. If +# `no`, it has "regular" endianness. This can often be automatically +# detected, but not always. If not automatically detected, +# `configure` will fail unless this variable is set. Since no +# default value is used, `configure` will try to figure this out +# automatically. +#erl_xcomp_double_middle_endian + # * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes', # the target system must have a working `clock_gettime()' implementation # that can be used for retrieving process CPU time. |