diff options
256 files changed, 14047 insertions, 3844 deletions
diff --git a/INSTALL-WIN32.md b/INSTALL-WIN32.md index 1e5e1f45fa..93b7e3a1e1 100644 --- a/INSTALL-WIN32.md +++ b/INSTALL-WIN32.md @@ -600,32 +600,36 @@ OpenSSL. Well' here's the list: Windows command prompt (it should be on the same drive as where you are going to install it if everything is to work smothly). - `C:\> cd <some dir>` + C:\> cd <some dir> Add ActiveState (or some other windows perl, not cygwins) to your PATH: - `C:\...\> set PATH=C:\Perl\bin;%PATH%` + C:\...\> set PATH=C:\Perl\bin;%PATH% + + Or if you installed the 64bit perl: + + C:\...\> set PATH=C:\Perl64\bin;%PATH% Configure OpenSSL for 32 bit: - `C:\...\> perl Configure VC-WIN32 --prefix=/OpenSSL` + C:\...\> perl Configure VC-WIN32 --prefix=/OpenSSL Or for 64 bit: - `C:\...\> perl Configure VC-WIN64A --prefix=/OpenSSL-Win64` + C:\...\> perl Configure VC-WIN64A --prefix=/OpenSSL-Win64 Do some setup (for 32 bit): - `C:\...\> ms\do_win32` + C:\...\> ms\do_ms The same for 64 bit: - `C:\...\> ms\do_win64a` + C:\...\> ms\do_win64a Then build static libraries and install: - `C:\...\> nmake -f ms\nt.mak` - `C:\...\> nmake -f ms\nt.mak install` + C:\...\> nmake -f ms\nt.mak + C:\...\> nmake -f ms\nt.mak install That's it - you now have your perfectly consistent static build of openssl. If you want to get rid of any possibly patented @@ -644,19 +648,21 @@ OpenSSL. Well' here's the list: edit: `C:\cygwin\opt\local\pgm\wxMSW-2.8.11\include\wx\msw\setup.h` enable `wxUSE_GLCANVAS`, `wxUSE_POSTSCRIPT` and `wxUSE_GRAPHICS_CONTEXT` - build: From a command prompt with the VC tools available (usually started from a - shortcut installed by the SDK/Visual Studio): + build: From a command prompt with the VC tools available (See the + instructions for OpenSSL build above for help on starting the + proper command prompt in RELEASE mode): - `cd C:\cygwin\opt\local\pgm\wxMSW-2.8.11\build\msw` - `nmake BUILD=release SHARED=0 UNICODE=1 USE_OPENGL=1 USE_GDIPLUS=1 DIR_SUFFIX_CPU= -f makefile.vc` - `cd C:\cygwin\opt\local\pgm\wxMSW-2.8.11\contrib\build\stc` - `nmake BUILD=release SHARED=0 UNICODE=1 USE_OPENGL=1 USE_GDIPLUS=1 DIR_SUFFIX_CPU= -f makefile.vc` + C:\...\> cd C:\cygwin\opt\local\pgm\wxMSW-2.8.11\build\msw + C:\...\> nmake BUILD=release SHARED=0 UNICODE=1 USE_OPENGL=1 USE_GDIPLUS=1 DIR_SUFFIX_CPU= -f makefile.vc + C:\...\> cd C:\cygwin\opt\local\pgm\wxMSW-2.8.11\contrib\build\stc + C:\...\> nmake BUILD=release SHARED=0 UNICODE=1 USE_OPENGL=1 USE_GDIPLUS=1 DIR_SUFFIX_CPU= -f makefile.vc Or - if building a 64bit version: - `cd C:\cygwin\opt\local\pgm\wxMSW-2.8.11\build\msw` - `nmake TARGET_CPU=amd64 BUILD=release SHARED=0 UNICODE=1 USE_OPENGL=1 USE_GDIPLUS=1 DIR_SUFFIX_CPU= -f makefile.vc` - `cd C:\cygwin\opt\local\pgm\wxMSW-2.8.11\contrib\build\stc` - `nmake TARGET_CPU=amd64 BUILD=release SHARED=0 UNICODE=1 USE_OPENGL=1 USE_GDIPLUS=1 DIR_SUFFIX_CPU= -f makefile.vc` + + C:\...\> cd C:\cygwin\opt\local\pgm\wxMSW-2.8.11\build\msw + C:\...\> nmake TARGET_CPU=amd64 BUILD=release SHARED=0 UNICODE=1 USE_OPENGL=1 USE_GDIPLUS=1 DIR_SUFFIX_CPU= -f makefile.vc + C:\...\> cd C:\cygwin\opt\local\pgm\wxMSW-2.8.11\contrib\build\stc + C:\...\> nmake TARGET_CPU=amd64 BUILD=release SHARED=0 UNICODE=1 USE_OPENGL=1 USE_GDIPLUS=1 DIR_SUFFIX_CPU= -f makefile.vc * The Erlang source distribution (from <http://www.erlang.org/download.html>). The same as for Unix platforms. Preferably use tar from within Cygwin to diff --git a/README.dtrace.md b/README.dtrace.md new file mode 100644 index 0000000000..5bc042f9fc --- /dev/null +++ b/README.dtrace.md @@ -0,0 +1,393 @@ +DTrace and Erlang/OTP +===================== + +History +------- + +The first implementation of DTrace probes for the Erlang virtual +machine was presented at the [2008 Erlang User Conference] [4]. That +work, based on the Erlang/OTP R12 release, was discontinued due to +what appears to be miscommunication with the original developers. + +Several users have created Erlang port drivers, linked-in drivers, or +NIFs that allow Erlang code to try to activate a probe, +e.g. `foo_module:dtrace_probe("message goes here!")`. + +Goals +----- + +1. Annotate as much of the Erlang VM as is practical. + * The initial goal is to trace file I/O operations. +2. Support all platforms that implement DTrace: OS X, Solaris, + and (I hope) FreeBSD and NetBSD. +3. To the extent that it's practical, support SystemTap on Linux + via DTrace provider compatibility. +4. Allow Erlang code to supply annotations. + +Supported platforms +------------------- + +* OS X 10.6.x / Snow Leopard. It should also work for 10.7 / Lion, + but I haven't personally tested it. +* Solaris 10. I have done limited testing on Solaris 11 and + OpenIndiana release 151a, and both appear to work. +* FreeBSD 9.0, though please see the "FreeBSD 9.0 Release Notes" + section below! +* Linux via SystemTap compatibility. Please see the file + `README.systemtap.md` for more details. + +Just add the `--with-dynamic-trace=dtrace` option to your command when you +run the `configure` script. If you are using systemtap, the configure option +is `--with-dynamic-trace=systemtap` + +Status +------ + +As of R15B01, the dynamic trace code is included in the main OTP distribution, +although it's considered experimental. The main development of the dtrace code +still happens outside of Ericsson, but there is no need to fetch a patched +version of OTP to get the basic funtionality. + +Implementation summary +---------------------- + +So far, most effort has been focused on the `efile_drv.c` code, +which implements most file I/O on behalf of the Erlang virtual +machine. This driver also presents a big challenge: its use of an I/O +worker pool (enabled by using the `erl +A 8` flag, for example) makes +it much more difficult to trace I/O activity because each of the +following may be executed in a different Pthread: + +* I/O initiation (Erlang code) +* I/O proxy process handling, e.g. read/write when file is not opened + in `raw` mode, operations executed by the code & file server processes. + (Erlang code) +* `efile_drv` command setup (C code) +* `efile_drv` command execution (C code) +* `efile_drv` status return (C code) + +**TODO: keep this description up-to-date.** + +Example output from `lib/dtrace/examples/efile_drv.d` while executing +`file:rename("old-name", "new-name")`: + + efile_drv enter tag={3,84} user tag some-user-tag | RENAME (12) | args: old-name new-name , 0 0 (port #Port<0.59>) + async I/O worker tag={3,83} | RENAME (12) | efile_drv-int_entry + async I/O worker tag={3,83} | RENAME (12) | efile_drv-int_return + efile_drv return tag={3,83} user tag | RENAME (12) | errno 2 + +... where the following key can help decipher the output: + +* `{3,83}` is the Erlang scheduler thread number (3) and operation + counter number (83) assigned to this I/O operation. Together, + these two numbers form a unique ID for the I/O operation. +* `12` is the command number for the rename operation. See the + definition for `FILE_RENAME` in the source code file `efile_drv.c` + or the `BEGIN` section of the D script `lib/dtrace/examples/efile_drv.d`. +* `old-name` and `new-name` are the two string arguments for the + source and destination of the `rename(2)` system call. + The two integer arguments are unused; the simple formatting code + prints the arguments anyway, 0 and 0. +* The worker pool code was called on behalf of Erlang port `#Port<0.59>`. +* The system call failed with a POSIX errno value of 2: `ENOENT`, + because the path `old-name` does not exist. +* The `efile_drv-int_entry` and `efile_drv_int_return` probes are + provided in case the user is + interested in measuring only the latency of code executed by + `efile_drv` asynchronous functions by I/O worker pool threads + and the OS system call that they encapsulate. + +So, where does the `some-user-tag` string come from? + +At the moment, the user tag comes from code like the following: + + put(dtrace_utag, "some-user-tag"), + file:rename("old-name", "new-name"). + +This method of tagging I/O at the Erlang level is subject to change. + +Example DTrace probe specification +---------------------------------- + + /** + * Fired when a message is sent from one local process to another. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * @param sender the PID (string form) of the sender + * @param receiver the PID (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__send(char *sender, char *receiver, uint32_t size, + int token_label, int token_previous, int token_current); + + /** + * Fired when a message is sent from a local process to a remote process. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * @param sender the PID (string form) of the sender + * @param node_name the Erlang node name (string form) of the receiver + * @param receiver the PID/name (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__send__remote(char *sender, char *node_name, char *receiver, + uint32_t size, + int token_label, int token_previous, int token_current); + + /** + * Fired when a message is queued to a local process. This probe + * will not fire if the sender's pid == receiver's pid. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * @param receiver the PID (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param queue_len length of the queue of the receiving process + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__queued(char *receiver, uint32_t size, uint32_t queue_len, + int token_label, int token_previous, int token_current); + + /** + * Fired when a message is 'receive'd by a local process and removed + * from its mailbox. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * @param receiver the PID (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param queue_len length of the queue of the receiving process + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__receive(char *receiver, uint32_t size, uint32_t queue_len, + int token_label, int token_previous, int token_current); + + /* ... */ + + /* Async driver pool */ + + /** + * Show the post-add length of the async driver thread pool member's queue. + * + * NOTE: The port name is not available: additional lock(s) must + * be acquired in order to get the port name safely in an SMP + * environment. The same is true for the aio__pool_get probe. + * + * @param port the Port (string form) + * @param new queue length + */ + probe aio_pool__add(char *, int); + + /** + * Show the post-get length of the async driver thread pool member's queue. + * + * @param port the Port (string form) + * @param new queue length + */ + probe aio_pool__get(char *, int); + + /* Probes for efile_drv.c */ + + /** + * Entry into the efile_drv.c file I/O driver + * + * For a list of command numbers used by this driver, see the section + * "Guide to probe arguments" in ../../../README.md. That section + * also contains explanation of the various integer and string + * arguments that may be present when any particular probe fires. + * + * TODO: Adding the port string, args[10], is a pain. Making that + * port string available to all the other efile_drv.c probes + * will be more pain. Is the pain worth it? If yes, then + * add them everywhere else and grit our teeth. If no, then + * rip it out. + * + * @param thread-id number of the scheduler Pthread arg0 + * @param tag number: {thread-id, tag} uniquely names a driver operation + * @param user-tag string arg2 + * @param command number arg3 + * @param string argument 1 arg4 + * @param string argument 2 arg5 + * @param integer argument 1 arg6 + * @param integer argument 2 arg7 + * @param integer argument 3 arg8 + * @param integer argument 4 arg9 + * @param port the port ID of the busy port args[10] + */ + probe efile_drv__entry(int, int, char *, int, char *, char *, + int64_t, int64_t, int64_t, int64_t, char *); + + /** + * Entry into the driver's internal work function. Computation here + * is performed by a async worker pool Pthread. + * + * @param thread-id number + * @param tag number + * @param command number + */ + probe efile_drv__int_entry(int, int, int); + + /** + * Return from the driver's internal work function. + * + * @param thread-id number + * @param tag number + * @param command number + */ + probe efile_drv__int_return(int, int, int); + + /** + * Return from the efile_drv.c file I/O driver + * + * @param thread-id number arg0 + * @param tag number arg1 + * @param user-tag string arg2 + * @param command number arg3 + * @param Success? 1 is success, 0 is failure arg4 + * @param If failure, the errno of the error. arg5 + */ + probe efile_drv__return(int, int, char *, int, int, int); + +Guide to efile_drv.c probe arguments +------------------------------------ + + /* Driver op code: used by efile_drv-entry arg3 */ + /* used by efile_drv-int_entry arg3 */ + /* used by efile_drv-int_return arg3 */ + /* used by efile_drv-return arg3 */ + + #define FILE_OPEN 1 (probe arg3) + probe arg6 = C driver dt_i1 = flags; + probe arg4 = C driver dt_s1 = path; + + #define FILE_READ 2 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + probe arg7 = C driver dt_i2 = flags; + probe arg8 = C driver dt_i3 = size; + + #define FILE_LSEEK 3 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + probe arg7 = C driver dt_i2 = offset; + probe arg8 = C driver dt_i3 = origin; + + #define FILE_WRITE 4 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + probe arg7 = C driver dt_i2 = flags; + probe arg8 = C driver dt_i3 = size; + + #define FILE_FSTAT 5 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + + #define FILE_PWD 6 (probe arg3) + none + + #define FILE_READDIR 7 (probe arg3) + probe arg4 = C driver dt_s1 = path; + + #define FILE_CHDIR 8 (probe arg3) + probe arg4 = C driver dt_s1 = path; + + #define FILE_FSYNC 9 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + + #define FILE_MKDIR 10 (probe arg3) + probe arg4 = C driver dt_s1 = path; + + #define FILE_DELETE 11 (probe arg3) + probe arg4 = C driver dt_s1 = path; + + #define FILE_RENAME 12 (probe arg3) + probe arg4 = C driver dt_s1 = old_name; + probe arg5 = C driver dt_s2 = new_name; + + #define FILE_RMDIR 13 (probe arg3) + probe arg4 = C driver dt_s1 = path; + + #define FILE_TRUNCATE 14 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + probe arg7 = C driver dt_i2 = flags; + + #define FILE_READ_FILE 15 (probe arg3) + probe arg4 = C driver dt_s1 = path; + + #define FILE_WRITE_INFO 16 (probe arg3) + probe arg6 = C driver dt_i1 = mode; + probe arg7 = C driver dt_i2 = uid; + probe arg8 = C driver dt_i3 = gid; + + #define FILE_LSTAT 19 (probe arg3) + probe arg4 = C driver dt_s1 = path; + + #define FILE_READLINK 20 (probe arg3) + probe arg4 = C driver dt_s1 = path; + + #define FILE_LINK 21 (probe arg3) + probe arg4 = C driver dt_s1 = existing_path; + probe arg5 = C driver dt_s2 = new_path; + + #define FILE_SYMLINK 22 (probe arg3) + probe arg4 = C driver dt_s1 = existing_path; + probe arg5 = C driver dt_s2 = new_path; + + #define FILE_CLOSE 23 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + probe arg7 = C driver dt_i2 = flags; + + #define FILE_PWRITEV 24 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + probe arg7 = C driver dt_i2 = flags; + probe arg8 = C driver dt_i3 = size; + + #define FILE_PREADV 25 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + probe arg7 = C driver dt_i2 = flags; + probe arg8 = C driver dt_i3 = size; + + #define FILE_SETOPT 26 (probe arg3) + probe arg6 = C driver dt_i1 = opt_name; + probe arg7 = C driver dt_i2 = opt_specific_value; + + #define FILE_IPREAD 27 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + probe arg7 = C driver dt_i2 = flags; + probe arg8 = C driver dt_i3 = offsets[0]; + probe arg9 = C driver dt_i4 = size; + + #define FILE_ALTNAME 28 (probe arg3) + probe arg4 = C driver dt_s1 = path; + + #define FILE_READ_LINE 29 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + probe arg7 = C driver dt_i2 = flags; + probe arg8 = C driver dt_i3 = read_offset; + probe arg9 = C driver dt_i4 = read_ahead; + + #define FILE_FDATASYNC 30 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + + #define FILE_FADVISE 31 (probe arg3) + probe arg6 = C driver dt_i1 = fd; + probe arg7 = C driver dt_i2 = offset; + probe arg8 = C driver dt_i3 = length; + probe arg9 = C driver dt_i4 = advise_type; + + [1]: http://www.erlang.org/euc/08/ diff --git a/README.systemtap.md b/README.systemtap.md new file mode 100644 index 0000000000..c190bcc893 --- /dev/null +++ b/README.systemtap.md @@ -0,0 +1,72 @@ +SystemTap and Erlang/OTP +======================== + +Introduction +------------ + +SystemTap is DTrace for Linux. In fact Erlang's SystemTap support +is build using SystemTap's DTrace compatibility's layer. For an +introduction to Erlang DTrace support read README.dtrace.md. + +Requisites +---------- + +* Linux Kernel with UTRACE support + + check for UTRACE support in your current kernel: + + # grep CONFIG_UTRACE /boot/config-`uname -r` + CONFIG_UTRACE=y + + Fedora 16 is known to contain UTRACE, for most other Linux distributions + a custom build kernel will be required. + Check Fedora's SystemTap documentation for additional required packages + (e.g. Kernel Debug Symbols) + +* SystemTap > 1.6 + + A the time of writing this, the latest released version of SystemTap is + version 1.6. Erlang's DTrace support requires a MACRO that was introduced + after that release. So either get a newer release or build SystemTap from + git yourself (see: http://sourceware.org/systemtap/getinvolved.html) + +Building Erlang +--------------- + +Configure and build Erlang with SystemTap support: + + # ./configure --with-dynamic-trace=systemtap + whatever args you need + # make + +Testing +------- + +SystemTap, unlike DTrace, needs to know what binary it is tracing and has to +be able to read that binary before it starts tracing. Your probe script +therefor has to reference the correct beam emulator and stap needs to be able +to find that binary. +The examples are written for "beam", but other versions such as "beam.smp" or +"beam.debug.smp" might exist (depending on your configuration). Make sure you +either specify the full the path of the binary in the probe or your "beam" +binary is in the search path. + +All available probes can be listed like this: + + # stap -L 'process("beam").mark("*")' + +or: + + # PATH=/path/to/beam:$PATH stap -L 'process("beam").mark("*")' + + +Probes in the dtrace.so NIF library like this: + + # PATH=/path/to/dtrace/priv/lib:$PATH stap -L 'process("dtrace.so").mark("*")' + +Running SystemTap scripts +------------------------- + +Adjust the process("beam") reference to your beam version and attach the script +to a running "beam" instance: + + # stap /path/to/probe/script/port1.systemtap -x <pid of beam> diff --git a/configure.in b/configure.in index 2c3ee1cb57..c7e6e9ecf9 100644 --- a/configure.in +++ b/configure.in @@ -225,6 +225,14 @@ AC_ARG_ENABLE(native-libs, AS_HELP_STRING([--enable-native-libs], [compile Erlang libraries to native code])) +AC_ARG_WITH(dynamic-trace, +AS_HELP_STRING([--with-dynamic-trace={dtrace|systemtap}], + [specify use of dynamic trace framework, dtrace or systemtap]) +AS_HELP_STRING([--without-dynamic-trace], + [don't enable any dynamic tracing (default)])) +AC_ARG_ENABLE(vm-probes, +AS_HELP_STRING([--enable-vm-probes], + [add dynamic trace probes to the Beam VM (only possible if --with-dynamic-trace is enabled, and then default)])) AC_ARG_WITH(javac, AS_HELP_STRING([--with-javac=JAVAC], [specify Java compiler to use]) AS_HELP_STRING([--with-javac], [use a Java compiler if found (default)]) diff --git a/erts/configure.in b/erts/configure.in index b801994e14..cb1b00b8b1 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -276,6 +276,61 @@ else [Define to enable hrvtime() on Linux systems with perfctr extension]) fi + +AC_ARG_WITH(dynamic-trace, +AS_HELP_STRING([--with-dynamic-trace={dtrace|systemtap}], + [specify use of dynamic trace framework, dtrace or systemtap]) +AS_HELP_STRING([--without-dynamic-trace], + [don't enable any dynamic tracing (default)])) + +if test X"$with_dynamic_trace" = X""; then + with_dynamic_trace=no +fi + +case "$with_dynamic_trace" in + no) DYNAMIC_TRACE_FRAMEWORK=;; + dtrace) + AC_DEFINE(USE_DTRACE,[1], + [Define if you want to use dtrace for dynamic tracing]) + DYNAMIC_TRACE_FRAMEWORK=dtrace;; + systemtap) + AC_DEFINE(USE_SYSTEMTAP,[1], + [Define if you want to use systemtap for dynamic tracing]) + DYNAMIC_TRACE_FRAMEWORK=systemtap;; + *) + AC_MSG_ERROR(Unknown dynamic tracing framework specified with --with-dynamic-trace!);; +esac + +if test X"$DYNAMIC_TRACE_FRAMEWORK" != X""; then + AC_DEFINE(USE_DYNAMIC_TRACE,[1], + [Define if you want to use dynamic tracing]) +fi + +AC_ARG_ENABLE(vm-probes, +AS_HELP_STRING([--enable-vm-probes], + [add dynamic trace probes to the Beam VM (only possible if --with-dynamic-trace is enabled, and then default)]), + [ case "$enableval" in + no) use_vm_probes=no ;; + *) + if test X"$DYNAMIC_TRACE_FRAMEWORK" != X""; then + use_vm_probes=yes ; + else + AC_MSG_ERROR(Can not enable VM probes without any dynamic tracing framework!); + fi;; + esac ], if test X"$DYNAMIC_TRACE_FRAMEWORK" != X""; then + use_vm_probes=yes ; + else + use_vm_probes=no + fi) + +AC_SUBST(USE_VM_PROBES) +if test X"$use_vm_probes" = X"yes"; then + USE_VM_PROBES=yes + AC_DEFINE(USE_VM_PROBES,[1], + [Define to enable VM dynamic trace probes]) +fi + + AC_ARG_ENABLE(clock-gettime, AS_HELP_STRING([--enable-clock-gettime], [use clock-gettime for time correction]), @@ -3546,6 +3601,74 @@ dnl LM_FIND_EMU_CC dnl +dnl DTrace +dnl +case $DYNAMIC_TRACE_FRAMEWORK in + dtrace|systemtap) + AC_CHECK_TOOL(DTRACE, dtrace, none) + test "$DTRACE" = "none" && AC_MSG_ERROR([No dtrace utility found.]); + enable_dtrace_test=yes;; + *) enable_dtrace_test=no;; +esac + +AC_SUBST(DTRACE) + +AC_SUBST(DTRACE_CPP) +AC_SUBST(DTRACE_ENABLED) +AC_SUBST(DTRACE_ENABLED_2STEP) +DTRACE_CPP=-C +DTRACE_ENABLED= +DTRACE_ENABLED_2STEP= +DTRACE_2STEP_TEST=./dtrace-test.o +DTRACE_BITS_FLAG= +case $OPSYS in + freebsd) + if test "$BITS64" = "yes" ; then + DTRACE_BITS_FLAG=-64 + else + DTRACE_BITS_FLAG=-32 + fi + ;; + *) + : # Nothing to do + ;; +esac +if test "$enable_dtrace_test" = "yes" ; then + if test "$DTRACE" = "dtrace" ; then + AC_CHECK_HEADERS(sys/sdt.h) + # The OS X version of dtrace prints a spurious line here. + if ! dtrace -h $DTRACE_CPP -Iemulator/beam -o ./foo-dtrace.h -s emulator/beam/erlang_dtrace.d; then + AC_MSG_ERROR([Could not precompile erlang_dtrace.d: dtrace -h failed]) + fi + rm -f foo-dtrace.h + + $RM -f $DTRACE_2STEP_TEST + if dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d 2> /dev/null && \ + test -f $DTRACE_2STEP_TEST ; then + rm $DTRACE_2STEP_TEST + DTRACE_ENABLED_2STEP=yes + AC_MSG_NOTICE([dtrace precompilation for 2-stage DTrace successful]) + else + AC_MSG_NOTICE([dtrace precompilation for 1-stage DTrace successful]) + fi + DTRACE_ENABLED=yes + case $OPSYS in + linux) + : # No extra libs to add to LIBS + ;; + freebsd) + LIBS="$LIBS -lelf" + ;; + *) + LIBS="$LIBS -ldtrace" + ;; + esac + else + AC_MSG_ERROR([Dtrace preprocessing test failed.]) + fi +fi + +dnl dnl SSL, SSH and CRYPTO need the OpenSSL libraries dnl dnl Check flags --with-ssl, --without-ssl --with-ssl=PATH. diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 8c438b0bd7..0963904b83 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -1432,29 +1432,69 @@ true <name>halt()</name> <fsummary>Halt the Erlang runtime system and indicate normal exit to the calling environment</fsummary> <desc> - <p>Halts the Erlang runtime system and indicates normal exit to - the calling environment. Has no return value.</p> + <p>The same as + <seealso marker="#halt/2"><c>halt(0, [])</c></seealso>.</p> <pre> > <input>halt().</input> -os_prompt%</pre> +os_prompt% </pre> </desc> </func> <func> <name>halt(Status)</name> <fsummary>Halt the Erlang runtime system</fsummary> <type> - <v>Status = integer() >= 0 | string()</v> + <v>Status = integer() >= 0 | string() | abort</v> </type> <desc> - <p><c>Status</c> must be a non-negative integer, or a string. - Halts the Erlang runtime system. Has no return value. - If <c>Status</c> is an integer, it is returned as an exit - status of Erlang to the calling environment. - If <c>Status</c> is a string, produces an Erlang crash dump - with <c>String</c> as slogan, and then exits with a non-zero - status code.</p> - <p>Note that on many platforms, only the status codes 0-255 are - supported by the operating system.</p> + <p>The same as + <seealso marker="#halt/2"><c>halt(Status, [])</c></seealso>.</p> + <pre> +> <input>halt(17).</input> +os_prompt% <input>echo $?</input> +17 +os_prompt% </pre> + </desc> + </func> + <func> + <name>halt(Status, Options)</name> + <fsummary>Halt the Erlang runtime system</fsummary> + <type> + <v>Status = integer() >= 0 | string() | abort</v> + <v>Options = [Option]</v> + <v>Option = {flush,boolean()} | term()</v> + </type> + <desc> + <p><c>Status</c> must be a non-negative integer, a string, + or the atom <c>abort</c>. + Halts the Erlang runtime system. Has no return value. + Depending on <c>Status</c>: + </p> + <taglist> + <tag>integer()</tag> + <item>The runtime system exits with the integer value <c>Status</c> + as status code to the calling environment (operating system). + </item> + <tag>string()</tag> + <item>An erlang crash dump is produced with <c>Status</c> as slogan, + and then the runtime system exits with status code <c>1</c>. + </item> + <tag><c>abort</c></tag> + <item> + The runtime system aborts producing a core dump, if that is + enabled in the operating system. + </item> + </taglist> + <p>Note that on many platforms, only the status codes 0-255 are + supported by the operating system. + </p> + <p>For integer <c>Status</c> the Erlang runtime system closes all ports + and allows async threads to finish their operations before exiting. + To exit without such flushing use + <c>Option</c> as <c>{flush,false}</c>. + </p> + <p>For statuses <c>string()</c> and <c>abort</c> the <c>flush</c> + option is ignored and flushing is <em>not</em> done. + </p> </desc> </func> <func> @@ -5704,6 +5744,29 @@ ok used by the runtime system. It will be on the form <seealso marker="erts:erl_driver#version_management">"<major ver>.<minor ver>"</seealso>.</p> </item> + <tag><c>dynamic_trace</c></tag> + <item> + <p>Returns an atom describing the dynamic trace framework + compiled into the virtual machine. It can currently be either + <c>dtrace</c>, <c>systemtap</c> or <c>none</c>. For a + commercial or standard build, this is always <c>none</c>, + the other return values indicate a custom configuration + (e.g. <c>./configure --with-dynamic-trace=dtrace</c>). See + the <seealso marker="runtime_tools:dyntrace">dyntrace + </seealso> manual page and the + <c>README.dtrace</c>/<c>README.systemtap</c> files in the + Erlang source code top directory for more information + about dynamic tracing.</p> + </item> + <tag><c>dynamic_trace_probes</c></tag> + <item> + <p>Returns a <c>boolean()</c> indicating if dynamic trace probes + (either dtrace or systemtap) are built into the + emulator. This can only be <c>true</c> if the virtual + machine was built for dynamic tracing + (i.e. <c>system_info(dynamic_trace)</c> returns + <c>dtrace</c> or <c>systemtap</c>).</p> + </item> <tag><c>elib_malloc</c></tag> <item> <p>This option will be removed in a future release. diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 279844adb2..2efbe2d57e 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -23,6 +23,9 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk ENABLE_ALLOC_TYPE_VARS = @ENABLE_ALLOC_TYPE_VARS@ HIPE_ENABLED=@HIPE_ENABLED@ +DTRACE_ENABLED=@DTRACE_ENABLED@ +DTRACE_ENABLED_2STEP=@DTRACE_ENABLED_2STEP@ +USE_VM_PROBES=@USE_VM_PROBES@ LIBS = @LIBS@ Z_LIB=@Z_LIB@ NO_INLINE_FUNCTIONS=false @@ -483,6 +486,14 @@ GENERATE += $(HIPE_ASM) \ endif endif +ifdef DTRACE_ENABLED +# global.h causes problems by including dtrace-wrapper.h which includes +# the autogenerated erlang_dtrace.h ... so make erlang_dtrace.h very early. +generate: $(TARGET)/erlang_dtrace.h $(GENERATE) +else +generate: $(GENERATE) +endif + ifdef HIPE_ENABLED OPCODE_TABLES += hipe/hipe_ops.tab endif @@ -498,6 +509,7 @@ $(TTF_DIR)/OPCODES-GENERATED: $(OPCODE_TABLES) utils/beam_makeops LANG=C $(PERL) utils/beam_makeops \ -wordsize @EXTERNAL_WORD_SIZE@ \ -outdir $(TTF_DIR) \ + -DUSE_VM_PROBES=$(if $(USE_VM_PROBES),1,0) \ -emulator $(OPCODE_TABLES) && echo $? >$(TTF_DIR)/OPCODES-GENERATED GENERATE += $(TTF_DIR)/OPCODES-GENERATED @@ -590,6 +602,11 @@ $(TTF_DIR)/GENERATED: $(GENERATE) echo $? >$(TTF_DIR)/GENERATED endif +$(TARGET)/erlang_dtrace.h: beam/erlang_dtrace.d + dtrace -h -C -Ibeam -s $< -o ./erlang_dtrace.tmp + sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./erlang_dtrace.tmp > $@ + rm ./erlang_dtrace.tmp + # ---------------------------------------------------------------------- # Pattern rules # @@ -633,7 +650,6 @@ $(OBJDIR)/beam_emu.o: beam/beam_emu.c $(EMU_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@ endif - $(OBJDIR)/%.o: beam/%.c $(CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@ @@ -833,7 +849,18 @@ endif BASE_OBJS = $(RUN_OBJS) $(EMU_OBJS) $(OS_OBJS) $(EXTRA_BASE_OBJS) -OBJS = $(BASE_OBJS) $(DRV_OBJS) +before_DTrace_OBJS = $(BASE_OBJS) $(DRV_OBJS) + +DTRACE_OBJS = +ifdef DTRACE_ENABLED_2STEP +DTRACE_OBJS = $(OBJDIR)/erlang_dtrace.o +$(OBJDIR)/erlang_dtrace.o: $(before_DTrace_OBJS) $(TARGET)/erlang_dtrace.h + dtrace -G -C -Ibeam \ + -s beam/erlang_dtrace.d \ + -o $@ $(before_DTrace_OBJS) +endif + +OBJS = $(before_DTrace_OBJS) $(DTRACE_OBJS) $(INIT_OBJS): $(TTF_DIR)/GENERATED $(OBJS): $(TTF_DIR)/GENERATED diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 7be40976f6..9ce1068b23 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -248,6 +248,7 @@ atom global_heaps_size atom Gt='>' atom grun atom group_leader +atom have_dt_utag atom heap_block_size atom heap_size atom heap_sizes diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index c65b2be106..8b4f067b98 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -41,6 +41,7 @@ #include "hipe_mode_switch.h" #include "hipe_bif1.h" #endif +#include "dtrace-wrapper.h" /* #define HARDDEBUG 1 */ @@ -1050,6 +1051,101 @@ init_emulator(void) # define REG_tmp_arg2 #endif +#ifdef USE_VM_PROBES +# define USE_VM_CALL_PROBES +#endif + +#ifdef USE_VM_CALL_PROBES + +#define DTRACE_LOCAL_CALL(p, m, f, a) \ + if (DTRACE_ENABLED(local_function_entry)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + int depth = STACK_START(p) - STACK_TOP(p); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE3(local_function_entry, process_name, mfa, depth); \ + } + +#define DTRACE_GLOBAL_CALL(p, m, f, a) \ + if (DTRACE_ENABLED(global_function_entry)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + int depth = STACK_START(p) - STACK_TOP(p); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE3(global_function_entry, process_name, mfa, depth); \ + } + +#define DTRACE_RETURN(p, m, f, a) \ + if (DTRACE_ENABLED(function_return)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + int depth = STACK_START(p) - STACK_TOP(p); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE3(function_return, process_name, mfa, depth); \ + } + +#define DTRACE_BIF_ENTRY(p, m, f, a) \ + if (DTRACE_ENABLED(bif_entry)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE2(bif_entry, process_name, mfa); \ + } + +#define DTRACE_BIF_RETURN(p, m, f, a) \ + if (DTRACE_ENABLED(bif_return)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE2(bif_return, process_name, mfa); \ + } + +#define DTRACE_NIF_ENTRY(p, m, f, a) \ + if (DTRACE_ENABLED(nif_entry)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE2(nif_entry, process_name, mfa); \ + } + +#define DTRACE_NIF_RETURN(p, m, f, a) \ + if (DTRACE_ENABLED(nif_return)) { \ + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \ + dtrace_fun_decode(p, m, f, a, \ + process_name, mfa); \ + DTRACE2(nif_return, process_name, mfa); \ + } + +#else /* USE_VM_PROBES */ + +#define DTRACE_LOCAL_CALL(p, m, f, a) do {} while (0) +#define DTRACE_GLOBAL_CALL(p, m, f, a) do {} while (0) +#define DTRACE_RETURN(p, m, f, a) do {} while (0) +#define DTRACE_BIF_ENTRY(p, m, f, a) do {} while (0) +#define DTRACE_BIF_RETURN(p, m, f, a) do {} while (0) +#define DTRACE_NIF_ENTRY(p, m, f, a) do {} while (0) +#define DTRACE_NIF_RETURN(p, m, f, a) do {} while (0) + +#endif /* USE_VM_PROBES */ + +#ifdef USE_VM_PROBES +void +dtrace_drvport_str(ErlDrvPort drvport, char *port_buf) +{ + Port *port = erts_drvport2port(drvport); + + erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>", + port_channel_no(port->id), + port_number(port->id)); +} +#endif /* * process_main() is called twice: * The first call performs some initialisation, including exporting @@ -1221,6 +1317,30 @@ void process_main(void) #endif SWAPIN; ASSERT(VALID_INSTR(next)); + +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_scheduled)) { + DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(fun_buf, DTRACE_TERM_BUF_SIZE); + dtrace_proc_str(c_p, process_buf); + + if (ERTS_PROC_IS_EXITING(c_p)) { + strcpy(fun_buf, "<exiting>"); + } else { + BeamInstr *fptr = find_function_from_pc(c_p->i); + if (fptr) { + dtrace_fun_decode(c_p, (Eterm)fptr[0], + (Eterm)fptr[1], (Uint)fptr[2], + NULL, fun_buf); + } else { + erts_snprintf(fun_buf, sizeof(fun_buf), + "<unknown/%p>", next); + } + } + + DTRACE2(process_scheduled, process_buf, fun_buf); + } +#endif Goto(next); } @@ -1397,6 +1517,7 @@ void process_main(void) /* FALL THROUGH */ OpCase(i_call_only_f): { SET_I((BeamInstr *) Arg(0)); + DTRACE_LOCAL_CALL(c_p, (Eterm)I[-3], (Eterm)I[-2], I[-1]); Dispatch(); } @@ -1408,6 +1529,7 @@ void process_main(void) RESTORE_CP(E); E = ADD_BYTE_OFFSET(E, Arg(1)); SET_I((BeamInstr *) Arg(0)); + DTRACE_LOCAL_CALL(c_p, (Eterm)I[-3], (Eterm)I[-2], I[-1]); Dispatch(); } @@ -1419,6 +1541,7 @@ void process_main(void) OpCase(i_call_f): { SET_CP(c_p, I+2); SET_I((BeamInstr *) Arg(0)); + DTRACE_LOCAL_CALL(c_p, (Eterm)I[-3], (Eterm)I[-2], I[-1]); Dispatch(); } @@ -1435,6 +1558,12 @@ void process_main(void) * is not loaded, it points to code which will invoke the error handler * (see lb_call_error_handler below). */ +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(global_function_entry)) { + BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address); + DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]); + } +#endif Dispatchx(); OpCase(i_move_call_ext_cre): { @@ -1444,6 +1573,12 @@ void process_main(void) /* FALL THROUGH */ OpCase(i_call_ext_e): SET_CP(c_p, I+2); +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(global_function_entry)) { + BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address); + DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]); + } +#endif Dispatchx(); OpCase(i_move_call_ext_only_ecr): { @@ -1451,6 +1586,12 @@ void process_main(void) } /* FALL THROUGH */ OpCase(i_call_ext_only_e): +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(global_function_entry)) { + BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address); + DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]); + } +#endif Dispatchx(); OpCase(init_y): { @@ -1486,7 +1627,16 @@ void process_main(void) OpCase(return): { +#ifdef USE_VM_CALL_PROBES + BeamInstr* fptr; +#endif SET_I(c_p->cp); + +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(function_return) && (fptr = find_function_from_pc(c_p->cp))) { + DTRACE_RETURN(c_p, (Eterm)fptr[0], (Eterm)fptr[1], (Uint)fptr[2]); + } +#endif /* * We must clear the CP to make sure that a stale value do not * create a false module dependcy preventing code upgrading. @@ -1755,6 +1905,7 @@ void process_main(void) * remove it... */ ASSERT(!msgp->data.attached); + /* TODO: Add DTrace probe for this bad message situation? */ UNLINK_MESSAGE(c_p, msgp); free_message(msgp); goto loop_rec__; @@ -1780,24 +1931,88 @@ void process_main(void) save_calls(c_p, &exp_receive); } if (ERL_MESSAGE_TOKEN(msgp) == NIL) { - SEQ_TRACE_TOKEN(c_p) = NIL; +#ifdef USE_VM_PROBES + if (DT_UTAG(c_p) != NIL) { + if (DT_UTAG_FLAGS(c_p) & DT_UTAG_PERMANENT) { + SEQ_TRACE_TOKEN(c_p) = am_have_dt_utag; +#ifdef DTRACE_TAG_HARDDEBUG + if (DT_UTAG_FLAGS(c_p) & DT_UTAG_SPREADING) + erts_fprintf(stderr, + "Dtrace -> (%T) stop spreading " + "tag %T with message %T\r\n", + c_p->id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp)); +#endif + } else { +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) kill tag %T with " + "message %T\r\n", + c_p->id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp)); +#endif + DT_UTAG(c_p) = NIL; + SEQ_TRACE_TOKEN(c_p) = NIL; + } + } else { +#endif + SEQ_TRACE_TOKEN(c_p) = NIL; +#ifdef USE_VM_PROBES + } + DT_UTAG_FLAGS(c_p) &= ~DT_UTAG_SPREADING; +#endif } else if (ERL_MESSAGE_TOKEN(msgp) != am_undefined) { Eterm msg; SEQ_TRACE_TOKEN(c_p) = ERL_MESSAGE_TOKEN(msgp); - ASSERT(is_tuple(SEQ_TRACE_TOKEN(c_p))); - ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5); - ASSERT(is_small(SEQ_TRACE_TOKEN_SERIAL(c_p))); - ASSERT(is_small(SEQ_TRACE_TOKEN_LASTCNT(c_p))); - ASSERT(is_small(SEQ_TRACE_TOKEN_FLAGS(c_p))); - ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p))); - c_p->seq_trace_lastcnt = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p)); - if (c_p->seq_trace_clock < unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p))) { - c_p->seq_trace_clock = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p)); +#ifdef USE_VM_PROBES + if (ERL_MESSAGE_TOKEN(msgp) == am_have_dt_utag) { + if (DT_UTAG(c_p) == NIL) { + DT_UTAG(c_p) = ERL_MESSAGE_DT_UTAG(msgp); + } + DT_UTAG_FLAGS(c_p) |= DT_UTAG_SPREADING; +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) receive tag (%T) " + "with message %T\r\n", + c_p->id, DT_UTAG(c_p), ERL_MESSAGE_TERM(msgp)); +#endif + } else { +#endif + ASSERT(is_tuple(SEQ_TRACE_TOKEN(c_p))); + ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5); + ASSERT(is_small(SEQ_TRACE_TOKEN_SERIAL(c_p))); + ASSERT(is_small(SEQ_TRACE_TOKEN_LASTCNT(c_p))); + ASSERT(is_small(SEQ_TRACE_TOKEN_FLAGS(c_p))); + ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p))); + c_p->seq_trace_lastcnt = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p)); + if (c_p->seq_trace_clock < unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p))) { + c_p->seq_trace_clock = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p)); + } + msg = ERL_MESSAGE_TERM(msgp); + seq_trace_output(SEQ_TRACE_TOKEN(c_p), msg, SEQ_TRACE_RECEIVE, + c_p->id, c_p); +#ifdef USE_VM_PROBES } - msg = ERL_MESSAGE_TERM(msgp); - seq_trace_output(SEQ_TRACE_TOKEN(c_p), msg, SEQ_TRACE_RECEIVE, - c_p->id, c_p); +#endif + } +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(message_receive)) { + Eterm token2 = NIL; + DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + + dtrace_proc_str(c_p, receiver_name); + token2 = SEQ_TRACE_TOKEN(c_p); + if (token2 != NIL && token2 != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token2)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token2)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token2)); + } + DTRACE6(message_receive, + receiver_name, size_object(ERL_MESSAGE_TERM(msgp)), + c_p->msg.len - 1, tok_label, tok_lastcnt, tok_serial); } +#endif UNLINK_MESSAGE(c_p, msgp); JOIN_MESSAGE(c_p); CANCEL_TIMER(c_p); @@ -3157,6 +3372,7 @@ void process_main(void) */ BifFunction vbf; + DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); c_p->current = I-3; /* current and vbf set to please handle_error */ SWAPOUT; c_p->fcalls = FCALLS - 1; @@ -3178,6 +3394,8 @@ void process_main(void) ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result)); PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); + + DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); goto apply_bif_or_nif_epilogue; OpCase(apply_bif): @@ -3197,6 +3415,8 @@ void process_main(void) c_p->arity = 0; /* To allow garbage collection on ourselves * (check_process_code/2). */ + DTRACE_BIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); + SWAPOUT; c_p->fcalls = FCALLS - 1; vbf = (BifFunction) Arg(0); @@ -3216,6 +3436,8 @@ void process_main(void) PROCESS_MAIN_CHK_LOCKS(c_p); } + DTRACE_BIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); + apply_bif_or_nif_epilogue: ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); ERTS_HOLE_CHECK(c_p); @@ -5899,6 +6121,12 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg) save_calls(p, ep); } +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(global_function_entry)) { + BeamInstr *fptr = (BeamInstr *) ep->address; + DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]); + } +#endif return ep->address; } @@ -5948,6 +6176,12 @@ fixed_apply(Process* p, Eterm* reg, Uint arity) save_calls(p, ep); } +#ifdef USE_VM_CALL_PROBES + if (DTRACE_ENABLED(global_function_entry)) { + BeamInstr *fptr = (BeamInstr *) ep->address; + DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]); + } +#endif return ep->address; } @@ -5997,6 +6231,15 @@ erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* re c_p->max_arg_reg = sizeof(c_p->def_arg_reg)/sizeof(c_p->def_arg_reg[0]); } +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_hibernate)) { + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); + dtrace_fun_decode(c_p, module, function, arity, + process_name, mfa); + DTRACE2(process_hibernate, process_name, mfa); + } +#endif /* * Arrange for the process to be resumed at the given MFA with * the stack cleared. @@ -6072,6 +6315,9 @@ call_fun(Process* p, /* Current process. */ actual_arity = (int) code_ptr[-1]; if (actual_arity == arity+num_free) { + DTRACE_LOCAL_CALL(p, (Eterm)code_ptr[-3], + (Eterm)code_ptr[-2], + code_ptr[-1]); if (num_free == 0) { return code_ptr; } else { @@ -6089,7 +6335,7 @@ call_fun(Process* p, /* Current process. */ } else { /* * Something wrong here. First build a list of the arguments. - */ + */ if (is_non_value(args)) { Uint sz = 2 * arity; @@ -6164,6 +6410,7 @@ call_fun(Process* p, /* Current process. */ actual_arity = (int) ep->code[2]; if (arity == actual_arity) { + DTRACE_GLOBAL_CALL(p, ep->code[0], ep->code[1], (Uint)ep->code[2]); return ep->address; } else { /* @@ -6239,6 +6486,7 @@ call_fun(Process* p, /* Current process. */ reg[1] = function; reg[2] = args; } + DTRACE_GLOBAL_CALL(p, module, function, arity); return ep->address; } else { badfun: diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index f8305944a4..39d4582435 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -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 @@ -563,7 +563,11 @@ erts_queue_monitor_message(Process *p, ref_copy = copy_struct(ref, ref_size, &hp, ohp); tup = TUPLE5(hp, am_DOWN, ref_copy, type, item_copy, reason_copy); - erts_queue_message(p, p_locksp, bp, tup, NIL); + erts_queue_message(p, p_locksp, bp, tup, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } static BIF_RETTYPE @@ -1944,7 +1948,11 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) { if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) save_calls(p, &exp_send); - if (SEQ_TRACE_TOKEN(p) != NIL) { + if (SEQ_TRACE_TOKEN(p) != NIL +#ifdef USE_VM_PROBES + && SEQ_TRACE_TOKEN(p) != am_have_dt_utag +#endif + ) { seq_trace_update_send(p); seq_trace_output(SEQ_TRACE_TOKEN(p), msg, SEQ_TRACE_SEND, portid, p); @@ -3665,43 +3673,122 @@ BIF_RETTYPE display_nl_0(BIF_ALIST_0) /* ARGSUSED */ BIF_RETTYPE halt_0(BIF_ALIST_0) { - VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt/0\n")); - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erl_exit(0, ""); - return NIL; /* Pedantic (lint does not know about erl_exit) */ + VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt()\n")); + erl_halt(0); + ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined); } /**********************************************************************/ -#define MSG_SIZE 200 +#define HALT_MSG_SIZE 200 +static char halt_msg[HALT_MSG_SIZE]; /* stop the system with exit code */ /* ARGSUSED */ BIF_RETTYPE halt_1(BIF_ALIST_1) { Sint code; - static char msg[MSG_SIZE]; - int i; if (is_small(BIF_ARG_1) && (code = signed_val(BIF_ARG_1)) >= 0) { - VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%d)\n", code)); + VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1)); + erl_halt((int)(- code)); + ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined); + } + else if (ERTS_IS_ATOM_STR("abort", BIF_ARG_1)) { + VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1)); erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erl_exit(-code, ""); - } else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) { - if ((i = intlist_to_buf(BIF_ARG_1, msg, MSG_SIZE-1)) < 0) { + erl_exit(ERTS_ABORT_EXIT, ""); + } + else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) { + int i; + + if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE-1)) < 0) { goto error; } - msg[i] = '\0'; - VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%s)\n", msg)); + halt_msg[i] = '\0'; + VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1)); erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erl_exit(ERTS_DUMP_EXIT, "%s\n", msg); - } else { - error: + erl_exit(ERTS_DUMP_EXIT, "%s\n", halt_msg); + } + else + goto error; + return NIL; /* Pedantic (lint does not know about erl_exit) */ + error: BIF_ERROR(BIF_P, BADARG); +} + +/**********************************************************************/ + +/* stop the system with exit code and flags */ +/* ARGSUSED */ +BIF_RETTYPE halt_2(BIF_ALIST_2) +{ + Sint code; + Eterm optlist = BIF_ARG_2; + int flush = 0; + + for (optlist = BIF_ARG_2; + is_list(optlist); + optlist = CDR(list_val(optlist))) { + Eterm *tp, opt = CAR(list_val(optlist)); + if (is_not_tuple(opt)) + goto error; + tp = tuple_val(opt); + if (tp[0] != make_arityval(2)) + goto error; + if (tp[1] == am_flush) { + if (tp[2] == am_true) + flush = 1; + else if (tp[2] == am_false) + flush = 0; + else + goto error; + } + else + goto error; } + if (is_not_nil(optlist)) + goto error; + + if (is_small(BIF_ARG_1) && (code = signed_val(BIF_ARG_1)) >= 0) { + VERBOSE(DEBUG_SYSTEM, + ("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2)); + if (flush) { + erl_halt((int)(- code)); + ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined); + } + else { + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erl_exit((int)(- code), ""); + } + } + else if (ERTS_IS_ATOM_STR("abort", BIF_ARG_1)) { + VERBOSE(DEBUG_SYSTEM, + ("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2)); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erl_exit(ERTS_ABORT_EXIT, ""); + } + else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) { + int i; + + if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE-1)) < 0) { + goto error; + } + halt_msg[i] = '\0'; + VERBOSE(DEBUG_SYSTEM, + ("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2)); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erl_exit(ERTS_DUMP_EXIT, "%s\n", halt_msg); + } + else + goto error; return NIL; /* Pedantic (lint does not know about erl_exit) */ + error: + BIF_ERROR(BIF_P, BADARG); } +/**********************************************************************/ + BIF_RETTYPE function_exported_3(BIF_ALIST_3) { if (is_not_atom(BIF_ARG_1) || @@ -4147,13 +4234,21 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) for (i = 0; i < erts_max_processes; i++) { if (process_tab[i] != (Process*) 0) { Process* p = process_tab[i]; +#ifdef USE_VM_PROBES + p->seq_trace_token = (p->dt_utag != NIL) ? am_have_dt_utag : NIL; +#else p->seq_trace_token = NIL; +#endif p->seq_trace_clock = 0; p->seq_trace_lastcnt = 0; ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p); mp = p->msg.first; while(mp != NULL) { +#ifdef USE_VM_PROBES + ERL_MESSAGE_TOKEN(mp) = (ERL_MESSAGE_DT_UTAG(mp) != NIL) ? am_have_dt_utag : NIL; +#else ERL_MESSAGE_TOKEN(mp) = NIL; +#endif mp = mp->next; } } @@ -4551,3 +4646,193 @@ BIF_RETTYPE get_module_info_2(BIF_ALIST_2) } BIF_RET(ret); } + +BIF_RETTYPE dt_put_tag_1(BIF_ALIST_1) +{ +#ifdef USE_VM_PROBES + Eterm otag; + if (BIF_ARG_1 == am_undefined) { + otag = (DT_UTAG(BIF_P) == NIL) ? am_undefined : DT_UTAG(BIF_P); + DT_UTAG(BIF_P) = NIL; + DT_UTAG_FLAGS(BIF_P) = 0; + if (SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag) { + SEQ_TRACE_TOKEN(BIF_P) = NIL; + } + BIF_RET(otag); + } + if (!is_binary(BIF_ARG_1)) { + BIF_ERROR(BIF_P,BADARG); + } + otag = (DT_UTAG(BIF_P) == NIL) ? am_undefined : DT_UTAG(BIF_P); + DT_UTAG(BIF_P) = BIF_ARG_1; + DT_UTAG_FLAGS(BIF_P) |= DT_UTAG_PERMANENT; + if (SEQ_TRACE_TOKEN(BIF_P) == NIL) { + SEQ_TRACE_TOKEN(BIF_P) = am_have_dt_utag; + } + BIF_RET(otag); +#else + BIF_RET(am_undefined); +#endif +} + +BIF_RETTYPE dt_get_tag_0(BIF_ALIST_0) +{ +#ifdef USE_VM_PROBES + BIF_RET((DT_UTAG(BIF_P) == NIL || !(DT_UTAG_FLAGS(BIF_P) & DT_UTAG_PERMANENT)) ? am_undefined : DT_UTAG(BIF_P)); +#else + BIF_RET(am_undefined); +#endif +} +BIF_RETTYPE dt_get_tag_data_0(BIF_ALIST_0) +{ +#ifdef USE_VM_PROBES + BIF_RET((DT_UTAG(BIF_P) == NIL) ? am_undefined : DT_UTAG(BIF_P)); +#else + BIF_RET(am_undefined); +#endif +} +BIF_RETTYPE dt_prepend_vm_tag_data_1(BIF_ALIST_1) +{ +#ifdef USE_VM_PROBES + Eterm b; + Eterm *hp; + hp = HAlloc(BIF_P,2); + if (is_binary((DT_UTAG(BIF_P)))) { + Uint sz = binary_size(DT_UTAG(BIF_P)); + int i; + unsigned char *p,*q; + byte *temp_alloc = NULL; + b = new_binary(BIF_P,NULL,sz+1); + q = binary_bytes(b); + p = erts_get_aligned_binary_bytes(DT_UTAG(BIF_P),&temp_alloc); + for(i=0;i<sz;++i) { + q[i] = p[i]; + } + erts_free_aligned_binary_bytes(temp_alloc); + q[sz] = '\0'; + } else { + b = new_binary(BIF_P,(byte *)"\0",1); + } + BIF_RET(CONS(hp,b,BIF_ARG_1)); +#else + BIF_RET(BIF_ARG_1); +#endif +} +BIF_RETTYPE dt_append_vm_tag_data_1(BIF_ALIST_1) +{ +#ifdef USE_VM_PROBES + Eterm b; + Eterm *hp; + hp = HAlloc(BIF_P,2); + if (is_binary((DT_UTAG(BIF_P)))) { + Uint sz = binary_size(DT_UTAG(BIF_P)); + int i; + unsigned char *p,*q; + byte *temp_alloc = NULL; + b = new_binary(BIF_P,NULL,sz+1); + q = binary_bytes(b); + p = erts_get_aligned_binary_bytes(DT_UTAG(BIF_P),&temp_alloc); + for(i=0;i<sz;++i) { + q[i] = p[i]; + } + erts_free_aligned_binary_bytes(temp_alloc); + q[sz] = '\0'; + } else { + b = new_binary(BIF_P,(byte *)"\0",1); + } + BIF_RET(CONS(hp,BIF_ARG_1,b)); +#else + BIF_RET(BIF_ARG_1); +#endif +} +BIF_RETTYPE dt_spread_tag_1(BIF_ALIST_1) +{ +#ifdef USE_VM_PROBES + Eterm ret; + Eterm *hp; +#endif + if (BIF_ARG_1 != am_true && BIF_ARG_1 != am_false) { + BIF_ERROR(BIF_P,BADARG); + } +#ifdef USE_VM_PROBES + hp = HAlloc(BIF_P,3); + ret = TUPLE2(hp,make_small(DT_UTAG_FLAGS(BIF_P)),DT_UTAG(BIF_P)); + if (DT_UTAG(BIF_P) != NIL) { + if (BIF_ARG_1 == am_true) { + DT_UTAG_FLAGS(BIF_P) |= DT_UTAG_SPREADING; +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) start spreading tag %T\r\n", + BIF_P->id,DT_UTAG(BIF_P)); +#endif + } else { + DT_UTAG_FLAGS(BIF_P) &= ~DT_UTAG_SPREADING; +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) stop spreading tag %T\r\n", + BIF_P->id,DT_UTAG(BIF_P)); +#endif + } + } + BIF_RET(ret); +#else + BIF_RET(am_true); +#endif +} +BIF_RETTYPE dt_restore_tag_1(BIF_ALIST_1) +{ +#ifdef USE_VM_PROBES + Eterm *tpl; + Uint x; + if (is_not_tuple(BIF_ARG_1)) { + BIF_ERROR(BIF_P,BADARG); + } + tpl = tuple_val(BIF_ARG_1); + if(arityval(*tpl) != 2 || is_not_small(tpl[1]) || (is_not_binary(tpl[2]) && tpl[2] != NIL)) { + BIF_ERROR(BIF_P,BADARG); + } + if (tpl[2] == NIL) { + if (DT_UTAG(BIF_P) != NIL) { +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) restore Killing tag!\r\n", + BIF_P->id); +#endif + } + DT_UTAG(BIF_P) = NIL; + if (SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag) { + SEQ_TRACE_TOKEN(BIF_P) = NIL; + } + DT_UTAG_FLAGS(BIF_P) = 0; + } else { + x = unsigned_val(tpl[1]) & (DT_UTAG_SPREADING | DT_UTAG_PERMANENT); +#ifdef DTRACE_TAG_HARDDEBUG + + if (!(x & DT_UTAG_SPREADING) && (DT_UTAG_FLAGS(BIF_P) & + DT_UTAG_SPREADING)) { + erts_fprintf(stderr, + "Dtrace -> (%T) restore stop spreading " + "tag %T\r\n", + BIF_P->id, tpl[2]); + } else if ((x & DT_UTAG_SPREADING) && + !(DT_UTAG_FLAGS(BIF_P) & DT_UTAG_SPREADING)) { + erts_fprintf(stderr, + "Dtrace -> (%T) restore start spreading " + "tag %T\r\n",BIF_P->id,tpl[2]); + } +#endif + DT_UTAG_FLAGS(BIF_P) = x; + DT_UTAG(BIF_P) = tpl[2]; + if (SEQ_TRACE_TOKEN(BIF_P) == NIL) { + SEQ_TRACE_TOKEN(BIF_P) = am_have_dt_utag; + } + } +#else + if (BIF_ARG_1 != am_true) { + BIF_ERROR(BIF_P,BADARG); + } +#endif + BIF_RET(am_true); +} + + diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 8cc568b16c..8a85e102d1 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -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 @@ -115,6 +115,8 @@ bif erlang:halt/0 bif 'erl.lang.system':halt/0 ebif_halt_0 bif erlang:halt/1 bif 'erl.lang.system':halt/1 ebif_halt_1 +bif erlang:halt/2 +bif 'erl.lang.system':halt/2 ebif_halt_2 bif erlang:phash/2 bif erlang:phash2/1 bif erlang:phash2/2 @@ -812,6 +814,23 @@ bif erlang:check_old_code/1 # bif erlang:universaltime_to_posixtime/1 bif erlang:posixtime_to_universaltime/1 + +# +# New in R15B01 +# + +# The dtrace BIF's are always present, but give dummy results if dynamic trace is not enabled in the build +bif erlang:dt_put_tag/1 +bif erlang:dt_get_tag/0 +bif erlang:dt_get_tag_data/0 +bif erlang:dt_spread_tag/1 +bif erlang:dt_restore_tag/1 + +# These are dummies even with enabled dynamic trace unless vm probes are enabled. +# They are also internal, for dtrace tags sent to the VM's own drivers (efile) +bif erlang:dt_prepend_vm_tag_data/1 +bif erlang:dt_append_vm_tag_data/1 + # # Obsolete # diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 1d968fb147..2c355fadfa 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -30,6 +30,7 @@ #include "big.h" #include "erl_binary.h" #include "erl_bits.h" +#include "dtrace-wrapper.h" #ifdef HYBRID MA_STACK_DECLARE(src); @@ -59,6 +60,14 @@ copy_object(Eterm obj, Process* to) Eterm* hp = HAlloc(to, size); Eterm res; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(copy_object)) { + DTRACE_CHARBUF(proc_name, 64); + + erts_snprintf(proc_name, sizeof(proc_name), "%T", to->id); + DTRACE2(copy_object, proc_name, size); + } +#endif res = copy_struct(obj, size, &hp, &to->off_heap); #ifdef DEBUG if (eq(obj, res) == 0) { @@ -213,6 +222,8 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) if (IS_CONST(obj)) return obj; + DTRACE1(copy_struct, (int32_t)sz); + hp = htop = *hpp; hbot = htop + sz; hstart = (char *)htop; diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index bee61e7273..802feaeb1c 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -42,6 +42,7 @@ #include "external.h" #include "erl_binary.h" #include "erl_thr_progress.h" +#include "dtrace-wrapper.h" /* Turn this on to get printouts of all distribution messages * which go on the line @@ -381,7 +382,11 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp) Eterm tup; Eterm *hp = erts_alloc_message_heap(3,&bp,&ohp,rp,&rp_locks); tup = TUPLE2(hp, am_nodedown, name); - erts_queue_message(rp, &rp_locks, bp, tup, NIL); + erts_queue_message(rp, &rp_locks, bp, tup, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } erts_smp_proc_unlock(rp, rp_locks); } @@ -740,19 +745,50 @@ erts_dsig_send_msg(ErtsDSigData *dsdp, Eterm remote, Eterm message) Eterm token = NIL; Process *sender = dsdp->proc; int res; +#ifdef USE_VM_PROBES + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + Uint msize = 0; + DTRACE_CHARBUF(node_name, 64); + DTRACE_CHARBUF(sender_name, 64); + DTRACE_CHARBUF(receiver_name, 64); +#endif UseTmpHeapNoproc(5); - if (SEQ_TRACE_TOKEN(sender) != NIL) { + if (SEQ_TRACE_TOKEN(sender) != NIL +#ifdef USE_VM_PROBES + && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag +#endif + ) { seq_trace_update_send(sender); token = SEQ_TRACE_TOKEN(sender); seq_trace_output(token, message, SEQ_TRACE_SEND, remote, sender); } +#ifdef USE_VM_PROBES + *node_name = *sender_name = *receiver_name = '\0'; + if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) { + erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname); + erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id); + erts_snprintf(receiver_name, sizeof(receiver_name), "%T", remote); + msize = size_object(message); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + } +#endif if (token != NIL) ctl = TUPLE4(&ctl_heap[0], make_small(DOP_SEND_TT), am_Cookie, remote, token); else ctl = TUPLE3(&ctl_heap[0], make_small(DOP_SEND), am_Cookie, remote); + DTRACE6(message_send, sender_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); + DTRACE7(message_send_remote, sender_name, node_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); res = dsig_send(dsdp, ctl, message, 0); UnUseTmpHeapNoproc(5); return res; @@ -766,13 +802,41 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message) Eterm token = NIL; Process *sender = dsdp->proc; int res; +#ifdef USE_VM_PROBES + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + Uint32 msize = 0; + DTRACE_CHARBUF(node_name, 64); + DTRACE_CHARBUF(sender_name, 64); + DTRACE_CHARBUF(receiver_name, 128); +#endif UseTmpHeapNoproc(6); - if (SEQ_TRACE_TOKEN(sender) != NIL) { + if (SEQ_TRACE_TOKEN(sender) != NIL +#ifdef USE_VM_PROBES + && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag +#endif + ) { seq_trace_update_send(sender); token = SEQ_TRACE_TOKEN(sender); seq_trace_output(token, message, SEQ_TRACE_SEND, remote_name, sender); } +#ifdef USE_VM_PROBES + *node_name = *sender_name = *receiver_name = '\0'; + if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) { + erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname); + erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id); + erts_snprintf(receiver_name, sizeof(receiver_name), + "{%T,%s}", remote_name, node_name); + msize = size_object(message); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + } +#endif if (token != NIL) ctl = TUPLE5(&ctl_heap[0], make_small(DOP_REG_SEND_TT), @@ -780,6 +844,10 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message) else ctl = TUPLE4(&ctl_heap[0], make_small(DOP_REG_SEND), sender->id, am_Cookie, remote_name); + DTRACE6(message_send, sender_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); + DTRACE7(message_send_remote, sender_name, node_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); res = dsig_send(dsdp, ctl, message, 0); UnUseTmpHeapNoproc(6); return res; @@ -793,9 +861,23 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote, Eterm ctl; DeclareTmpHeapNoproc(ctl_heap,6); int res; +#ifdef USE_VM_PROBES + Process *sender = dsdp->proc; + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + DTRACE_CHARBUF(node_name, 64); + DTRACE_CHARBUF(sender_name, 64); + DTRACE_CHARBUF(remote_name, 128); + DTRACE_CHARBUF(reason_str, 128); +#endif UseTmpHeapNoproc(6); - if (token != NIL) { + if (token != NIL +#ifdef USE_VM_PROBES + && token != am_have_dt_utag +#endif + ) { seq_trace_update_send(dsdp->proc); seq_trace_output_exit(token, reason, SEQ_TRACE_SEND, remote, local); ctl = TUPLE5(&ctl_heap[0], @@ -803,6 +885,23 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote, } else { ctl = TUPLE4(&ctl_heap[0], make_small(DOP_EXIT), local, remote, reason); } +#ifdef USE_VM_PROBES + *node_name = *sender_name = *remote_name = '\0'; + if (DTRACE_ENABLED(process_exit_signal_remote)) { + erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname); + erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id); + erts_snprintf(remote_name, sizeof(remote_name), + "{%T,%s}", remote, node_name); + erts_snprintf(reason_str, sizeof(reason), "%T", reason); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + } +#endif + DTRACE7(process_exit_signal_remote, sender_name, node_name, + remote_name, reason_str, tok_label, tok_lastcnt, tok_serial); /* forced, i.e ignore busy */ res = dsig_send(dsdp, ctl, THE_NON_VALUE, 1); UnUseTmpHeapNoproc(6); @@ -1619,6 +1718,18 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy) if (!(dep->qflgs & ERTS_DE_QFLG_BUSY)) { if (suspended) resume = 1; /* was busy when we started, but isn't now */ +#ifdef USE_VM_PROBES + if (resume && DTRACE_ENABLED(dist_port_not_busy)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + + erts_snprintf(port_str, sizeof(port_str), "%T", cid); + erts_snprintf(remote_str, sizeof(remote_str), + "%T", dep->sysname); + DTRACE3(dist_port_not_busy, erts_this_node_sysname, + port_str, remote_str); + } +#endif } else { /* Enqueue suspended process on dist entry */ @@ -1668,6 +1779,19 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy) } if (suspended) { +#ifdef USE_VM_PROBES + if (!resume && DTRACE_ENABLED(dist_port_busy)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + DTRACE_CHARBUF(pid_str, 16); + + erts_snprintf(port_str, sizeof(port_str), "%T", cid); + erts_snprintf(remote_str, sizeof(remote_str), "%T", dep->sysname); + erts_snprintf(pid_str, sizeof(pid_str), "%T", c_p->id); + DTRACE4(dist_port_busy, erts_this_node_sysname, + port_str, remote_str, pid_str); + } +#endif if (!resume && erts_system_monitor_flags.busy_dist_port) monitor_generic(c_p, am_busy_dist_port, cid); return ERTS_DSIG_SEND_YIELD; @@ -1691,6 +1815,18 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf) "(%beu bytes) passed.\n", size); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(dist_output)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + + erts_snprintf(port_str, sizeof(port_str), "%T", prt->id); + erts_snprintf(remote_str, sizeof(remote_str), + "%T", prt->dist_entry->sysname); + DTRACE4(dist_output, erts_this_node_sysname, port_str, + remote_str, size); + } +#endif prt->caller = NIL; fpe_was_unmasked = erts_block_fpe(); (*prt->drv_ptr->output)((ErlDrvData) prt->drv_data, @@ -1733,6 +1869,18 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf) ASSERT(prt->drv_ptr->outputv); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(dist_outputv)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + + erts_snprintf(port_str, sizeof(port_str), "%T", prt->id); + erts_snprintf(remote_str, sizeof(remote_str), + "%T", prt->dist_entry->sysname); + DTRACE4(dist_outputv, erts_this_node_sysname, port_str, + remote_str, size); + } +#endif prt->caller = NIL; fpe_was_unmasked = erts_block_fpe(); (*prt->drv_ptr->outputv)((ErlDrvData) prt->drv_data, &eiov); @@ -2052,6 +2200,18 @@ erts_dist_command(Port *prt, int reds_limit) void erts_dist_port_not_busy(Port *prt) { +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(dist_port_not_busy)) { + DTRACE_CHARBUF(port_str, 64); + DTRACE_CHARBUF(remote_str, 64); + + erts_snprintf(port_str, sizeof(port_str), "%T", prt->id); + erts_snprintf(remote_str, sizeof(remote_str), + "%T", prt->dist_entry->sysname); + DTRACE3(dist_port_not_busy, erts_this_node_sysname, + port_str, remote_str); + } +#endif erts_schedule_dist_command(prt, NULL); } @@ -2972,7 +3132,11 @@ send_nodes_mon_msg(Process *rp, } ASSERT(hend == hp); - erts_queue_message(rp, rp_locksp, bp, msg, NIL); + erts_queue_message(rp, rp_locksp, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } static void @@ -2985,6 +3149,21 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas ASSERT(is_immed(what)); ASSERT(is_immed(node)); ASSERT(is_immed(type)); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(dist_monitor)) { + DTRACE_CHARBUF(what_str, 12); + DTRACE_CHARBUF(node_str, 64); + DTRACE_CHARBUF(type_str, 12); + DTRACE_CHARBUF(reason_str, 64); + + erts_snprintf(what_str, sizeof(what_str), "%T", what); + erts_snprintf(node_str, sizeof(node_str), "%T", node); + erts_snprintf(type_str, sizeof(type_str), "%T", type); + erts_snprintf(reason_str, sizeof(reason_str), "%T", reason); + DTRACE5(dist_monitor, erts_this_node_sysname, + what_str, node_str, type_str, reason_str); + } +#endif ERTS_SMP_LC_ASSERT(!c_p || (erts_proc_lc_my_proc_locks(c_p) diff --git a/erts/emulator/beam/dtrace-wrapper.h b/erts/emulator/beam/dtrace-wrapper.h new file mode 100644 index 0000000000..9d1e55fc43 --- /dev/null +++ b/erts/emulator/beam/dtrace-wrapper.h @@ -0,0 +1,109 @@ +/* + * %CopyrightBegin% + * + * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 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% + */ + +#ifndef __DTRACE_WRAPPER_H +#define __DTRACE_WRAPPER_H + +#define DTRACE_TERM_BUF_SIZE 256 + +/* + * Some varieties of SystemTap macros do not like statically-sized + * char[N] buffers. (For example, CentOS 6's macros.) + * So, we'll play a game to humor them. + * + * The code necessary to play nice with CentOS 6's SystemTap looks + * stupid to a C programmer's eyes, so we hide the ugliness with this + * macro, which expands: + * + * DTRACE_CHARBUF(proc_name, 64); + * + * to become: + * + * char proc_name_BUFFER[64], *proc_name = proc_name_BUFFER; + */ + +#define DTRACE_CHARBUF(name, size) \ + char name##_BUFFER[size], *name = name##_BUFFER + +#if defined(USE_DYNAMIC_TRACE) && defined(USE_VM_PROBES) + +#include "erlang_dtrace.h" + +#define DTRACE_ENABLED(name) \ + erlang_##name##_enabled() +#define DTRACE0(name) \ + erlang_##name() +#define DTRACE1(name, a0) \ + erlang_##name(a0) +#define DTRACE2(name, a0, a1) \ + erlang_##name((a0), (a1)) +#define DTRACE3(name, a0, a1, a2) \ + erlang_##name((a0), (a1), (a2)) +#define DTRACE4(name, a0, a1, a2, a3) \ + erlang_##name((a0), (a1), (a2), (a3)) +#define DTRACE5(name, a0, a1, a2, a3, a4) \ + erlang_##name((a0), (a1), (a2), (a3), (a4)) +#define DTRACE6(name, a0, a1, a2, a3, a4, a5) \ + erlang_##name((a0), (a1), (a2), (a3), (a4), (a5)) +#define DTRACE7(name, a0, a1, a2, a3, a4, a5, a6) \ + erlang_##name((a0), (a1), (a2), (a3), (a4), (a5), (a6)) +#define DTRACE10(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ + erlang_##name((a0), (a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9)) +#define DTRACE11(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \ + erlang_##name((a0), (a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9), (a10)) + +#if defined(_SDT_PROBE) && !defined(STAP_PROBE11) +/* SLF: This is Ubuntu 11-style SystemTap hackery */ +/* work arround for missing STAP macro */ +#define STAP_PROBE11(provider,name,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11) \ + _SDT_PROBE(provider, name, 11, \ + (arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11)) +#define _SDT_ASM_OPERANDS_11(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11) \ + _SDT_ASM_OPERANDS_10(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,arg10), \ + _SDT_ARG(11, arg11) +#endif + +#ifdef STAP_PROBE_ADDR +/* SLF: This is CentOS 5-style SystemTap hackery */ +/* SystemTap compat mode cannot support 11 args. We'll ignore the 11th */ +#define STAP_PROBE11(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9,parm10,parm11) \ + STAP_PROBE10(provider,probe,(parm1),(parm2),(parm3),(parm4),(parm5),(parm6),(parm7),(parm8),(parm9),(parm10)) +#endif /* STAP_PROBE_ADDR */ + +#else /* USE_DYNAMIC_TRACE && USE_VM_PROBES */ + +/* Render all macros to do nothing */ +#define DTRACE_ENABLED(name) 0 +#define DTRACE0(name) do {} while (0) +#define DTRACE1(name, a0) do {} while (0) +#define DTRACE2(name, a0, a1) do {} while (0) +#define DTRACE3(name, a0, a1, a2) do {} while (0) +#define DTRACE4(name, a0, a1, a2, a3) do {} while (0) +#define DTRACE5(name, a0, a1, a2, a3, a4) do {} while (0) +#define DTRACE6(name, a0, a1, a2, a3, a4, a5) do {} while (0) +#define DTRACE7(name, a0, a1, a2, a3, a4, a5, a6) do {} while (0) +#define DTRACE10(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ + do {} while (0) +#define DTRACE11(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \ + do {} while (0) + +#endif /* USE_DYNAMIC_TRACE && USE_VM_PROBES */ + +#endif /* __DTRACE_WRAPPER_H */ diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index df27186680..8130d5c576 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3001,7 +3001,11 @@ reply_alloc_info(void *vair) HRelease(rp, hp_end, hp); } - erts_queue_message(rp, &rp_locks, bp, msg, NIL); + erts_queue_message(rp, &rp_locks, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); if (air->req_sched == sched_id) rp_locks &= ~ERTS_PROC_LOCK_MAIN; diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index 8bca9ae582..f0e98b33a5 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -26,6 +26,7 @@ #include "erl_threads.h" #include "erl_thr_queue.h" #include "erl_async.h" +#include "dtrace-wrapper.h" #define ERTS_MAX_ASYNC_READY_CALLS_IN_SEQ 20 @@ -121,6 +122,14 @@ typedef struct { #endif } ErtsAsyncData; +/* + * Some compilers, e.g. GCC 4.2.1 and -O3, will optimize away DTrace + * calls if they're the last thing in the function. :-( + * Many thanks to Trond Norbye, via: + * https://github.com/memcached/memcached/commit/6298b3978687530bc9d219b6ac707a1b681b2a46 + */ +static unsigned gcc_optimizer_hack = 0; + int erts_async_max_threads; /* Initialized by erl_init.c */ int erts_async_thread_suggested_stack_size; /* Initialized by erl_init.c */ @@ -244,6 +253,8 @@ erts_get_async_ready_queue(Uint sched_id) static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q) { + int len; + if (is_internal_port(a->port)) { #if ERTS_USE_ASYNC_READY_Q ErtsAsyncReadyQ *arq = async_ready_q(a->sched_id); @@ -259,6 +270,17 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q) #endif erts_thr_q_enqueue(&q->thr_q, a); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(aio_pool_add)) { + DTRACE_CHARBUF(port_str, 16); + + erts_snprintf(port_str, sizeof(port_str), "%T", a->port); + /* DTRACE TODO: Get the queue length from erts_thr_q_enqueue() ? */ + len = -1; + DTRACE2(aio_pool_add, port_str, len); + } +#endif + gcc_optimizer_hack++; } static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, @@ -269,6 +291,7 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, int saved_fin_deq = 0; ErtsThrQFinDeQ_t fin_deq; #endif + int len; while (1) { ErtsAsync *a = (ErtsAsync *) erts_thr_q_dequeue(q); @@ -280,7 +303,16 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, if (saved_fin_deq) erts_thr_q_append_finalize_dequeue_data(&a->q.fin_deq, &fin_deq); #endif +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(aio_pool_get)) { + DTRACE_CHARBUF(port_str, 16); + erts_snprintf(port_str, sizeof(port_str), "%T", a->port); + /* DTRACE TODO: Get the length from erts_thr_q_dequeue() ? */ + len = -1; + DTRACE2(aio_pool_get, port_str, len); + } +#endif return a; } diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index 37d540b41b..bcfdacb91c 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -45,6 +45,7 @@ #include "big.h" #include "dist.h" #include "erl_version.h" +#include "dtrace-wrapper.h" #ifdef ERTS_SMP #define DDLL_SMP 1 @@ -1647,6 +1648,7 @@ static int do_unload_driver_entry(DE_Handle *dh, Eterm *save_name) diver_list lock here!*/ if (q->finish) { int fpe_was_unmasked = erts_block_fpe(); + DTRACE1(driver_finish, q->name); (*(q->finish))(); erts_unblock_fpe(fpe_was_unmasked); } @@ -1760,7 +1762,11 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type, hp += REF_THING_SIZE; mess = TUPLE5(hp,type,r,am_driver,driver_name,tag); } - erts_queue_message(proc, &rp_locks, bp, mess, am_undefined); + erts_queue_message(proc, &rp_locks, bp, mess, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(proc, rp_locks); ERTS_SMP_CHK_NO_PROC_LOCKS; } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index ebd475f73a..041eac240d 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -115,6 +115,12 @@ static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE #ifdef VALGRIND " [valgrind-compiled]" #endif +#ifdef USE_DTRACE + " [dtrace]" +#endif +#ifdef USE_SYSTEMTAP + " [systemtap]" +#endif "\n"); #define ASIZE(a) (sizeof(a)/sizeof(a[0])) @@ -2720,6 +2726,24 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) #endif BIF_RET(am_true); } + else if (ERTS_IS_ATOM_STR("dynamic_trace", BIF_ARG_1)) { +#if defined(USE_DTRACE) + DECL_AM(dtrace); + BIF_RET(AM_dtrace); +#elif defined(USE_SYSTEMTAP) + DECL_AM(systemtap); + BIF_RET(AM_systemtap); +#else + BIF_RET(am_none); +#endif + } + else if (ERTS_IS_ATOM_STR("dynamic_trace_probes", BIF_ARG_1)) { +#if defined(USE_VM_PROBES) + BIF_RET(am_true); +#else + BIF_RET(am_false); +#endif + } #ifdef ERTS_SMP else if (ERTS_IS_ATOM_STR("thread_progress", BIF_ARG_1)) { erts_thr_progress_dbg_print_state(); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index cd423eb200..3056319809 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -40,6 +40,7 @@ #include "external.h" #include "packet_parser.h" #include "erl_bits.h" +#include "dtrace-wrapper.h" static int open_port(Process* p, Eterm name, Eterm settings, int *err_nump); static byte* convert_environment(Process* p, Eterm env); @@ -343,6 +344,16 @@ port_call(Process* c_p, Eterm arg1, Eterm arg2, Eterm arg3) __FILE__, __LINE__, endp - (bytes + size)); } erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_call)) { + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + + dtrace_pid_str(p->connected, process_str); + dtrace_port_str(p, port_str); + DTRACE5(driver_call, process_str, port_str, p->name, op, real_size); + } +#endif prc = (char *) port_resp; fpe_was_unmasked = erts_block_fpe(); ret = drv->call((ErlDrvData)p->drv_data, @@ -539,6 +550,18 @@ BIF_RETTYPE port_connect_2(BIF_ALIST_2) prt->connected = pid; /* internal pid */ erts_smp_port_unlock(prt); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_connect)) { + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE); + + dtrace_pid_str(prt->connected, process_str); + erts_snprintf(port_str, sizeof(port_str), "%T", prt->id); + dtrace_proc_str(rp, newprocess_str); + DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str); + } +#endif BIF_RET(am_true); } @@ -904,7 +927,16 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump) erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN); port_num = erts_open_driver(driver, p->id, name_buf, &opts, err_nump); +#ifdef USE_VM_PROBES + if (port_num >= 0 && DTRACE_ENABLED(port_open)) { + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + dtrace_proc_str(p, process_str); + erts_snprintf(port_str, sizeof(port_str), "%T", erts_port[port_num].id); + DTRACE3(port_open, process_str, name_buf, port_str); + } +#endif erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); if (port_num < 0) { diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c index a922a33da3..0002f8374f 100644 --- a/erts/emulator/beam/erl_bif_timer.c +++ b/erts/emulator/beam/erl_bif_timer.c @@ -373,7 +373,11 @@ bif_timer_timeout(ErtsBifTimer* btm) message = TUPLE3(hp, am_timeout, ref, message); } - erts_queue_message(rp, &rp_locks, bp, message, NIL); + erts_queue_message(rp, &rp_locks, bp, message, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(rp, rp_locks); erts_smp_proc_dec_refc(rp); } diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index b0a58c80ea..1ef4b07c24 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -1744,9 +1744,17 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, return THE_NON_VALUE; } if (build_result) { +#ifdef USE_VM_PROBES + old_value = (SEQ_TRACE_TOKEN(p) == am_have_dt_utag) ? NIL : SEQ_TRACE_TOKEN(p); +#else old_value = SEQ_TRACE_TOKEN(p); +#endif } +#ifdef USE_VM_PROBES + SEQ_TRACE_TOKEN(p) = (DT_UTAG(p) != NIL) ? am_have_dt_utag : NIL; +#else SEQ_TRACE_TOKEN(p) = NIL; +#endif return old_value; } else { @@ -1759,7 +1767,11 @@ new_seq_trace_token(Process* p) { Eterm* hp; - if (SEQ_TRACE_TOKEN(p) == NIL) { + if (SEQ_TRACE_TOKEN(p) == NIL +#ifdef USE_VM_PROBES + || SEQ_TRACE_TOKEN(p) == am_have_dt_utag +#endif + ) { hp = HAlloc(p, 6); SEQ_TRACE_TOKEN(p) = TUPLE5(hp, make_small(0), /* Flags */ make_small(0), /* Label */ @@ -1779,7 +1791,11 @@ BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item) BIF_ERROR(p, BADARG); } - if (SEQ_TRACE_TOKEN(p) == NIL) { + if (SEQ_TRACE_TOKEN(p) == NIL +#ifdef USE_VM_PROBES + || SEQ_TRACE_TOKEN(p) == am_have_dt_utag +#endif + ) { if ((item == am_send) || (item == am_receive) || (item == am_print) || (item == am_timestamp)) { hp = HAlloc(p,3); @@ -1836,8 +1852,13 @@ BIF_RETTYPE seq_trace_info_1(BIF_ALIST_1) */ BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1) { - if (SEQ_TRACE_TOKEN(BIF_P) == NIL) + if (SEQ_TRACE_TOKEN(BIF_P) == NIL +#ifdef USE_VM_PROBES + || SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag +#endif + ) { BIF_RET(am_false); + } seq_trace_update_send(BIF_P); seq_trace_output(SEQ_TRACE_TOKEN(BIF_P), BIF_ARG_1, SEQ_TRACE_PRINT, NIL, BIF_P); @@ -1854,8 +1875,13 @@ BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1) */ BIF_RETTYPE seq_trace_print_2(BIF_ALIST_2) { - if (SEQ_TRACE_TOKEN(BIF_P) == NIL) + if (SEQ_TRACE_TOKEN(BIF_P) == NIL +#ifdef USE_VM_PROBES + || SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag +#endif + ) { BIF_RET(am_false); + } if (!(is_atom(BIF_ARG_1) || is_small(BIF_ARG_1))) { BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 4821a7d9fb..be345e7c9b 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2203,7 +2203,11 @@ restart: *esp++ = am_true; break; case matchIsSeqTrace: - if (SEQ_TRACE_TOKEN(c_p) != NIL) + if (SEQ_TRACE_TOKEN(c_p) != NIL +#ifdef USE_VM_PROBES + && SEQ_TRACE_TOKEN(c_p) != am_have_dt_utag +#endif + ) *esp++ = am_true; else *esp++ = am_false; @@ -2227,7 +2231,11 @@ restart: --esp; break; case matchGetSeqToken: - if (SEQ_TRACE_TOKEN(c_p) == NIL) + if (SEQ_TRACE_TOKEN(c_p) == NIL +#ifdef USE_VM_PROBES + || SEQ_TRACE_TOKEN(c_p) == am_have_dt_utag +#endif + ) *esp++ = NIL; else { Eterm sender = SEQ_TRACE_TOKEN_SENDER(c_p); diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 7510f6b724..1ae9a211d7 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -649,6 +649,8 @@ EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size); #endif +/* also in global.h, but driver's can't include global.h */ +void dtrace_drvport_str(ErlDrvPort port, char *port_buf); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index bde87b8346..82f2dc6091 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -35,6 +35,7 @@ #include "hipe_stack.h" #include "hipe_mode_switch.h" #endif +#include "dtrace-wrapper.h" #define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1 #define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20 @@ -349,7 +350,9 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) Uint reclaimed_now = 0; int done = 0; Uint ms1, s1, us1; - +#ifdef USE_VM_PROBES + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); +#endif if (IS_TRACED_FL(p, F_TRACE_GC)) { trace_gc(p, am_gc_start); } @@ -369,15 +372,27 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) if (GEN_GCS(p) >= MAX_GEN_GCS(p)) { FLAGS(p) |= F_NEED_FULLSWEEP; } - +#ifdef USE_VM_PROBES + *pidbuf = '\0'; + if (DTRACE_ENABLED(gc_major_start) + || DTRACE_ENABLED(gc_major_end) + || DTRACE_ENABLED(gc_minor_start) + || DTRACE_ENABLED(gc_minor_end)) { + dtrace_proc_str(p, pidbuf); + } +#endif /* * Test which type of GC to do. */ while (!done) { if ((FLAGS(p) & F_NEED_FULLSWEEP) != 0) { + DTRACE2(gc_major_start, pidbuf, need); done = major_collection(p, need, objv, nobj, &reclaimed_now); + DTRACE2(gc_major_end, pidbuf, reclaimed_now); } else { + DTRACE2(gc_minor_start, pidbuf, need); done = minor_collection(p, need, objv, nobj, &reclaimed_now); + DTRACE2(gc_minor_end, pidbuf, reclaimed_now); } } @@ -1118,6 +1133,15 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj) sys_memcpy(n_heap + new_sz - n, p->stop, n * sizeof(Eterm)); p->stop = n_heap + new_sz - n; +#ifdef USE_VM_PROBES + if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) { + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, pidbuf); + DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz); + } +#endif + ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, (void*)HEAP_START(p), HEAP_SIZE(p) * sizeof(Eterm)); @@ -1339,6 +1363,15 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl) sys_memcpy(n_heap + new_sz - n, p->stop, n * sizeof(Eterm)); p->stop = n_heap + new_sz - n; +#ifdef USE_VM_PROBES + if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) { + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, pidbuf); + DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz); + } +#endif + ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, (void *) HEAP_START(p), (HEAP_END(p) - HEAP_START(p)) * sizeof(Eterm)); @@ -1907,7 +1940,13 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) roots[n].sz = 1; n++; } - +#ifdef USE_VM_PROBES + if (is_not_immed(p->dt_utag)) { + roots[n].v = &p->dt_utag; + roots[n].sz = 1; + n++; + } +#endif ASSERT(is_nil(p->tracer_proc) || is_internal_pid(p->tracer_proc) || is_internal_port(p->tracer_proc)); @@ -2009,6 +2048,16 @@ grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj) HEAP_TOP(p) = new_heap + heap_size; HEAP_START(p) = new_heap; } + +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_heap_grow)) { + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, pidbuf); + DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz); + } +#endif + HEAP_SIZE(p) = new_sz; } @@ -2018,7 +2067,6 @@ shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj) Eterm* new_heap; Uint heap_size = HEAP_TOP(p) - HEAP_START(p); Sint offs; - Uint stack_size = p->hend - p->stop; ASSERT(new_sz < p->heap_sz); @@ -2047,6 +2095,16 @@ shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj) HEAP_TOP(p) = new_heap + heap_size; HEAP_START(p) = new_heap; } + +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_heap_shrink)) { + DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, pidbuf); + DTRACE3(process_heap_shrink, pidbuf, HEAP_SIZE(p), new_sz); + } +#endif + HEAP_SIZE(p) = new_sz; } @@ -2429,6 +2487,13 @@ offset_mqueue(Process *p, Sint offs, char* area, Uint area_size) if (is_boxed(mesg) && in_area(ptr_val(mesg), area, area_size)) { ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs); } +#ifdef USE_VM_PROBES + mesg = ERL_MESSAGE_DT_UTAG(mp); + if (is_boxed(mesg) && in_area(ptr_val(mesg), area, area_size)) { + ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs); + } +#endif + ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) || is_tuple(ERL_MESSAGE_TOKEN(mp)) || is_atom(ERL_MESSAGE_TOKEN(mp)))); @@ -2448,6 +2513,9 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size, offset_heap_ptr(&p->fvalue, 1, offs, area, area_size); offset_heap_ptr(&p->ftrace, 1, offs, area, area_size); offset_heap_ptr(&p->seq_trace_token, 1, offs, area, area_size); +#ifdef USE_VM_PROBES + offset_heap_ptr(&p->dt_utag, 1, offs, area, area_size); +#endif offset_heap_ptr(&p->group_leader, 1, offs, area, area_size); offset_mqueue(p, offs, area, area_size); offset_heap_ptr(p->stop, (STACK_START(p) - p->stop), offs, area, area_size); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 717315d8bd..ca4385dd3a 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -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 @@ -1510,7 +1510,7 @@ __decl_noreturn void erts_thr_fatal_error(int err, char *what) #endif static void -system_cleanup(int exit_code) +system_cleanup(int flush_async) { /* * Make sure only one thread exits the runtime system. @@ -1542,7 +1542,7 @@ system_cleanup(int exit_code) * (in threaded non smp case). */ - if (exit_code != 0 + if (!flush_async || !erts_initialized #if defined(USE_THREADS) && !defined(ERTS_SMP) || !erts_equal_tids(main_thread, erts_thr_self()) @@ -1585,21 +1585,12 @@ system_cleanup(int exit_code) erts_exit_flush_async(); } -/* - * Common exit function, all exits from the system go through here. - * n <= 0 -> normal exit with status n; - * n = 127 -> Erlang crash dump produced, exit with status 1; - * other positive n -> Erlang crash dump and core dump produced. - */ - -__decl_noreturn void erl_exit0(char *file, int line, int n, char *fmt,...) +static __decl_noreturn void __noreturn +erl_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2) { unsigned int an; - va_list args; - va_start(args, fmt); - - system_cleanup(n); + system_cleanup(flush_async); save_statistics(); @@ -1609,66 +1600,42 @@ __decl_noreturn void erl_exit0(char *file, int line, int n, char *fmt,...) erts_mtrace_exit((Uint32) an); /* Produce an Erlang core dump if error */ - if (n > 0 && erts_initialized && - (erts_no_crash_dump == 0 || n == ERTS_DUMP_EXIT)) { - erl_crash_dump_v(file, line, fmt, args); + if (((n > 0 && erts_no_crash_dump == 0) || n == ERTS_DUMP_EXIT) + && erts_initialized) { + erl_crash_dump_v((char*) NULL, 0, fmt, args1); } - /* need to reinitialize va_args thing */ - va_end(args); - va_start(args, fmt); - if (fmt != NULL && *fmt != '\0') - erl_error(fmt, args); /* Print error message. */ - va_end(args); + erl_error(fmt, args2); /* Print error message. */ sys_tty_reset(n); if (n == ERTS_INTR_EXIT) exit(0); - else if (n == 127) + else if (n == ERTS_DUMP_EXIT) ERTS_EXIT_AFTER_DUMP(1); else if (n > 0 || n == ERTS_ABORT_EXIT) abort(); exit(an); } -__decl_noreturn void erl_exit(int n, char *fmt,...) +/* Exit without flushing async threads */ +__decl_noreturn void __noreturn erl_exit(int n, char *fmt, ...) { - unsigned int an; - va_list args; - - va_start(args, fmt); - - system_cleanup(n); - - save_statistics(); - - an = abs(n); - - if (erts_mtrace_enabled) - erts_mtrace_exit((Uint32) an); - - /* Produce an Erlang core dump if error */ - if (n > 0 && erts_initialized && - (erts_no_crash_dump == 0 || n == ERTS_DUMP_EXIT)) { - erl_crash_dump_v((char*) NULL, 0, fmt, args); - } - - /* need to reinitialize va_args thing */ - va_end(args); - va_start(args, fmt); - - if (fmt != NULL && *fmt != '\0') - erl_error(fmt, args); /* Print error message. */ - va_end(args); - sys_tty_reset(n); - - if (n == ERTS_INTR_EXIT) - exit(0); - else if (n == ERTS_DUMP_EXIT) - ERTS_EXIT_AFTER_DUMP(1); - else if (n > 0 || n == ERTS_ABORT_EXIT) - abort(); - exit(an); + va_list args1, args2; + va_start(args1, fmt); + va_start(args2, fmt); + erl_exit_vv(n, 0, fmt, args1, args2); + va_end(args2); + va_end(args1); } +/* Exit after flushing async threads */ +__decl_noreturn void __noreturn erl_exit_flush_async(int n, char *fmt, ...) +{ + va_list args1, args2; + va_start(args1, fmt); + va_start(args2, fmt); + erl_exit_vv(n, 1, fmt, args1, args2); + va_end(args2); + va_end(args1); +} diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 09e85893c3..5eb2a69242 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -183,6 +183,9 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "save_ops_lock", NULL }, #endif #endif +#ifdef USE_VM_PROBES + { "efile_drv dtrace mutex", NULL }, +#endif { "mtrace_buf", NULL }, { "erts_alloc_hard_debug", NULL } }; diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index ab1ab7b1ea..4cdf2d7d09 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -31,6 +31,7 @@ #include "erl_process.h" #include "erl_nmgc.h" #include "erl_binary.h" +#include "dtrace-wrapper.h" ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message, ErlMessage, @@ -335,6 +336,11 @@ erts_queue_dist_message(Process *rcvr, Eterm token) { ErlMessage* mp; +#ifdef USE_VM_PROBES + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; +#endif #ifdef ERTS_SMP ErtsProcLocks need_locks; #endif @@ -376,15 +382,61 @@ erts_queue_dist_message(Process *rcvr, message_free(mp); msg = erts_msg_distext2heap(rcvr, rcvr_locks, &mbuf, &token, dist_ext); if (is_value(msg)) - erts_queue_message(rcvr, rcvr_locks, mbuf, msg, token); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(message_queued)) { + DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(rcvr, receiver_name); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + DTRACE6(message_queued, + receiver_name, size_object(msg), rcvr->msg.len, + tok_label, tok_lastcnt, tok_serial); + } +#endif + erts_queue_message(rcvr, rcvr_locks, mbuf, msg, token +#ifdef USE_VM_PROBES + , NIL +#endif + ); } else { /* Enqueue message on external format */ ERL_MESSAGE_TERM(mp) = THE_NON_VALUE; - ERL_MESSAGE_TOKEN(mp) = token; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; + if (token == am_have_dt_utag) { + ERL_MESSAGE_TOKEN(mp) = NIL; + } else { +#endif + ERL_MESSAGE_TOKEN(mp) = token; +#ifdef USE_VM_PROBES + } +#endif mp->next = NULL; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(message_queued)) { + DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(rcvr, receiver_name); + if (token != NIL && token != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); + } + /* + * TODO: We don't know the real size of the external message here. + * -1 will appear to a D script as 4294967295. + */ + DTRACE6(message_queued, receiver_name, -1, rcvr->msg.len + 1, + tok_label, tok_lastcnt, tok_serial); + } +#endif mp->data.dist_ext = dist_ext; LINK_MESSAGE(rcvr, mp); @@ -398,7 +450,11 @@ erts_queue_message(Process* receiver, ErtsProcLocks *receiver_locks, ErlHeapFragment* bp, Eterm message, - Eterm seq_trace_token) + Eterm seq_trace_token +#ifdef USE_VM_PROBES + , Eterm dt_utag +#endif +) { ErlMessage* mp; #ifdef ERTS_SMP @@ -439,6 +495,9 @@ erts_queue_message(Process* receiver, ERL_MESSAGE_TERM(mp) = message; ERL_MESSAGE_TOKEN(mp) = seq_trace_token; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = dt_utag; +#endif mp->next = NULL; mp->data.heap_frag = bp; @@ -462,12 +521,30 @@ erts_queue_message(Process* receiver, LINK_MESSAGE(receiver, mp); #endif +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(message_queued)) { + DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + + dtrace_proc_str(receiver, receiver_name); + if (seq_trace_token != NIL && is_tuple(seq_trace_token)) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token)); + } + DTRACE6(message_queued, + receiver_name, size_object(message), receiver->msg.len, + tok_label, tok_lastcnt, tok_serial); + } +#endif notify_new_message(receiver); if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) { trace_receive(receiver, message); } - + #ifndef ERTS_SMP ERTS_HOLE_CHECK(receiver); #endif @@ -497,6 +574,9 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg) Sint offs; Uint sz; ErlHeapFragment *bp; +#ifdef USE_VM_PROBES + Eterm utag; +#endif #ifdef HARD_DEBUG ProcBin *dbg_mso_start = off_heap->mso; @@ -506,32 +586,56 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg) ErlHeapFragment *dbg_bp; Uint *dbg_hp, *dbg_thp_start; Uint dbg_term_sz, dbg_token_sz; +#ifdef USE_VM_PROBES + Eterm dbg_utag; + Uint dbg_utag_sz; +#endif #endif bp = msg->data.heap_frag; term = ERL_MESSAGE_TERM(msg); token = ERL_MESSAGE_TOKEN(msg); +#ifdef USE_VM_PROBES + utag = ERL_MESSAGE_DT_UTAG(msg); +#endif if (!bp) { +#ifdef USE_VM_PROBES + ASSERT(is_immed(term) && is_immed(token) && is_immed(utag)); +#else ASSERT(is_immed(term) && is_immed(token)); +#endif return; } #ifdef HARD_DEBUG dbg_term_sz = size_object(term); dbg_token_sz = size_object(token); + dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz); +#ifdef USE_VM_PROBES + dbg_utag_sz = size_object(utag); + dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz + dbg_utag_sz ); +#endif /*ASSERT(dbg_term_sz + dbg_token_sz == erts_msg_used_frag_sz(msg)); Copied size may be smaller due to removed SubBins's or garbage. Copied size may be larger due to duplicated shared terms. */ - dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz); dbg_hp = dbg_bp->mem; dbg_term = copy_struct(term, dbg_term_sz, &dbg_hp, &dbg_bp->off_heap); dbg_token = copy_struct(token, dbg_token_sz, &dbg_hp, &dbg_bp->off_heap); - dbg_thp_start = *hpp; +#ifdef USE_VM_PROBES + dbg_utag = copy_struct(utag, dbg_utag_sz, &dbg_hp, &dbg_bp->off_heap); +#endif + dbg_thp_start = *hpp; #endif if (bp->next != NULL) { - move_multi_frags(hpp, off_heap, bp, msg->m, 2); + move_multi_frags(hpp, off_heap, bp, msg->m, +#ifdef USE_VM_PROBES + 3 +#else + 2 +#endif + ); goto copy_done; } @@ -633,6 +737,16 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg) ASSERT(hp > ptr_val(ERL_MESSAGE_TERM(msg))); #endif } +#ifdef USE_VM_PROBES + if (is_not_immed(utag)) { + ASSERT(in_heapfrag(ptr_val(utag), bp)); + ERL_MESSAGE_DT_UTAG(msg) = offset_ptr(utag, offs); +#ifdef HARD_DEBUG + ASSERT(dbg_thp_start <= ptr_val(ERL_MESSAGE_DT_UTAG(msg))); + ASSERT(hp > ptr_val(ERL_MESSAGE_DT_UTAG(msg))); +#endif + } +#endif copy_done: @@ -699,6 +813,9 @@ copy_done: #ifdef HARD_DEBUG ASSERT(eq(ERL_MESSAGE_TERM(msg), dbg_term)); ASSERT(eq(ERL_MESSAGE_TOKEN(msg), dbg_token)); +#ifdef USE_VM_PROBES + ASSERT(eq(ERL_MESSAGE_DT_UTAG(msg), dbg_utag)); +#endif free_message_buffer(dbg_bp); #endif @@ -774,39 +891,101 @@ erts_send_message(Process* sender, Uint msize; ErlHeapFragment* bp = NULL; Eterm token = NIL; - +#ifdef USE_VM_PROBES + DTRACE_CHARBUF(sender_name, 64); + DTRACE_CHARBUF(receiver_name, 64); + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; +#endif BM_STOP_TIMER(system); BM_MESSAGE(message,sender,receiver); BM_START_TIMER(send); + #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); + } +#endif if (SEQ_TRACE_TOKEN(sender) != NIL && !(flags & ERTS_SND_FLG_NO_SEQ_TRACE)) { Eterm* hp; + Eterm stoken = SEQ_TRACE_TOKEN(sender); + Uint seq_trace_size = 0; +#ifdef USE_VM_PROBES + Uint dt_utag_size = 0; + Eterm utag = NIL; +#endif - BM_SWAP_TIMER(send,size); + BM_SWAP_TIMER(send,size); msize = size_object(message); - BM_SWAP_TIMER(size,send); + BM_SWAP_TIMER(size,send); + +#ifdef USE_VM_PROBES + if (stoken != am_have_dt_utag) { +#endif + + seq_trace_update_send(sender); + seq_trace_output(stoken, message, SEQ_TRACE_SEND, + receiver->id, sender); + seq_trace_size = 6; /* TUPLE5 */ +#ifdef USE_VM_PROBES + } + if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) { + dt_utag_size = size_object(DT_UTAG(sender)); + } else if (stoken == am_have_dt_utag ) { + stoken = NIL; + } +#endif - seq_trace_update_send(sender); - seq_trace_output(SEQ_TRACE_TOKEN(sender), message, SEQ_TRACE_SEND, - receiver->id, sender); - bp = new_message_buffer(msize + 6 /* TUPLE5 */); + bp = new_message_buffer(msize + seq_trace_size +#ifdef USE_VM_PROBES + + dt_utag_size +#endif + ); hp = bp->mem; BM_SWAP_TIMER(send,copy); - token = copy_struct(SEQ_TRACE_TOKEN(sender), - 6 /* TUPLE5 */, + token = copy_struct(stoken, + seq_trace_size, &hp, &bp->off_heap); message = copy_struct(message, msize, &hp, &bp->off_heap); +#ifdef USE_VM_PROBES + if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) { + utag = copy_struct(DT_UTAG(sender), dt_utag_size, &hp, &bp->off_heap); +#ifdef DTRACE_TAG_HARDDEBUG + erts_fprintf(stderr, + "Dtrace -> (%T) Spreading tag (%T) with " + "message %T!\r\n",sender->id, utag, message); +#endif + } +#endif BM_MESSAGE_COPIED(msize); BM_SWAP_TIMER(copy,send); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(message_send)) { + if (stoken != NIL && stoken != am_have_dt_utag) { + tok_label = signed_val(SEQ_TRACE_T_LABEL(stoken)); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(stoken)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(stoken)); + } + DTRACE6(message_send, sender_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); + } +#endif erts_queue_message(receiver, receiver_locks, bp, message, - token); + token +#ifdef USE_VM_PROBES + , utag +#endif + ); BM_SWAP_TIMER(send,system); #ifdef HYBRID } else { @@ -835,8 +1014,13 @@ erts_send_message(Process* sender, #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); @@ -874,9 +1058,14 @@ erts_send_message(Process* sender, { ErlMessage* mp = message_alloc(); + DTRACE6(message_send, sender_name, receiver_name, + size_object(message), tok_label, tok_lastcnt, tok_serial); mp->data.attached = NULL; ERL_MESSAGE_TERM(mp) = message; ERL_MESSAGE_TOKEN(mp) = NIL; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; +#endif mp->next = NULL; /* * We move 'in queue' to 'private queue' and place @@ -908,7 +1097,13 @@ erts_send_message(Process* sender, message = copy_struct(message, msize, &hp, ohp); BM_MESSAGE_COPIED(msz); BM_SWAP_TIMER(copy,send); - erts_queue_message(receiver, receiver_locks, bp, message, token); + DTRACE6(message_send, sender_name, receiver_name, + msize, tok_label, tok_lastcnt, tok_serial); + erts_queue_message(receiver, receiver_locks, bp, message, token +#ifdef USE_VM_PROBES + , NIL +#endif + ); BM_SWAP_TIMER(send,system); #else ErlMessage* mp = message_alloc(); @@ -928,8 +1123,13 @@ erts_send_message(Process* sender, message = copy_struct(message, msize, &hp, &receiver->off_heap); BM_MESSAGE_COPIED(msize); BM_SWAP_TIMER(copy,send); + DTRACE6(message_send, sender_name, receiver_name, + (uint32_t)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; mp->data.attached = NULL; LINK_MESSAGE(receiver, mp); @@ -968,7 +1168,11 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, Eterm temptoken; ErlHeapFragment* bp = NULL; - if (token != NIL) { + if (token != NIL +#ifdef USE_VM_PROBES + && token != am_have_dt_utag +#endif + ) { ASSERT(is_tuple(token)); sz_reason = size_object(reason); @@ -983,7 +1187,11 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, /* the trace token must in this case be updated by the caller */ seq_trace_output(token, save, SEQ_TRACE_SEND, to->id, NULL); temptoken = copy_struct(token, sz_token, &hp, &bp->off_heap); - erts_queue_message(to, to_locksp, bp, save, temptoken); + erts_queue_message(to, to_locksp, bp, save, temptoken +#ifdef USE_VM_PROBES + , NIL +#endif + ); } else { ErlOffHeap *ohp; sz_reason = size_object(reason); @@ -1000,7 +1208,11 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp, ? from : copy_struct(from, sz_from, &hp, ohp)); save = TUPLE3(hp, am_EXIT, from_copy, mess); - erts_queue_message(to, to_locksp, bp, save, NIL); + erts_queue_message(to, to_locksp, bp, save, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } } diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 5aca0db6fe..7678c7c753 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -70,11 +70,18 @@ typedef struct erl_mesg { ErlHeapFragment *heap_frag; void *attached; } data; +#ifdef USE_VM_PROBES + Eterm m[3]; /* m[0] = message, m[1] = seq trace token, m[3] = dynamic trace user tag */ +#else Eterm m[2]; /* m[0] = message, m[1] = seq trace token */ +#endif } ErlMessage; #define ERL_MESSAGE_TERM(mp) ((mp)->m[0]) #define ERL_MESSAGE_TOKEN(mp) ((mp)->m[1]) +#ifdef USE_VM_PROBES +#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[2]) +#endif /* Size of default message buffer (erl_message.c) */ #define ERL_MESSAGE_BUF_SZ 500 @@ -221,7 +228,11 @@ ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint, Eterm *, Uint); void free_message_buffer(ErlHeapFragment *); void erts_queue_dist_message(Process*, ErtsProcLocks*, ErtsDistExternal *, Eterm); -void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, Eterm, Eterm); +void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, Eterm, Eterm +#ifdef USE_VM_PROBES + , Eterm dt_utag +#endif +); void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm); void erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned); void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 58a09986d2..40f2fde578 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -66,6 +66,9 @@ static void add_readonly_check(ErlNifEnv*, unsigned char* ptr, unsigned sz); static int is_offheap(const ErlOffHeap* off_heap); #endif +#ifdef USE_VM_PROBES +void dtrace_nifenv_str(ErlNifEnv *, char *); +#endif #define MIN_HEAP_FRAG_SZ 200 static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp); @@ -350,7 +353,11 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, if (flush_me) { flush_env(env); /* Needed for ERTS_HOLE_CHECK */ } - erts_queue_message(rp, &rp_locks, frags, msg, am_undefined); + erts_queue_message(rp, &rp_locks, frags, msg, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); if (rp_locks) { ERTS_SMP_LC_ASSERT(rp_locks == (rp_had_locks | (ERTS_PROC_LOCK_MSGQ | ERTS_PROC_LOCK_STATUS))); @@ -1779,6 +1786,13 @@ void erl_nif_init() resource_type_list.name = THE_NON_VALUE; } +#ifdef USE_VM_PROBES +void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf) +{ + dtrace_pid_str(env->proc->id, process_buf); +} +#endif + #ifdef READONLY_CHECK /* Use checksums to assert that NIFs do not write into inspected binaries */ diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 908ba755ed..1481f66b55 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -27,6 +27,7 @@ #include "big.h" #include "error.h" #include "erl_thr_progress.h" +#include "dtrace-wrapper.h" Hash erts_dist_table; Hash erts_node_table; @@ -42,6 +43,8 @@ Sint erts_no_of_not_connected_dist_entries; DistEntry *erts_this_dist_entry; ErlNode *erts_this_node; +char erts_this_node_sysname_BUFFER[256], + *erts_this_node_sysname = "uninitialized yet"; static Uint node_entries; static Uint dist_entries; @@ -702,6 +705,9 @@ erts_set_this_node(Eterm sysname, Uint creation) (void) hash_erase(&erts_node_table, (void *) erts_this_node); erts_this_node->sysname = sysname; erts_this_node->creation = creation; + erts_this_node_sysname = erts_this_node_sysname_BUFFER; + erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname), + "%T", sysname); (void) hash_put(&erts_node_table, (void *) erts_this_node); erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx); @@ -789,6 +795,9 @@ void erts_init_node_tables(void) erts_this_node->sysname = am_Noname; erts_this_node->creation = 0; erts_this_node->dist_entry = erts_this_dist_entry; + erts_this_node_sysname = erts_this_node_sysname_BUFFER; + erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname), + "%T", erts_this_node->sysname); (void) hash_put(&erts_node_table, (void *) erts_this_node); diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index b0a63ae035..5cfd0ac641 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -169,6 +169,7 @@ extern Sint erts_no_of_not_connected_dist_entries; extern DistEntry *erts_this_dist_entry; extern ErlNode *erts_this_node; +extern char *erts_this_node_sysname; /* must match erl_node_tables.c */ DistEntry *erts_channel_no_to_dist_entry(Uint); DistEntry *erts_sysname_to_connected_dist_entry(Eterm); diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index a2b08fcf56..a8cb4563d6 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -32,6 +32,7 @@ #include "global.h" #include "erl_port_task.h" #include "dist.h" +#include "dtrace-wrapper.h" #if defined(DEBUG) && 0 #define HARD_DEBUG @@ -61,6 +62,20 @@ do { \ (P)->sched.next = NULL; \ } while (0) +#ifdef USE_VM_PROBES +#define DTRACE_DRIVER(PROBE_NAME, PP) \ + if (DTRACE_ENABLED(driver_ready_input)) { \ + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); \ + \ + dtrace_pid_str(PP->connected, process_str); \ + dtrace_port_str(PP, port_str); \ + DTRACE3(PROBE_NAME, process_str, port_str, PP->name); \ + } +#else +#define DTRACE_DRIVER(PROBE_NAME, PP) do {} while(0) +#endif + erts_smp_atomic_t erts_port_task_outstanding_io_tasks; struct ErtsPortTaskQueue_ { @@ -823,12 +838,15 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) goto tasks_done; case ERTS_PORT_TASK_TIMEOUT: reds += ERTS_PORT_REDS_TIMEOUT; - if (!(pp->status & ERTS_PORT_SFLGS_DEAD)) + if (!(pp->status & ERTS_PORT_SFLGS_DEAD)) { + DTRACE_DRIVER(driver_timeout, pp); (*pp->drv_ptr->timeout)((ErlDrvData) pp->drv_data); + } break; case ERTS_PORT_TASK_INPUT: reds += ERTS_PORT_REDS_INPUT; ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0); + DTRACE_DRIVER(driver_ready_input, pp); /* NOTE some windows drivers use ->ready_input for input and output */ (*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data, ptp->event); io_tasks_executed++; @@ -836,12 +854,14 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) case ERTS_PORT_TASK_OUTPUT: reds += ERTS_PORT_REDS_OUTPUT; ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0); + DTRACE_DRIVER(driver_ready_output, pp); (*pp->drv_ptr->ready_output)((ErlDrvData) pp->drv_data, ptp->event); io_tasks_executed++; break; case ERTS_PORT_TASK_EVENT: reds += ERTS_PORT_REDS_EVENT; ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0); + DTRACE_DRIVER(driver_event, pp); (*pp->drv_ptr->event)((ErlDrvData) pp->drv_data, ptp->event, ptp->event_data); io_tasks_executed++; break; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 138acfeb2c..95d408f79d 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -42,6 +42,7 @@ #include "erl_thr_progress.h" #include "erl_thr_queue.h" #include "erl_async.h" +#include "dtrace-wrapper.h" #define ERTS_RUNQ_CHECK_BALANCE_REDS_PER_SCHED (2000*CONTEXT_REDS) #define ERTS_RUNQ_CALL_CHECK_BALANCE_REDS \ @@ -104,6 +105,9 @@ do { \ #define ERTS_EMPTY_RUNQ(RQ) \ ((RQ)->len == 0 && (RQ)->misc.start == NULL) +#define ERTS_EMPTY_RUNQ_PORTS(RQ) \ + ((RQ)->ports.info.len == 0 && (RQ)->misc.start == NULL) + extern BeamInstr beam_apply[]; extern BeamInstr beam_exit[]; extern BeamInstr beam_continue_exit[]; @@ -366,6 +370,9 @@ dbg_chk_aux_work_val(erts_aint32_t value) #ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN valid |= ERTS_SSI_AUX_WORK_CHECK_CHILDREN; #endif +#ifdef ERTS_SSI_AUX_WORK_REAP_PORTS + valid |= ERTS_SSI_AUX_WORK_REAP_PORTS; +#endif if (~valid & value) erl_exit(ERTS_ABORT_EXIT, @@ -672,7 +679,11 @@ reply_sched_wall_time(void *vswtrp) hpp = &hp; } - erts_queue_message(rp, &rp_locks, bp, msg, NIL); + erts_queue_message(rp, &rp_locks, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); if (swtrp->req_sched == esdp->no) rp_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -861,8 +872,6 @@ set_aux_work_flags_wakeup_nob(ErtsSchedulerSleepInfo *ssi, } } -#if 0 /* Currently not used */ - static ERTS_INLINE void set_aux_work_flags_wakeup_relb(ErtsSchedulerSleepInfo *ssi, erts_aint32_t flgs) @@ -882,8 +891,6 @@ set_aux_work_flags_wakeup_relb(ErtsSchedulerSleepInfo *ssi, } } -#endif - static ERTS_INLINE erts_aint32_t set_aux_work_flags(ErtsSchedulerSleepInfo *ssi, erts_aint32_t flgs) { @@ -1351,6 +1358,65 @@ handle_check_children(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) #endif +static void +notify_reap_ports_relb(void) +{ + int i; + for (i = 0; i < erts_no_schedulers; i++) { + set_aux_work_flags_wakeup_relb(ERTS_SCHED_SLEEP_INFO_IX(i), + ERTS_SSI_AUX_WORK_REAP_PORTS); + } +} + +erts_smp_atomic32_t erts_halt_progress; +int erts_halt_code; + +static ERTS_INLINE erts_aint32_t +handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) +{ + unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_REAP_PORTS); + awdp->esdp->run_queue->halt_in_progress = 1; + if (erts_smp_atomic32_dec_read_acqb(&erts_halt_progress) == 0) { + int i; + erts_smp_atomic32_set_nob(&erts_halt_progress, 1); + for (i = 0; i < erts_max_ports; i++) { + Port *prt = &erts_port[i]; + erts_smp_port_state_lock(prt); + if ((prt->status & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + | ERTS_PORT_SFLG_HALT))) { + erts_smp_port_state_unlock(prt); + continue; + } + /* We need to set the halt flag - get the port lock */ +#ifdef ERTS_SMP + erts_smp_atomic_inc_nob(&prt->refc); +#endif + erts_smp_port_state_unlock(prt); +#ifdef ERTS_SMP + erts_smp_mtx_lock(prt->lock); +#endif + if ((prt->status & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + | ERTS_PORT_SFLG_HALT))) { + erts_port_release(prt); + continue; + } + erts_port_status_bor_set(prt, ERTS_PORT_SFLG_HALT); + erts_smp_atomic32_inc_nob(&erts_halt_progress); + if (prt->status & (ERTS_PORT_SFLG_EXITING + | ERTS_PORT_SFLG_CLOSING)) { + erts_port_release(prt); + continue; + } + erts_do_exit_port(prt, prt->id, am_killed); + erts_port_release(prt); + } + if (erts_smp_atomic32_dec_read_nob(&erts_halt_progress) == 0) { + erl_exit_flush_async(erts_halt_code, ""); + } + } + return aux_work & ~ERTS_SSI_AUX_WORK_REAP_PORTS; +} + #if HAVE_ERTS_MSEG static ERTS_INLINE erts_aint32_t @@ -1451,6 +1517,9 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) handle_mseg_cache_check); #endif + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_REAP_PORTS, + handle_reap_ports); + ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); return aux_work; @@ -2716,6 +2785,9 @@ try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq) ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, *rq_lockedp); ERTS_SMP_LC_CHK_RUNQ_LOCK(vrq, vrq_locked); + if (rq->halt_in_progress) + goto try_steal_port; + /* * Check for a runnable process to steal... */ @@ -2802,6 +2874,8 @@ try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq) vrq_locked = 1; } + try_steal_port: + ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, *rq_lockedp); ERTS_SMP_LC_CHK_RUNQ_LOCK(vrq, vrq_locked); @@ -2917,7 +2991,8 @@ try_steal_task(ErtsRunQueue *rq) erts_smp_runq_lock(rq); if (!res) - res = !ERTS_EMPTY_RUNQ(rq); + res = rq->halt_in_progress ? + !ERTS_EMPTY_RUNQ_PORTS(rq) : !ERTS_EMPTY_RUNQ(rq); return res; } @@ -3583,6 +3658,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) rq->len = 0; rq->wakeup_other = 0; rq->wakeup_other_reds = 0; + rq->halt_in_progress = 0; rq->procs.len = 0; rq->procs.pending_exiters = NULL; @@ -3777,6 +3853,9 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) ERTS_VERIFY_UNUSED_TEMP_ALLOC(NULL); #endif #endif + + erts_smp_atomic32_init_relb(&erts_halt_progress, -1); + erts_halt_code = 0; } ErtsRunQueue * @@ -6140,6 +6219,15 @@ Process *schedule(Process *p, int calls) int actual_reds; int reds; +#ifdef USE_VM_PROBES + if (p != NULL && DTRACE_ENABLED(process_unscheduled)) { + DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, process_buf); + DTRACE1(process_unscheduled, process_buf); + } +#endif + if (ERTS_USE_MODIFIED_TIMING()) { context_reds = ERTS_MODIFIED_TIMING_CONTEXT_REDS; input_reductions = ERTS_MODIFIED_TIMING_INPUT_REDS; @@ -6343,7 +6431,9 @@ Process *schedule(Process *p, int calls) ASSERT(rq->len == rq->procs.len + rq->ports.info.len); - if (rq->len == 0 && !rq->misc.start) { + if ((rq->len == 0 && !rq->misc.start) + || (rq->halt_in_progress + && rq->ports.info.len == 0 && !rq->misc.start)) { #ifdef ERTS_SMP @@ -6441,7 +6531,8 @@ Process *schedule(Process *p, int calls) if (rq->ports.info.len) { int have_outstanding_io; have_outstanding_io = erts_port_task_execute(rq, &esdp->current_port); - if (have_outstanding_io && fcalls > 2*input_reductions) { + if ((have_outstanding_io && fcalls > 2*input_reductions) + || rq->halt_in_progress) { /* * If we have performed more than 2*INPUT_REDUCTIONS since * last call to erl_sys_schedule() and we still haven't @@ -7175,6 +7266,10 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->seq_trace_lastcnt = 0; p->seq_trace_clock = 0; SEQ_TRACE_TOKEN(p) = NIL; +#ifdef USE_VM_PROBES + DT_UTAG(p) = NIL; + DT_UTAG_FLAGS(p) = 0; +#endif p->parent = parent->id == ERTS_INVALID_PID ? NIL : parent->id; #ifdef HYBRID @@ -7307,6 +7402,16 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->id)); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_spawn)) { + DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); + + dtrace_fun_decode(p, mod, func, arity, process_name, mfa); + DTRACE2(process_spawn, process_name, mfa); + } +#endif + error: erts_smp_proc_unlock(parent, ERTS_PROC_LOCKS_ALL_MINOR); @@ -7758,7 +7863,11 @@ static ERTS_INLINE void send_exit_message(Process *to, ErtsProcLocks *to_locksp, Eterm exit_term, Uint term_size, Eterm token) { - if (token == NIL) { + if (token == NIL +#ifdef USE_VM_PROBES + || token == am_have_dt_utag +#endif + ) { Eterm* hp; Eterm mess; ErlHeapFragment* bp; @@ -7766,7 +7875,11 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, hp = erts_alloc_message_heap(term_size, &bp, &ohp, to, to_locksp); mess = copy_struct(exit_term, term_size, &hp, ohp); - erts_queue_message(to, to_locksp, bp, mess, NIL); + erts_queue_message(to, to_locksp, bp, mess, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } else { ErlHeapFragment* bp; Eterm* hp; @@ -7782,7 +7895,11 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp, /* the trace token must in this case be updated by the caller */ seq_trace_output(token, mess, SEQ_TRACE_SEND, to->id, NULL); temp_token = copy_struct(token, sz_token, &hp, &bp->off_heap); - erts_queue_message(to, to_locksp, bp, mess, temp_token); + erts_queue_message(to, to_locksp, bp, mess, temp_token +#ifdef USE_VM_PROBES + , NIL +#endif + ); } } @@ -7875,9 +7992,26 @@ send_exit_signal(Process *c_p, /* current process if and only ASSERT(reason != THE_NON_VALUE); +#ifdef USE_VM_PROBES + if(DTRACE_ENABLED(process_exit_signal) && is_pid(from)) { + DTRACE_CHARBUF(sender_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(receiver_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE); + + dtrace_pid_str(from, sender_str); + dtrace_proc_str(rp, receiver_str); + erts_snprintf(reason_buf, sizeof(reason_buf) - 1, "%T", reason); + DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf); + } +#endif + if (ERTS_PROC_IS_TRAPPING_EXITS(rp) && (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) { - if (is_not_nil(token) && token_update) + if (is_not_nil(token) +#ifdef USE_VM_PROBES + && token != am_have_dt_utag +#endif + && token_update) seq_trace_update_send(token_update); if (is_value(exit_tuple)) send_exit_message(rp, rp_locks, exit_tuple, exit_tuple_sz, token); @@ -8301,7 +8435,18 @@ erts_do_exit_process(Process* p, Eterm reason) p->arity = 0; /* No live registers */ p->fvalue = reason; - + +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(process_exit)) { + DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE); + + dtrace_proc_str(p, process_buf); + erts_snprintf(reason_buf, DTRACE_TERM_BUF_SIZE - 1, "%T", reason); + DTRACE2(process_exit, process_buf, reason_buf); + } +#endif + #ifdef ERTS_SMP ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); /* By locking all locks (main lock is already locked) when going @@ -9867,3 +10012,30 @@ debug_processes_assert_error(char* expr, char* file, int line) /* *\ * End of the processes/0 BIF implementation. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * A nice system halt closing all open port goes as follows: + * 1) This function schedules the aux work ERTS_SSI_AUX_WORK_REAP_PORTS + * on all schedulers, then schedules itself out. + * 2) All shedulers detect this and set the flag halt_in_progress + * on their run queue. The last scheduler sets all non-closed ports + * ERTS_PORT_SFLG_HALT. Global atomic erts_halt_progress is used + * as refcount to determine which is last. + * 3) While the run ques has flag halt_in_progress no processes + * will be scheduled, only ports. + * 4) When the last port closes that scheduler calls erlang:halt/1. + * The same global atomic is used as refcount. + * + * A BIF that calls this should make sure to schedule out to never come back: + * erl_halt((int)(- code)); + * ERTS_BIF_YIELD1(bif_export[BIF_erlang_halt_1], BIF_P, NIL); + */ +void erl_halt(int code) +{ + if (-1 == erts_smp_atomic32_cmpxchg_acqb(&erts_halt_progress, + erts_no_schedulers, + -1)) { + erts_halt_code = code; + notify_reap_ports_relb(); + } +} diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index c23810f15a..cff0783bc4 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -264,6 +264,7 @@ typedef enum { #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) typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; @@ -341,6 +342,7 @@ struct ErtsRunQueue_ { int len; int wakeup_other; int wakeup_other_reds; + int halt_in_progress; struct { int len; @@ -681,6 +683,10 @@ struct process { Uint seq_trace_lastcnt; Eterm seq_trace_token; /* Sequential trace token (tuple size 5 see below) */ +#ifdef USE_VM_PROBES + Eterm dt_utag; /* Place to store the dynamc trace user tag */ + Uint dt_utag_flags; /* flag field for the dt_utag */ +#endif BeamInstr initial[3]; /* Initial module(0), function(1), arity(2), often used instead of pointer to funcinfo instruction, hence the BeamInstr datatype */ BeamInstr* current; /* Current Erlang function, part of the funcinfo: @@ -996,6 +1002,14 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags; #define SEQ_TRACE_PRINT (1 << 2) #define SEQ_TRACE_TIMESTAMP (1 << 3) +#ifdef USE_VM_PROBES +#define DT_UTAG_PERMANENT (1 << 0) +#define DT_UTAG_SPREADING (1 << 1) +#define DT_UTAG(P) ((P)->dt_utag) +#define DT_UTAG_FLAGS(P) ((P)->dt_utag_flags) +#endif + + #ifdef ERTS_SMP /* Status flags ... */ #define ERTS_PROC_SFLG_PENDADD2SCHEDQ (((Uint32) 1) << 0) /* Pending @@ -1659,4 +1673,6 @@ erts_sched_poke(ErtsSchedulerSleepInfo *ssi) #endif - +void erl_halt(int code); +extern erts_smp_atomic32_t erts_halt_progress; +extern int erts_halt_code; diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index b1d1e1d9b0..4261cd03be 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -125,8 +125,13 @@ do { \ enqueue_sys_msg_unlocked(SYS_MSG_TYPE_TRACE, (FPID), (TPID), (MSG), (BP)); \ } while(0) #else +#ifdef USE_VM_PROBES #define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \ - erts_queue_message((TPROC), NULL, (BP), (MSG), NIL) + erts_queue_message((TPROC), NULL, (BP), (MSG), NIL, NIL) +#else +#define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \ + erts_queue_message((TPROC), NULL, (BP), (MSG), NIL) +#endif #endif /* @@ -583,7 +588,11 @@ profile_send(Eterm from, Eterm message) { hp = erts_alloc_message_heap(sz, &bp, &off_heap, profile_p, 0); msg = copy_struct(message, sz, &hp, &bp->off_heap); - erts_queue_message(profile_p, NULL, bp, msg, NIL); + erts_queue_message(profile_p, NULL, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); } } @@ -994,9 +1003,13 @@ seq_trace_update_send(Process *p) { Eterm seq_tracer = erts_get_system_seq_tracer(); ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p)))); - if ( (p->id == seq_tracer) || (SEQ_TRACE_TOKEN(p) == NIL)) + if ( (p->id == seq_tracer) || (SEQ_TRACE_TOKEN(p) == NIL) +#ifdef USE_VM_PROBES + || (SEQ_TRACE_TOKEN(p) == am_have_dt_utag) +#endif + ) { return 0; - + } SEQ_TRACE_TOKEN_SENDER(p) = p->id; /* Internal pid */ SEQ_TRACE_TOKEN_SERIAL(p) = make_small(++(p -> seq_trace_clock)); @@ -1178,7 +1191,11 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type, enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SEQTRACE, NIL, NIL, mess, bp); erts_smp_mtx_unlock(&smq_mtx); #else - erts_queue_message(tracer, NULL, bp, mess, NIL); /* trace_token must be NIL here */ + erts_queue_message(tracer, NULL, bp, mess, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); /* trace_token must be NIL here */ #endif } } @@ -2469,7 +2486,11 @@ monitor_long_gc(Process *p, Uint time) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + erts_queue_message(monitor_p, NULL, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); #endif } @@ -2541,7 +2562,11 @@ monitor_large_heap(Process *p) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + erts_queue_message(monitor_p, NULL, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); #endif } @@ -2571,7 +2596,11 @@ monitor_generic(Process *p, Eterm type, Eterm spec) { #ifdef ERTS_SMP enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp); #else - erts_queue_message(monitor_p, NULL, bp, msg, NIL); + erts_queue_message(monitor_p, NULL, bp, msg, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); #endif } @@ -3357,7 +3386,11 @@ sys_msg_dispatcher_func(void *unused) } else { queue_proc_msg: - erts_queue_message(proc,&proc_locks,smqp->bp,smqp->msg,NIL); + erts_queue_message(proc,&proc_locks,smqp->bp,smqp->msg,NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); #ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "delivered\n"); #endif diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d new file mode 100644 index 0000000000..587e51cb67 --- /dev/null +++ b/erts/emulator/beam/erlang_dtrace.d @@ -0,0 +1,726 @@ +/* + * %CopyrightBegin% + * + * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 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% + */ + +/* + * A note on probe naming: if "__" appears in a provider probe + * definition, then two things happen during compilation: + * + * 1. The "__" will turn into a hypen, "-", for the probe name. + * 2. The "__" will turn into a single underscore, "_", for the + * macro names and function definitions that the compiler and + * C developers will see. + * + * We'll try to use the following naming convention. We're a bit + * limited because, as a USDT probe, we can only specify the 4th part + * of the probe name, e.g. erlang*:::mumble. The 2nd part of the + * probe name is always going to be "beam" or "beam.smp", and the 3rd + * part of the probe name will always be the name of the function + * that's calling the probe. + * + * So, all probes will be have names defined in this file using the + * convention category__name or category__sub_category__name. This + * will translate to probe names of category-name or + * category-sub_category-name. + * + * Each of "category", "sub_category", and "name" may have underscores + * but may not have hyphens. + */ + +provider erlang { + /** + * Fired when a message is sent from one local process to another. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * @param sender the PID (string form) of the sender + * @param receiver the PID (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__send(char *sender, char *receiver, uint32_t size, + int token_label, int token_previous, int token_current); + + /** + * Fired when a message is sent from a local process to a remote process. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * @param sender the PID (string form) of the sender + * @param node_name the Erlang node name (string form) of the receiver + * @param receiver the PID/name (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__send__remote(char *sender, char *node_name, char *receiver, + uint32_t size, + int token_label, int token_previous, int token_current); + + /** + * Fired when a message is queued to a local process. This probe + * will not fire if the sender's pid == receiver's pid. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * NOTE: In cases of messages in external format (i.e. from another + * Erlang node), we probably don't know the message size + * without performing substantial extra computation. To + * avoid the extra CPU overhead, the message size may be + * reported as -1, which can appear to a D script as 4294967295. + * + * @param receiver the PID (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param queue_len length of the queue of the receiving process + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__queued(char *receiver, uint32_t size, uint32_t queue_len, + int token_label, int token_previous, int token_current); + + /** + * Fired when a message is 'receive'd by a local process and removed + * from its mailbox. + * + * NOTE: The 'size' parameter is in machine-dependent words and + * that the actual size of any binary terms in the message + * are not included. + * + * NOTE: In cases of messages in external format (i.e. from another + * Erlang node), we probably don't know the message size + * without performing substantial extra computation. To + * avoid the extra CPU overhead, the message size may be + * reported as -1, which can appear to a D script as 4294967295. + * + * @param receiver the PID (string form) of the receiver + * @param size the size of the message being delivered (words) + * @param queue_len length of the queue of the receiving process + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe message__receive(char *receiver, uint32_t size, uint32_t queue_len, + int token_label, int token_previous, int token_current); + + /** + * Fired when an Eterm structure is being copied. + * + * NOTE: Due to the placement of this probe, the process ID of + * owner of the Eterm is not available. + * + * @param size the size of the structure + */ + probe copy__struct(uint32_t size); + + /** + * Fired when an Eterm is being copied onto a process. + * + * @param proc the PID (string form) of the recipient process + * @param size the size of the structure + */ + probe copy__object(char *proc, uint32_t size); + + /* PID, Module, Function, Arity */ + + /** + * Fired whenever a user function is being called locally. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + * @param depth the stack depth + */ + probe local__function__entry(char *p, char *mfa, int depth); + + /** + * Fired whenever a user function is called externally + * (through an export entry). + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + * @param depth the stack depth + */ + probe global__function__entry(char *p, char *mfa, int depth); + + /** + * Fired whenever a user function returns. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + * @param depth the stack depth + */ + probe function__return(char *p, char *mfa, int depth); + + /** + * Fired whenever a Built In Function is called. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + */ + probe bif__entry(char *p, char *mfa); + + /** + * Fired whenever a Built In Function returns. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + */ + probe bif__return(char *p, char *mfa); + + /** + * Fired whenever a Native Function is called. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + */ + probe nif__entry(char *p, char *mfa); + + /** + * Fired whenever a Native Function returns. + * + * @param p the PID (string form) of the process + * @param mfa the m:f/a of the function + */ + probe nif__return(char *p, char *mfa); + + /** + * Fired when a major GC is starting. + * + * @param p the PID (string form) of the exiting process + * @param need the number of words needed on the heap + */ + probe gc_major__start(char *p, int need); + + /** + * Fired when a minor GC is starting. + * + * @param p the PID (string form) of the exiting process + * @param need the number of words needed on the heap + */ + probe gc_minor__start(char *p, int need); + + /** + * Fired when a major GC is starting. + * + * @param p the PID (string form) of the exiting process + * @param reclaimed the amount of space reclaimed + */ + probe gc_major__end(char *p, int reclaimed); + + /** + * Fired when a minor GC is starting. + * + * @param p the PID (string form) of the exiting process + * @param reclaimed the amount of space reclaimed + */ + probe gc_minor__end(char *p, int reclaimed); + + /** + * Fired when a process is spawned. + * + * @param p the PID (string form) of the new process. + * @param mfa the m:f/a of the function + */ + probe process__spawn(char *p, char *mfa); + + /** + * Fired when a process is exiting. + * + * @param p the PID (string form) of the exiting process + * @param reason the reason for the exit (may be truncated) + */ + probe process__exit(char *p, char *reason); + + /** + * Fired when exit signal is delivered to a local process. + * + * @param sender the PID (string form) of the exiting process + * @param receiver the PID (string form) of the process receiving EXIT signal + * @param reason the reason for the exit (may be truncated) + */ + probe process__exit_signal(char *sender, char *receiver, char *reason); + + /** + * Fired when exit signal is delivered to a remote process. + * + * @param sender the PID (string form) of the exiting process + * @param node_name the Erlang node name (string form) of the receiver + * @param receiver the PID (string form) of the process receiving EXIT signal + * @param reason the reason for the exit (may be truncated) + * @param token_label for the sender's sequential trace token + * @param token_previous count for the sender's sequential trace token + * @param token_current count for the sender's sequential trace token + */ + probe process__exit_signal__remote(char *sender, char *node_name, + char *receiver, char *reason, + int token_label, int token_previous, int token_current); + + /** + * Fired when a process is scheduled. + * + * @param p the PID (string form) of the newly scheduled process + * @param mfa the m:f/a of the function it should run next + */ + probe process__scheduled(char *p, char *mfa); + + /** + * Fired when a process is unscheduled. + * + * @param p the PID (string form) of the process that has been + * unscheduled. + */ + probe process__unscheduled(char *p); + + /** + * Fired when a process goes into hibernation. + * + * @param p the PID (string form) of the process entering hibernation + * @param mfa the m:f/a of the location to resume + */ + probe process__hibernate(char *p, char *mfa); + + /** + * Fired when a process is unblocked after a port has been unblocked. + * + * @param p the PID (string form) of the process that has been + * unscheduled. + * @param port the port that is no longer busy (i.e., is now unblocked) + */ + probe process__port_unblocked(char *p, char *port); + + /** + * Fired when process' heap is growing. + * + * @param p the PID (string form) + * @param old_size the size of the old heap + * @param new_size the size of the new heap + */ + probe process__heap_grow(char *p, int old_size, int new_size); + + /** + * Fired when process' heap is shrinking. + * + * @param p the PID (string form) + * @param old_size the size of the old heap + * @param new_size the size of the new heap + */ + probe process__heap_shrink(char *p, int old_size, int new_size); + + /* network distribution */ + + /** + * Fired when network distribution event monitor events are triggered. + * + * @param node the name of the reporting node + * @param what the type of event, e.g., nodeup, nodedown + * @param monitored_node the name of the monitored node + * @param type the type of node, e.g., visible, hidden + * @param reason the reason term, e.g., normal, connection_closed, term() + */ + probe dist__monitor(char *node, char *what, char *monitored_node, + char *type, char *reason); + + /** + * Fired when network distribution port is busy (i.e. blocked), + * usually due to the remote node not consuming distribution + * data quickly enough. + * + * @param node the name of the reporting node + * @param port the port ID of the busy port + * @param remote_node the name of the remote node. + * @param pid the PID (string form) of the local process that has + * become unschedulable until the port becomes unblocked. + */ + probe dist__port_busy(char *node, char *port, char *remote_node, + char *pid); + + /** + * Fired when network distribution's driver's "output" callback is called + * + * @param node the name of the reporting node + * @param port the port ID of the busy port + * @param remote_node the name of the remote node. + * @param bytes the number of bytes written + */ + probe dist__output(char *node, char *port, char *remote_node, int bytes); + + /** + * Fired when network distribution's driver's "outputv" callback is called + * + * @param node the name of the reporting node + * @param port the port ID of the busy port + * @param remote_node the name of the remote node. + * @param bytes the number of bytes written + */ + probe dist__outputv(char *node, char *port, char *remote_node, int bytes); + + /** + * Fired when network distribution port is no longer busy (i.e. blocked). + * + * NOTE: This probe may fire multiple times after the same single + * dist-port_busy probe firing. + * + * @param node the name of the reporting node + * @param port the port ID of the busy port + * @param remote_node the name of the remote node. + */ + probe dist__port_not_busy(char *node, char *port, char *remote_node); + + /* ports */ + + /** + * Fired when new port is opened. + * + * @param process the PID (string form) + * @param port_name the string used when the port was opened + * @param port the Port (string form) of the new port + */ + probe port__open(char *process, char *port_name, char *port); + + /** + * Fired when port_command is issued. + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param command_type type of the issued command, one of: "close", "command" or "connect" + */ + probe port__command(char *process, char *port, char *port_name, char *command_type); + + /** + * Fired when port_control is issued. + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param command_no command number that has been issued to the port + */ + probe port__control(char *process, char *port, char *port_name, int command_no); + + /** + * Fired when port is closed via port_close/1 (reason = 'normal') + * or is sent an exit signal. + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param reason Erlang term representing the exit signal, e.g. 'normal' + */ + probe port__exit(char *process, char *port, char *port_name, + char *new_process); + + /** + * Fired when port_connect is issued. + * + * @param process the PID (string form) of the current port owner + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param new_process the PID (string form) of the new port owner + */ + probe port__connect(char *process, char *port, char *port_name, + char *new_process); + + /** + * Fired when a port is busy (i.e. blocked) + * + * @param port the port ID of the busy port + */ + probe port__busy(char *port); + + /** + * Fired when a port is no longer busy (i.e. no longer blocked) + * + * @param port the port ID of the not busy port + */ + probe port__not_busy(char *port); + + /* drivers */ + + /** + * Fired when drivers's "init" callback is called. + * + * @param name the name of the driver + * @param major the major version number + * @param minor the minor version number + * @param flags the flags argument + */ + probe driver__init(char *name, int major, int minor, int flags); + + /** + * Fired when drivers's "start" callback is called. + * + * @param process the PID (string form) of the calling process + * @param name the name of the driver + * @param port the Port (string form) of the driver's port + */ + probe driver__start(char *process, char *name, char *port); + + /** + * Fired when drivers's "stop" callback is called. + * + * @param process the PID (string form) of the calling process + * @param name the name of the driver + * @param port the Port (string form) of the driver's port + */ + probe driver__stop(char *process, char *name, char *port); + + /** + * Fired when drivers's "finish" callback is called. + * + * @param name the name of the driver + */ + probe driver__finish(char *name); + + /** + * Fired when drivers's "flush" callback is called. + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__flush(char *process, char *port, char *port_name); + + /** + * Fired when driver's "output" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param bytes the number of bytes written + */ + probe driver__output(char *node, char *port, char *port_name, int bytes); + + /** + * Fired when driver's "outputv" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param bytes the number of bytes written + */ + probe driver__outputv(char *node, char *port, char *port_name, int bytes); + + /** + * Fired when driver's "control" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param command the command # + * @param bytes the number of bytes written + */ + probe driver__control(char *node, char *port, char *port_name, + int command, int bytes); + + /** + * Fired when driver's "call" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + * @param command the command # + * @param bytes the number of bytes written + */ + probe driver__call(char *node, char *port, char *port_name, + int command, int bytes); + + /** + * Fired when driver's "event" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__event(char *node, char *port, char *port_name); + + /** + * Fired when driver's "ready_input" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__ready_input(char *node, char *port, char *port_name); + + /** + * Fired when driver's "read_output" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__ready_output(char *node, char *port, char *port_name); + + /** + * Fired when driver's "timeout" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__timeout(char *node, char *port, char *port_name); + + /** + * Fired when drivers's "ready_async" callback is called. + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__ready_async(char *process, char *port, char *port_name); + + /** + * Fired when driver's "process_exit" callback is called + * + * @param process the PID (string form) + * @param port the Port (string form) + * @param port_name the string used when the port was opened + */ + probe driver__process_exit(char *node, char *port, char *port_name); + + /** + * Fired when driver's "stop_select" callback is called + * + * @param name the name of the driver + */ + probe driver__stop_select(char *name); + + + /* Async driver pool */ + + /** + * Show the post-add length of the async driver thread pool member's queue. + * + * NOTE: The port name is not available: additional lock(s) must + * be acquired in order to get the port name safely in an SMP + * environment. The same is true for the aio__pool_get probe. + * + * @param port the Port (string form) + * @param new queue length + */ + probe aio_pool__add(char *, int); + + /** + * Show the post-get length of the async driver thread pool member's queue. + * + * @param port the Port (string form) + * @param new queue length + */ + probe aio_pool__get(char *, int); + + /* Probes for efile_drv.c */ + + /** + * Entry into the efile_drv.c file I/O driver + * + * For a list of command numbers used by this driver, see the section + * "Guide to probe arguments" in ../../../README.md. That section + * also contains explanation of the various integer and string + * arguments that may be present when any particular probe fires. + * + * NOTE: Not all Linux platforms (using SystemTap) can support + * arguments beyond arg9. + * + * + * TODO: Adding the port string, args[10], is a pain. Making that + * port string available to all the other efile_drv.c probes + * will be more pain. Is the pain worth it? If yes, then + * add them everywhere else and grit our teeth. If no, then + * rip it out. + * + * @param thread-id number of the scheduler Pthread arg0 + * @param tag number: {thread-id, tag} uniquely names a driver operation + * @param user-tag string arg2 + * @param command number arg3 + * @param string argument 1 arg4 + * @param string argument 2 arg5 + * @param integer argument 1 arg6 + * @param integer argument 2 arg7 + * @param integer argument 3 arg8 + * @param integer argument 4 arg9 + * @param port the port ID of the busy port args[10] + */ + probe efile_drv__entry(int, int, char *, int, char *, char *, + int64_t, int64_t, int64_t, int64_t, char *); + + /** + * Entry into the driver's internal work function. Computation here + * is performed by a async worker pool Pthread. + * + * @param thread-id number + * @param tag number + * @param command number + */ + probe efile_drv__int_entry(int, int, int); + + /** + * Return from the driver's internal work function. + * + * @param thread-id number + * @param tag number + * @param command number + */ + probe efile_drv__int_return(int, int, int); + + /** + * Return from the efile_drv.c file I/O driver + * + * @param thread-id number arg0 + * @param tag number arg1 + * @param user-tag string arg2 + * @param command number arg3 + * @param Success? 1 is success, 0 is failure arg4 + * @param If failure, the errno of the error. arg5 + */ + probe efile_drv__return(int, int, char *, int, int, int); + +/* + * NOTE: + * For formatting int64_t arguments within a D script, see: + * + * http://mail.opensolaris.org/pipermail/dtrace-discuss/2006-November/002830.html + * Summary: + * "1) you don't need the 'l' printf() modifiers with DTrace ever" + */ + +/* + * NOTE: For file_drv_return + SMP + R14B03 (and perhaps other + * releases), the sched-thread-id will be the same as the + * work-thread-id: erl_async.c's async_main() function + * will call the asynchronous invoke function and then + * immediately call the drivers ready_async function while + * inside the same I/O worker pool thread. + * For R14B03's source, see erl_async.c lines 302-317. + */ +}; + +#pragma D attributes Evolving/Evolving/Common provider erlang provider +#pragma D attributes Private/Private/Common provider erlang module +#pragma D attributes Private/Private/Common provider erlang function +#pragma D attributes Evolving/Evolving/Common provider erlang name +#pragma D attributes Evolving/Evolving/Common provider erlang args diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index f1335f600d..b000e2c5d4 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -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 @@ -806,6 +806,8 @@ do { \ /* Port uses port specific locking (opposed to driver specific locking) */ #define ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK ((Uint32) (1 << 13)) #define ERTS_PORT_SFLG_INVALID ((Uint32) (1 << 14)) +/* Last port to terminate halts the emulator */ +#define ERTS_PORT_SFLG_HALT ((Uint32) (1 << 15)) #ifdef DEBUG /* Only debug: make sure all flags aren't cleared unintentionally */ #define ERTS_PORT_SFLG_PORT_DEBUG ((Uint32) (1 << 31)) @@ -899,14 +901,9 @@ void loaded(int, void *); /* config.c */ __decl_noreturn void __noreturn erl_exit(int n, char*, ...); -__decl_noreturn void __noreturn erl_exit0(char *, int, int n, char*, ...); +__decl_noreturn void __noreturn erl_exit_flush_async(int n, char*, ...); void erl_error(char*, va_list); -#define ERL_EXIT0(n,f) erl_exit0(__FILE__, __LINE__, n, f) -#define ERL_EXIT1(n,f,a) erl_exit0(__FILE__, __LINE__, n, f, a) -#define ERL_EXIT2(n,f,a,b) erl_exit0(__FILE__, __LINE__, n, f, a, b) -#define ERL_EXIT3(n,f,a,b,c) erl_exit0(__FILE__, __LINE__, n, f, a, b, c) - /* copy.c */ void init_copy(void); Eterm copy_object(Eterm, Process*); @@ -1977,4 +1974,46 @@ erts_alloc_message_heap(Uint size, # define UseTmpHeapNoproc(Size) /* Nothing */ # define UnUseTmpHeapNoproc(Size) /* Nothing */ #endif /* HEAP_ON_C_STACK */ + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +#include "dtrace-wrapper.h" + +ERTS_GLB_INLINE void +dtrace_pid_str(Eterm pid, char *process_buf) +{ + erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "<%lu.%lu.%lu>", + pid_channel_no(pid), + pid_number(pid), + pid_serial(pid)); +} + +ERTS_GLB_INLINE void +dtrace_proc_str(Process *process, char *process_buf) +{ + dtrace_pid_str(process->id, process_buf); +} + +ERTS_GLB_INLINE void +dtrace_port_str(Port *port, char *port_buf) +{ + erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>", + port_channel_no(port->id), + port_number(port->id)); +} + +ERTS_GLB_INLINE void +dtrace_fun_decode(Process *process, + Eterm module, Eterm function, int arity, + char *process_buf, char *mfa_buf) +{ + if (process_buf) { + dtrace_proc_str(process, process_buf); + } + + erts_snprintf(mfa_buf, DTRACE_TERM_BUF_SIZE, "%T:%T/%d", + module, function, arity); +} +#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ + #endif /* !__GLOBAL_H__ */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index fe1a7ba345..8a2a43bebd 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -43,6 +43,7 @@ #include "erl_version.h" #include "error.h" #include "erl_async.h" +#include "dtrace-wrapper.h" extern ErlDrvEntry fd_driver_entry; extern ErlDrvEntry vanilla_driver_entry; @@ -180,6 +181,20 @@ typedef struct line_buf_context { #define LINEBUF_INITIAL 100 +#ifdef USE_VM_PROBES +#define DTRACE_FORMAT_COMMON_PID_AND_PORT(PID, PORT) \ + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); \ + \ + dtrace_pid_str((PID), process_str); \ + dtrace_port_str((PORT), port_str); +#define DTRACE_FORMAT_COMMON_PROC_AND_PORT(PID, PORT) \ + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); \ + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); \ + \ + dtrace_proc_str((PID), process_str); \ + dtrace_port_str((PORT), port_str); +#endif /* The 'number' field in a port now has two parts: the lowest bits contain the index in the port table, and the higher bits are a counter @@ -639,6 +654,12 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ trace_sched_ports_where(port, am_in, am_start); } port->caller = pid; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_start)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(pid, port) + DTRACE3(driver_start, process_str, driver->name, port_str); + } +#endif fpe_was_unmasked = erts_block_fpe(); drv_data = (*driver->start)((ErlDrvPort)(port_ix), name, opts); @@ -1170,6 +1191,12 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) ev.size = size; /* total size */ ev.iov = ivp; ev.binv = bvp; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_outputv)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p) + DTRACE4(driver_outputv, process_str, port_str, p->name, size); + } +#endif fpe_was_unmasked = erts_block_fpe(); (*drv->outputv)((ErlDrvData)p->drv_data, &ev); erts_unblock_fpe(fpe_was_unmasked); @@ -1189,8 +1216,21 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) buf = erts_alloc(ERTS_ALC_T_TMP, size+1); r = io_list_to_buf(list, buf, size); +#ifdef USE_VM_PROBES + if(DTRACE_ENABLED(port_command)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p) + DTRACE4(port_command, process_str, port_str, p->name, "command"); + } +#endif + if (r >= 0) { size -= r; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_output)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p) + DTRACE4(driver_output, process_str, port_str, p->name, size); + } +#endif fpe_was_unmasked = erts_block_fpe(); (*drv->output)((ErlDrvData)p->drv_data, buf, size); erts_unblock_fpe(fpe_was_unmasked); @@ -1214,6 +1254,12 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) */ buf = erts_alloc(ERTS_ALC_T_TMP, size+1); r = io_list_to_buf(list, buf, size); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_output)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p) + DTRACE4(driver_output, process_str, port_str, p->name, size); + } +#endif fpe_was_unmasked = erts_block_fpe(); (*drv->output)((ErlDrvData)p->drv_data, buf, size); erts_unblock_fpe(fpe_was_unmasked); @@ -1529,7 +1575,11 @@ deliver_result(Eterm sender, Eterm pid, Eterm res) hp = erts_alloc_message_heap(sz_res + 3, &bp, &ohp, rp, &rp_locks); res = copy_struct(res, sz_res, &hp, ohp); tuple = TUPLE2(hp, sender, res); - erts_queue_message(rp, &rp_locks, bp, tuple, NIL); + erts_queue_message(rp, &rp_locks, bp, tuple, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(rp, rp_locks); erts_smp_proc_dec_refc(rp); } @@ -1618,7 +1668,11 @@ static void deliver_read_message(Port* prt, Eterm to, tuple = TUPLE2(hp, prt->id, tuple); hp += 3; - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); + erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(rp, rp_locks); erts_smp_proc_dec_refc(rp); } @@ -1771,7 +1825,11 @@ deliver_vec_message(Port* prt, /* Port */ tuple = TUPLE2(hp, prt->id, tuple); hp += 3; - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); + erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(rp, rp_locks); erts_smp_proc_dec_refc(rp); } @@ -1810,6 +1868,12 @@ static void flush_port(Port *p) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); if (p->drv_ptr->flush != NULL) { +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_flush)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(p->connected, p) + DTRACE3(driver_flush, process_str, port_str, p->name); + } +#endif if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) { trace_sched_ports_where(p, am_in, am_flush); } @@ -1837,6 +1901,7 @@ terminate_port(Port *prt) Eterm send_closed_port_id; Eterm connected_id = NIL /* Initialize to silence compiler */; erts_driver_t *drv; + int halt; ERTS_SMP_CHK_NO_PROC_LOCKS; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); @@ -1844,6 +1909,8 @@ terminate_port(Port *prt) ASSERT(!prt->nlinks); ASSERT(!prt->monitors); + /* prt->status may be altered by kill_port()below */ + halt = (prt->status & ERTS_PORT_SFLG_HALT) != 0; if (prt->status & ERTS_PORT_SFLG_SEND_CLOSED) { erts_port_status_band_set(prt, ~ERTS_PORT_SFLG_SEND_CLOSED); send_closed_port_id = prt->id; @@ -1862,6 +1929,12 @@ terminate_port(Port *prt) drv = prt->drv_ptr; if ((drv != NULL) && (drv->stop != NULL)) { int fpe_was_unmasked = erts_block_fpe(); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_stop)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(prt->connected, prt) + DTRACE3(driver_stop, process_str, drv->name, port_str); + } +#endif (*drv->stop)((ErlDrvData)prt->drv_data); erts_unblock_fpe(fpe_was_unmasked); #ifdef ERTS_SMP @@ -1895,6 +1968,10 @@ terminate_port(Port *prt) * We don't want to send the closed message until after the * port has been removed from the port table (in kill_port()). */ + if (halt && (erts_smp_atomic32_dec_read_nob(&erts_halt_progress) == 0)) { + erts_smp_port_unlock(prt); /* We will exit and never return */ + erl_exit_flush_async(erts_halt_code, ""); + } if (is_internal_port(send_closed_port_id)) deliver_result(send_closed_port_id, connected_id, am_closed); @@ -2019,6 +2096,19 @@ erts_do_exit_port(Port *p, Eterm from, Eterm reason) rreason = (reason == am_kill) ? am_killed : reason; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_exit)) { + DTRACE_CHARBUF(from_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(rreason_str, 64); + + erts_snprintf(from_str, sizeof(from_str), "%T", from); + dtrace_port_str(p, port_str); + erts_snprintf(rreason_str, sizeof(rreason_str), "%T", rreason); + DTRACE4(port_exit, from_str, port_str, p->name, rreason_str); + } +#endif + if ((p->status & (ERTS_PORT_SFLGS_DEAD | ERTS_PORT_SFLG_EXITING | ERTS_PORT_SFLG_IMMORTAL)) @@ -2119,6 +2209,13 @@ void erts_port_command(Process *proc, if (tp[2] == am_close) { erts_port_status_bor_set(port, ERTS_PORT_SFLG_SEND_CLOSED); erts_do_exit_port(port, pid, am_normal); + +#ifdef USE_VM_PROBES + if(DTRACE_ENABLED(port_command)) { + DTRACE_FORMAT_COMMON_PROC_AND_PORT(proc, port) + DTRACE4(port_command, process_str, port_str, port->name, "close"); + } +#endif goto done; } else if (is_tuple_arity(tp[2], 2)) { tp = tuple_val(tp[2]); @@ -2126,6 +2223,12 @@ void erts_port_command(Process *proc, if (erts_write_to_port(caller_id, port, tp[2]) == 0) goto done; } else if ((tp[1] == am_connect) && is_internal_pid(tp[2])) { +#ifdef USE_VM_PROBES + if(DTRACE_ENABLED(port_command)) { + DTRACE_FORMAT_COMMON_PROC_AND_PORT(proc, port) + DTRACE4(port_command, process_str, port_str, port->name, "connect"); + } +#endif port->connected = tp[2]; deliver_result(port->id, pid, am_connected); goto done; @@ -2228,6 +2331,15 @@ erts_port_control(Process* p, Port* prt, Uint command, Eterm iolist) erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN); ERTS_SMP_CHK_NO_PROC_LOCKS; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_control) || DTRACE_ENABLED(driver_control)) { + DTRACE_FORMAT_COMMON_PROC_AND_PORT(p, prt); + DTRACE4(port_control, process_str, port_str, prt->name, command); + DTRACE5(driver_control, process_str, port_str, prt->name, + command, to_len); + } +#endif + /* * Call the port's control routine. */ @@ -2368,6 +2480,10 @@ print_port_info(int to, void *arg, int i) void set_busy_port(ErlDrvPort port_num, int on) { +#ifdef USE_VM_PROBES + DTRACE_CHARBUF(port_str, 16); +#endif + ERTS_SMP_CHK_NO_PROC_LOCKS; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(&erts_port[port_num])); @@ -2375,12 +2491,26 @@ set_busy_port(ErlDrvPort port_num, int on) if (on) { erts_port_status_bor_set(&erts_port[port_num], ERTS_PORT_SFLG_PORT_BUSY); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_busy)) { + erts_snprintf(port_str, sizeof(port_str), + "%T", erts_port[port_num].id); + DTRACE1(port_busy, port_str); + } +#endif } else { ErtsProcList* plp = erts_port[port_num].suspended; erts_port_status_band_set(&erts_port[port_num], ~ERTS_PORT_SFLG_PORT_BUSY); erts_port[port_num].suspended = NULL; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_not_busy)) { + erts_snprintf(port_str, sizeof(port_str), + "%T", erts_port[port_num].id); + DTRACE1(port_not_busy, port_str); + } +#endif if (erts_port[port_num].dist_entry) { /* * Processes suspended on distribution ports are @@ -2398,6 +2528,28 @@ set_busy_port(ErlDrvPort port_num, int on) */ if (plp) { +#ifdef USE_VM_PROBES + /* + * Hrm, for blocked dist ports, plp always seems to be NULL. + * That's not so fun. + * Well, another way to get the same info is using a D + * script to correlate an earlier process-port_blocked+pid + * event with a later process-scheduled event. That's + * subject to the multi-CPU races with how events are + * handled, but hey, that way works most of the time. + */ + if (DTRACE_ENABLED(process_port_unblocked)) { + DTRACE_CHARBUF(pid_str, 16); + ErtsProcList* plp2 = plp; + + erts_snprintf(port_str, sizeof(port_str), + "%T", erts_port[port_num]); + while (plp2 != NULL) { + erts_snprintf(pid_str, sizeof(pid_str), "%T", plp2->pid); + DTRACE2(process_port_unblocked, pid_str, port_str); + } + } +#endif /* First proc should be resumed last */ if (plp->next) { erts_resume_processes(plp->next); @@ -2444,6 +2596,14 @@ void erts_raw_port_command(Port* p, byte* buf, Uint len) p->drv_ptr->name ? p->drv_ptr->name : "unknown"); p->caller = NIL; +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_output)) { + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + + dtrace_port_str(p, port_str); + DTRACE4(driver_output, "-raw-", port_str, p->name, len); + } +#endif fpe_was_unmasked = erts_block_fpe(); (*p->drv_ptr->output)((ErlDrvData)p->drv_data, (char*) buf, (int) len); erts_unblock_fpe(fpe_was_unmasked); @@ -2459,6 +2619,12 @@ int async_ready(Port *p, void* data) ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); ASSERT(!(p->status & ERTS_PORT_SFLGS_DEAD)); if (p->drv_ptr->ready_async != NULL) { +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_ready_async)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(p->connected, p) + DTRACE3(driver_ready_async, process_str, port_str, p->name); + } +#endif (*p->drv_ptr->ready_async)((ErlDrvData)p->drv_data, data); need_free = 0; #ifdef ERTS_SMP @@ -2653,7 +2819,11 @@ void driver_report_exit(int ix, int status) hp += 3; tuple = TUPLE2(hp, prt->id, tuple); - erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined); + erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); erts_smp_proc_unlock(rp, rp_locks); erts_smp_proc_dec_refc(rp); @@ -3203,7 +3373,11 @@ driver_deliver_term(ErlDrvPort port, HRelease(rp, hp_end, hp); } /* send message */ - erts_queue_message(rp, &rp_locks, bp, mess, am_undefined); + erts_queue_message(rp, &rp_locks, bp, mess, am_undefined +#ifdef USE_VM_PROBES + , NIL +#endif + ); } else { if (b2t.ix > b2t.used) @@ -4434,6 +4608,12 @@ void erts_fire_port_monitor(Port *prt, Eterm ref) ASSERT(callback != NULL); ref_to_driver_monitor(ref,&drv_monitor); DRV_MONITOR_UNLOCK_PDL(prt); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(driver_process_exit)) { + DTRACE_FORMAT_COMMON_PID_AND_PORT(prt->connected, prt) + DTRACE3(driver_process_exit, process_str, port_str, prt->name); + } +#endif fpe_was_unmasked = erts_block_fpe(); (*callback)((ErlDrvData) (prt->drv_data), &drv_monitor); erts_unblock_fpe(fpe_was_unmasked); @@ -4877,6 +5057,8 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) else { int res; int fpe_was_unmasked = erts_block_fpe(); + DTRACE4(driver_init, drv->name, drv->version.major, drv->version.minor, + drv->flags); res = (*de->init)(); erts_unblock_fpe(fpe_was_unmasked); return res; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index fc53a88a3a..b2fc571032 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -883,6 +883,95 @@ call_ext_last u==3 u$func:erlang:hibernate/3 D => i_hibernate call_ext_only u==3 u$func:erlang:hibernate/3 => i_hibernate # +# If VM probes are not enabled, we want to short-circult calls to +# the dt tag BIFs to make them as cheap as possible. +# + +%unless USE_VM_PROBES + +call_ext Arity u$func:erlang:dt_get_tag/0 => \ + move a=am_undefined r +call_ext_last Arity u$func:erlang:dt_get_tag/0 D => \ + move a=am_undefined r | deallocate D | return +call_ext_only Arity u$func:erlang:dt_get_tag/0 => \ + move a=am_undefined r | return + +move Any r | call_ext Arity u$func:erlang:dt_put_tag/1 => \ + move a=am_undefined r +move Any r | call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \ + move a=am_undefined r | deallocate D | return +move Any r | call_ext_only Arity u$func:erlang:dt_put_tag/1 => \ + move a=am_undefined r | return +call_ext Arity u$func:erlang:dt_put_tag/1 => \ + move a=am_undefined r +call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \ + move a=am_undefined r | deallocate D | return +call_ext_only Arity u$func:erlang:dt_put_tag/1 => \ + move a=am_undefined r | return + +call_ext Arity u$func:erlang:dt_get_tag_data/0 => \ + move a=am_undefined r +call_ext_last Arity u$func:erlang:dt_get_tag_data/0 D => \ + move a=am_undefined r | deallocate D | return +call_ext_only Arity u$func:erlang:dt_get_tag_data/0 => \ + move a=am_undefined r | return + +move Any r | call_ext Arity u$func:erlang:dt_spread_tag/1 => \ + move a=am_true r +move Any r | call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \ + move a=am_true r | deallocate D | return +move Any r | call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \ + move a=am_true r | return +call_ext Arity u$func:erlang:dt_spread_tag/1 => \ + move a=am_true r +call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \ + move a=am_true r | deallocate D | return +call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \ + move a=am_true r | return + +move Any r | call_ext Arity u$func:erlang:dt_restore_tag/1 => \ + move a=am_true r +move Any r | call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \ + move a=am_true r | deallocate D | return +move Any r | call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \ + move a=am_true r | return +call_ext Arity u$func:erlang:dt_restore_tag/1 => \ + move a=am_true r +call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \ + move a=am_true r | deallocate D | return +call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \ + move a=am_true r | return + +move Any r | call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ + move Any r +move Any r | call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \ + move Any r | deallocate D | return +move Any r | call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ + move Any r | return +call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => +call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \ + deallocate D | return +call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \ + return + +move Any r | call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => \ + move Any r +move Any r | call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \ + move Any r | deallocate D | return +move Any r | call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \ + move Any r | return +call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => +call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \ + deallocate D | return +call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \ + return + +# Can happen after one of the transformations above. +move Discarded r | move Something r => move Something r + +%endif + +# # The general case for BIFs that have no special instructions. # A BIF used in the tail must be followed by a return instruction. # diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index eb6f2f8516..7b2bb81f62 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -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 @@ -511,7 +511,7 @@ __decl_noreturn void __noreturn erl_exit(int n, char*, ...); /* Some special erl_exit() codes: */ #define ERTS_INTR_EXIT INT_MIN /* called from signal handler */ #define ERTS_ABORT_EXIT (INT_MIN + 1) /* no crash dump; only abort() */ -#define ERTS_DUMP_EXIT (127) /* crash dump; then exit() */ +#define ERTS_DUMP_EXIT (INT_MIN + 2) /* crash dump; then exit() */ Eterm erts_check_io_info(void *p); diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 49b6618f73..5ab51fab50 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1697,7 +1697,11 @@ static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len) erts_queue_error_logger_message(from, tuple3, bp); } #else - erts_queue_message(p, NULL /* only used for smp build */, bp, tuple3, NIL); + erts_queue_message(p, NULL /* only used for smp build */, bp, tuple3, NIL +#ifdef USE_VM_PROBES + , NIL +#endif + ); #endif return 0; } diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index a251b064da..b33a8d210b 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -100,7 +100,7 @@ #endif #include <stdlib.h> -// Need (NON)BLOCKING macros for sendfile +/* Need (NON)BLOCKING macros for sendfile */ #ifndef WANT_NONBLOCKING #define WANT_NONBLOCKING #endif @@ -112,6 +112,7 @@ #include "erl_threads.h" #include "zlib.h" #include "gzio.h" +#include "dtrace-wrapper.h" #include <ctype.h> #include <sys/types.h> @@ -119,6 +120,39 @@ void erl_exit(int n, char *fmt, ...); static ErlDrvSysInfo sys_info; +/* For explanation of this var, see comment for same var in erl_async.c */ +static unsigned gcc_optimizer_hack = 0; + +#ifdef USE_VM_PROBES + +#define DTRACE_EFILE_BUFSIZ 128 + +#define DTRACE_INVOKE_SETUP(op) \ + do { DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, op); } while (0) +#define DTRACE_INVOKE_SETUP_BY_NAME(op) \ + struct t_data *d = (struct t_data *) data ; \ + DTRACE_INVOKE_SETUP(op) +#define DTRACE_INVOKE_RETURN(op) \ + do { DTRACE3(efile_drv_int_return, d->sched_i1, d->sched_i2, \ + op); } while (0) ; gcc_optimizer_hack++ ; + +/* Assign human-friendlier id numbers to scheduler & I/O worker threads */ +int dt_driver_idnum = 0; +int dt_driver_io_worker_base = 5000; +erts_mtx_t dt_driver_mutex; +pthread_key_t dt_driver_key; + +typedef struct { + int thread_num; + Uint64 tag; +} dt_private; + +dt_private *get_dt_private(int); +#else /* USE_VM_PROBES */ +#define DTRACE_INVOKE_SETUP(op) do {} while (0) +#define DTRACE_INVOKE_SETUP_BY_NAME(op) do {} while (0) +#define DTRACE_INVOKE_RETURN(op) do {} while (0) +#endif /* USE_VM_PROBES */ /* #define TRACE 1 */ #ifdef TRACE @@ -174,6 +208,9 @@ static ErlDrvSysInfo sys_info; #ifdef FILENAMES_16BIT +#ifdef USE_VM_PROBES +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif # define FILENAME_BYTELEN(Str) filename_len_16bit(Str) # define FILENAME_COPY(To,From) filename_cpy_16bit((To),(From)) # define FILENAME_CHARSIZE 2 @@ -289,6 +326,10 @@ typedef struct { ErlDrvPDL q_mtx; /* Mutex for the driver queue, known by the emulator. Also used for mutual exclusion when accessing field(s) below. */ size_t write_buffered; +#ifdef USE_VM_PROBES + int idnum; /* Unique ID # for this driver thread/desc */ + char port_str[DTRACE_TERM_BUF_SIZE]; +#endif } file_descriptor; @@ -386,6 +427,11 @@ struct t_data void (*free)(void *); int again; int reply; +#ifdef USE_VM_PROBES + int sched_i1; + Uint64 sched_i2; + char sched_utag[DTRACE_EFILE_BUFSIZ+1]; +#endif int result_ok; Efile_error errInfo; int flags; @@ -458,8 +504,6 @@ struct t_data char b[1]; }; - - #define EF_ALLOC(S) driver_alloc((S)) #define EF_REALLOC(P, S) driver_realloc((P), (S)) #define EF_SAFE_ALLOC(S) ef_safe_alloc((S)) @@ -488,7 +532,7 @@ static void *ef_safe_realloc(void *op, Uint s) * ErlIOVec manipulation functions. */ -/* char EV_CHAR(ErlIOVec *ev, int p, int q) */ +/* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */ #define EV_CHAR_P(ev, p, q) \ (((char *)(ev)->iov[(q)].iov_base) + (p)) @@ -683,6 +727,11 @@ file_init(void) : 0); driver_system_info(&sys_info, sizeof(ErlDrvSysInfo)); +#ifdef USE_VM_PROBES + erts_mtx_init(&dt_driver_mutex, "efile_drv dtrace mutex"); + pthread_key_create(&dt_driver_key, NULL); +#endif /* USE_VM_PROBES */ + return 0; } @@ -722,6 +771,10 @@ file_start(ErlDrvPort port, char* command) desc->write_error = 0; MUTEX_INIT(desc->q_mtx, port); /* Refc is one, referenced by emulator now */ desc->write_buffered = 0; +#ifdef USE_VM_PROBES + dtrace_drvport_str(port, desc->port_str); + get_dt_private(0); /* throw away return value */ +#endif /* USE_VM_PROBES */ return (ErlDrvData) desc; } @@ -741,8 +794,10 @@ static void do_close(int flags, SWord fd) { static void invoke_close(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE_INVOKE_SETUP(FILE_CLOSE); d->again = 0; do_close(d->flags, d->fd); + DTRACE_INVOKE_RETURN(FILE_CLOSE); } /********************************************************************* @@ -972,49 +1027,63 @@ static void invoke_name(void *data, int (*f)(Efile_error *, char *)) static void invoke_mkdir(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_MKDIR); invoke_name(data, efile_mkdir); + DTRACE_INVOKE_RETURN(FILE_MKDIR); } static void invoke_rmdir(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_RMDIR); invoke_name(data, efile_rmdir); + DTRACE_INVOKE_RETURN(FILE_RMDIR); } static void invoke_delete_file(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_DELETE); invoke_name(data, efile_delete_file); + DTRACE_INVOKE_RETURN(FILE_DELETE); } static void invoke_chdir(void *data) { + DTRACE_INVOKE_SETUP_BY_NAME(FILE_CHDIR); invoke_name(data, efile_chdir); + DTRACE_INVOKE_RETURN(FILE_CHDIR); } static void invoke_fdatasync(void *data) { struct t_data *d = (struct t_data *) data; int fd = (int) d->fd; + DTRACE_INVOKE_SETUP(FILE_FDATASYNC); d->again = 0; d->result_ok = efile_fdatasync(&d->errInfo, fd); + DTRACE_INVOKE_RETURN(FILE_FDATASYNC); } static void invoke_fsync(void *data) { struct t_data *d = (struct t_data *) data; int fd = (int) d->fd; + DTRACE_INVOKE_SETUP(FILE_FSYNC); d->again = 0; d->result_ok = efile_fsync(&d->errInfo, fd); + DTRACE_INVOKE_RETURN(FILE_FSYNC); } static void invoke_truncate(void *data) { struct t_data *d = (struct t_data *) data; int fd = (int) d->fd; + DTRACE_INVOKE_SETUP(FILE_TRUNCATE); d->again = 0; d->result_ok = efile_truncate_file(&d->errInfo, &fd, d->flags); + DTRACE_INVOKE_RETURN(FILE_TRUNCATE); } static void invoke_read(void *data) @@ -1022,6 +1091,7 @@ static void invoke_read(void *data) struct t_data *d = (struct t_data *) data; int status, segment; size_t size, read_size; + DTRACE_INVOKE_SETUP(FILE_READ); segment = d->again && d->c.read.bin_size >= 2*FILE_SEGMENT_READ; if (segment) { @@ -1056,6 +1126,7 @@ static void invoke_read(void *data) } else { d->again = 0; } + DTRACE_INVOKE_RETURN(FILE_READ); } static void free_read(void *data) @@ -1072,6 +1143,7 @@ static void invoke_read_line(void *data) int status; size_t read_size; int local_loop = (d->again == 0); + DTRACE_INVOKE_SETUP(FILE_READ_LINE); do { size_t size = (d->c.read_line.binp)->orig_size - @@ -1163,6 +1235,7 @@ static void invoke_read_line(void *data) break; } } while (local_loop); + DTRACE_INVOKE_RETURN(FILE_READ_LINE); } static void free_read_line(void *data) @@ -1178,6 +1251,7 @@ static void invoke_read_file(void *data) struct t_data *d = (struct t_data *) data; size_t read_size; int chop; + DTRACE_INVOKE_SETUP(FILE_READ_FILE); if (! d->c.read_file.binp) { /* First invocation only */ int fd; @@ -1214,12 +1288,14 @@ static void invoke_read_file(void *data) &read_size); if (d->result_ok) { d->c.read_file.offset += read_size; - if (chop) return; /* again */ + if (chop) goto chop_done; /* again */ } close: efile_closefile((int) d->fd); done: d->again = 0; + chop_done: + DTRACE_INVOKE_RETURN(FILE_READ_FILE); } static void free_read_file(void *data) @@ -1239,6 +1315,7 @@ static void invoke_preadv(void *data) ErlIOVec *ev = &c->eiov; size_t bytes_read_so_far = 0; unsigned char *p = (unsigned char *)ev->iov[0].iov_base + 4+4+8*c->cnt; + DTRACE_INVOKE_SETUP(FILE_PREADV); while (c->cnt < c->n) { size_t read_size = ev->iov[1 + c->cnt].iov_len - c->size; @@ -1260,7 +1337,7 @@ static void invoke_preadv(void *data) bytes_read_so_far += bytes_read; if (chop && bytes_read == read_size) { c->size += bytes_read; - return; + goto done; } ASSERT(bytes_read <= read_size); ev->iov[1 + c->cnt].iov_len = bytes_read + c->size; @@ -1271,7 +1348,7 @@ static void invoke_preadv(void *data) if (d->again && bytes_read_so_far >= FILE_SEGMENT_READ && c->cnt < c->n) { - return; + goto done; } } else { /* In case of a read error, ev->size will not be correct, @@ -1282,6 +1359,8 @@ static void invoke_preadv(void *data) } } d->again = 0; + done: + DTRACE_INVOKE_RETURN(FILE_PREADV); } static void free_preadv(void *data) { @@ -1303,6 +1382,7 @@ static void invoke_ipread(void *data) size_t bytes_read = 0; char buf[2*sizeof(Uint32)]; Uint32 offset, size; + DTRACE_INVOKE_SETUP(FILE_IPREAD); /* Read indirection header */ if (! efile_pread(&d->errInfo, (int) d->fd, c->offsets[0], @@ -1341,14 +1421,17 @@ static void invoke_ipread(void *data) /* Read data block */ d->invoke = invoke_preadv; invoke_preadv(data); + DTRACE_INVOKE_RETURN(FILE_IPREAD); return; error: d->result_ok = 0; d->again = 0; + DTRACE_INVOKE_RETURN(FILE_IPREAD); return; done: d->result_ok = !0; d->again = 0; + DTRACE_INVOKE_RETURN(FILE_IPREAD); } /* invoke_writev and invoke_pwritev are the only thread functions that @@ -1371,6 +1454,7 @@ static void invoke_writev(void *data) { size_t size; size_t p; int segment; + DTRACE_INVOKE_SETUP(FILE_WRITE); segment = d->again && d->c.writev.size >= 2*FILE_SEGMENT_WRITE; if (segment) { @@ -1444,6 +1528,7 @@ static void invoke_writev(void *data) { TRACE_F(("w%lu", (unsigned long)size)); } + DTRACE_INVOKE_RETURN(FILE_WRITE); } static void free_writev(void *data) { @@ -1457,34 +1542,40 @@ static void free_writev(void *data) { static void invoke_pwd(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE_INVOKE_SETUP(FILE_PWD); d->again = 0; d->result_ok = efile_getdcwd(&d->errInfo,d->drive, d->b+1, RESBUFSIZE-1); + DTRACE_INVOKE_RETURN(FILE_PWD); } static void invoke_readlink(void *data) { struct t_data *d = (struct t_data *) data; char resbuf[RESBUFSIZE]; /* Result buffer. */ + DTRACE_INVOKE_SETUP(FILE_READLINK); d->again = 0; d->result_ok = efile_readlink(&d->errInfo, d->b, resbuf+1, RESBUFSIZE-1); if (d->result_ok != 0) FILENAME_COPY((char *) d->b + 1, resbuf+1); + DTRACE_INVOKE_RETURN(FILE_READLINK); } static void invoke_altname(void *data) { struct t_data *d = (struct t_data *) data; char resbuf[RESBUFSIZE]; /* Result buffer. */ + DTRACE_INVOKE_SETUP(FILE_ALTNAME); d->again = 0; d->result_ok = efile_altname(&d->errInfo, d->b, resbuf+1, RESBUFSIZE-1); if (d->result_ok != 0) FILENAME_COPY((char *) d->b + 1, resbuf+1); + DTRACE_INVOKE_RETURN(FILE_ALTNAME); } static void invoke_pwritev(void *data) { @@ -1497,6 +1588,7 @@ static void invoke_pwritev(void *data) { size_t p; int segment; size_t size, write_size; + DTRACE_INVOKE_SETUP(FILE_PWRITEV); segment = d->again && c->size >= 2*FILE_SEGMENT_WRITE; if (segment) { @@ -1576,6 +1668,7 @@ static void invoke_pwritev(void *data) { } done: EF_FREE(iov); /* Free our copy of the vector, nothing to restore */ + DTRACE_INVOKE_RETURN(FILE_PWRITEV); } static void free_pwritev(void *data) { @@ -1591,9 +1684,14 @@ static void invoke_flstat(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, + d->command == FILE_LSTAT ? FILE_LSTAT : FILE_FSTAT); d->again = 0; d->result_ok = efile_fileinfo(&d->errInfo, &d->info, d->b, d->command == FILE_LSTAT); + DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, + d->command == FILE_LSTAT ? FILE_LSTAT : FILE_FSTAT); + gcc_optimizer_hack++; } static void invoke_link(void *data) @@ -1601,10 +1699,12 @@ static void invoke_link(void *data) struct t_data *d = (struct t_data *) data; char *name = d->b; char *new_name; + DTRACE_INVOKE_SETUP(FILE_LINK); d->again = 0; new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE; d->result_ok = efile_link(&d->errInfo, name, new_name); + DTRACE_INVOKE_RETURN(FILE_LINK); } static void invoke_symlink(void *data) @@ -1612,10 +1712,12 @@ static void invoke_symlink(void *data) struct t_data *d = (struct t_data *) data; char *name = d->b; char *new_name; + DTRACE_INVOKE_SETUP(FILE_SYMLINK); d->again = 0; new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE; d->result_ok = efile_symlink(&d->errInfo, name, new_name); + DTRACE_INVOKE_RETURN(FILE_SYMLINK); } static void invoke_rename(void *data) @@ -1623,24 +1725,29 @@ static void invoke_rename(void *data) struct t_data *d = (struct t_data *) data; char *name = d->b; char *new_name; + DTRACE_INVOKE_SETUP(FILE_RENAME); d->again = 0; new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE; d->result_ok = efile_rename(&d->errInfo, name, new_name); + DTRACE_INVOKE_RETURN(FILE_RENAME); } static void invoke_write_info(void *data) { struct t_data *d = (struct t_data *) data; + DTRACE_INVOKE_SETUP(FILE_WRITE_INFO); d->again = 0; d->result_ok = efile_write_info(&d->errInfo, &d->info, d->b); + DTRACE_INVOKE_RETURN(FILE_WRITE_INFO); } static void invoke_lseek(void *data) { struct t_data *d = (struct t_data *) data; int status; + DTRACE_INVOKE_SETUP(FILE_LSEEK); d->again = 0; if (d->flags & EFILE_COMPRESSED) { @@ -1665,6 +1772,7 @@ static void invoke_lseek(void *data) &d->c.lseek.location); } d->result_ok = status; + DTRACE_INVOKE_RETURN(FILE_LSEEK); } static void invoke_readdir(void *data) @@ -1675,6 +1783,7 @@ static void invoke_readdir(void *data) size_t n = 0, total = 0; struct t_readdir_buf *b = NULL; int res = 0; + DTRACE_INVOKE_SETUP(FILE_READDIR); d->again = 0; d->errInfo.posix_errno = 0; @@ -1710,13 +1819,14 @@ static void invoke_readdir(void *data) } while(res); d->result_ok = (d->errInfo.posix_errno == 0); + DTRACE_INVOKE_RETURN(FILE_READDIR); } static void invoke_open(void *data) { struct t_data *d = (struct t_data *) data; - int status = 1; /* Status of open call. */ + DTRACE_INVOKE_SETUP(FILE_OPEN); d->again = 0; if ((d->flags & EFILE_COMPRESSED) == 0) { @@ -1749,6 +1859,7 @@ static void invoke_open(void *data) } d->result_ok = status; + DTRACE_INVOKE_RETURN(FILE_OPEN); } static void invoke_fadvise(void *data) @@ -1758,9 +1869,11 @@ static void invoke_fadvise(void *data) off_t offset = (off_t) d->c.fadvise.offset; off_t length = (off_t) d->c.fadvise.length; int advise = (int) d->c.fadvise.advise; + DTRACE_INVOKE_SETUP(FILE_FADVISE); d->again = 0; d->result_ok = efile_fadvise(&d->errInfo, fd, offset, length, advise); + DTRACE_INVOKE_RETURN(FILE_FADVISE); } #ifdef HAVE_SENDFILE @@ -1841,6 +1954,7 @@ static void free_readdir(void *data) { struct t_data *d = (struct t_data *) data; struct t_readdir_buf *b1 = d->c.read_dir.first_buf; + while (b1) { struct t_readdir_buf *b2 = b1; b1 = b1->next; @@ -1909,12 +2023,16 @@ static void cq_execute(file_descriptor *desc) { DRIVER_ASYNC(d->level, desc, d->invoke, void_ptr=d, d->free); } -static int async_write(file_descriptor *desc, int *errp, - int reply, Uint32 reply_size) { +static struct t_data *async_write(file_descriptor *desc, int *errp, + int reply, Uint32 reply_size +#ifdef USE_VM_PROBES + ,Sint64 *dt_i1, Sint64 *dt_i2, Sint64 *dt_i3 +#endif +) { struct t_data *d; if (! (d = EF_ALLOC(sizeof(struct t_data) - 1))) { if (errp) *errp = ENOMEM; - return -1; + return NULL; } TRACE_F(("w%lu", (unsigned long)desc->write_buffered)); d->command = FILE_WRITE; @@ -1923,6 +2041,13 @@ static int async_write(file_descriptor *desc, int *errp, d->c.writev.port = desc->port; d->c.writev.q_mtx = desc->q_mtx; d->c.writev.size = desc->write_buffered; +#ifdef USE_VM_PROBES + if (dt_i1 != NULL) { + *dt_i1 = d->fd; + *dt_i2 = d->flags; + *dt_i3 = d->c.writev.size; + } +#endif d->reply = reply; d->c.writev.free_size = 0; d->c.writev.reply_size = reply_size; @@ -1931,18 +2056,49 @@ static int async_write(file_descriptor *desc, int *errp, d->level = 1; cq_enq(desc, d); desc->write_buffered = 0; - return 0; + return d; } -static int flush_write(file_descriptor *desc, int *errp) { - int result; +static int flush_write(file_descriptor *desc, int *errp +#ifdef USE_VM_PROBES + , dt_private *dt_priv, char *dt_utag +#endif +) { + int result = 0; +#ifdef USE_VM_PROBES + Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0; +#endif + struct t_data *d = NULL; + MUTEX_LOCK(desc->q_mtx); if (desc->write_buffered > 0) { - result = async_write(desc, errp, 0, 0); - } else { - result = 0; + if ((d = async_write(desc, errp, 0, 0 +#ifdef USE_VM_PROBES + ,&dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { + result = -1; + } } MUTEX_UNLOCK(desc->q_mtx); +#ifdef USE_VM_PROBES + if (d != NULL) { + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, FILE_WRITE, + NULL, NULL, dt_i1, dt_i2, dt_i3, 0, desc->port_str); + } +#endif /* USE_VM_PROBES */ return result; } @@ -1955,9 +2111,17 @@ static int check_write_error(file_descriptor *desc, int *errp) { return 0; } -static int flush_write_check_error(file_descriptor *desc, int *errp) { +static int flush_write_check_error(file_descriptor *desc, int *errp +#ifdef USE_VM_PROBES + , dt_private *dt_priv, char *dt_utag +#endif + ) { int r; - if ( (r = flush_write(desc, errp)) != 0) { + if ( (r = flush_write(desc, errp +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + )) != 0) { check_write_error(desc, NULL); return r; } else { @@ -1965,12 +2129,16 @@ static int flush_write_check_error(file_descriptor *desc, int *errp) { } } -static int async_lseek(file_descriptor *desc, int *errp, int reply, - Sint64 offset, int origin) { +static struct t_data *async_lseek(file_descriptor *desc, int *errp, int reply, + Sint64 offset, int origin +#ifdef USE_VM_PROBES + , Sint64 *dt_i1, Sint64 *dt_i2, Sint64 *dt_i3 +#endif + ) { struct t_data *d; if (! (d = EF_ALLOC(sizeof(struct t_data)))) { *errp = ENOMEM; - return -1; + return NULL; } d->flags = desc->flags; d->fd = desc->fd; @@ -1978,11 +2146,18 @@ static int async_lseek(file_descriptor *desc, int *errp, int reply, d->reply = reply; d->c.lseek.offset = offset; d->c.lseek.origin = origin; +#ifdef USE_VM_PROBES + if (dt_i1 != NULL) { + *dt_i1 = d->fd; + *dt_i2 = d->c.lseek.offset; + *dt_i3 = d->c.lseek.origin; + } +#endif d->invoke = invoke_lseek; d->free = free_data; d->level = 1; cq_enq(desc, d); - return 0; + return d; } static void flush_read(file_descriptor *desc) { @@ -1994,18 +2169,45 @@ static void flush_read(file_descriptor *desc) { } } -static int lseek_flush_read(file_descriptor *desc, int *errp) { +static int lseek_flush_read(file_descriptor *desc, int *errp +#ifdef USE_VM_PROBES + ,dt_private *dt_priv, char *dt_utag +#endif + ) { int r = 0; size_t read_size = desc->read_size; +#ifdef USE_VM_PROBES + Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0; +#endif + struct t_data *d; + + flush_read(desc); if (read_size != 0) { - flush_read(desc); - if ((r = async_lseek(desc, errp, 0, - -((ssize_t)read_size), EFILE_SEEK_CUR)) - < 0) { - return r; - } - } else { - flush_read(desc); + if ((d = async_lseek(desc, errp, 0, + -((ssize_t)read_size), EFILE_SEEK_CUR +#ifdef USE_VM_PROBES + , &dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { + r = -1; + } else { +#ifdef USE_VM_PROBES + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, FILE_LSEEK, + NULL, NULL, dt_i1, dt_i2, dt_i3, 0, desc->port_str); +#endif /* USE_VM_PROBES */ + } } return r; } @@ -2022,11 +2224,23 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) struct t_data *d = (struct t_data *) data; char header[5]; /* result code + count */ char resbuf[RESBUFSIZE]; /* Result buffer. */ - +#ifdef USE_VM_PROBES + int sched_i1 = d->sched_i1, sched_i2 = d->sched_i2, command = d->command, + result_ok = d->result_ok, + posix_errno = d->result_ok ? 0 : d->errInfo.posix_errno; + DTRACE_CHARBUF(sched_utag, DTRACE_EFILE_BUFSIZ+1); + + sched_utag[0] = '\0'; + if (DTRACE_ENABLED(efile_drv_return)) { + strncpy(sched_utag, d->sched_utag, DTRACE_EFILE_BUFSIZ); + sched_utag[DTRACE_EFILE_BUFSIZ] = '\0'; + } +#endif /* USE_VM_PROBES */ TRACE_C('r'); if (try_again(desc, d)) { + /* DTRACE TODO: what kind of probe makes sense here? */ return; } @@ -2224,6 +2438,9 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) if (d->reply) { TRACE_C('K'); reply_ok(desc); +#ifdef USE_VM_PROBES + result_ok = 1; +#endif } free_data(data); break; @@ -2279,6 +2496,8 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data) default: abort(); } + DTRACE6(efile_drv_return, sched_i1, sched_i2, sched_utag, + command, result_ok, posix_errno); if (desc->write_buffered != 0 && desc->timer_state == timer_idle) { desc->timer_state = timer_write; driver_set_timer(desc->port, desc->write_delay); @@ -2301,7 +2520,15 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) char* name; /* Points to the filename in buf. */ int command; struct t_data *d = NULL; - +#ifdef USE_VM_PROBES + char *dt_utag = NULL; + char *dt_s1 = NULL, *dt_s2 = NULL; + Sint64 dt_i1 = 0; + Sint64 dt_i2 = 0; + Sint64 dt_i3 = 0; + Sint64 dt_i4 = 0; + dt_private *dt_priv = get_dt_private(0); +#endif /* USE_VM_PROBES */ TRACE_C('o'); @@ -2316,6 +2543,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_mkdir; d->free = free_data; @@ -2327,6 +2558,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_rmdir; d->free = free_data; @@ -2338,6 +2573,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_delete_file; d->free = free_data; @@ -2355,6 +2594,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) FILENAME_COPY(d->b, name); FILENAME_COPY(d->b + namelen, new_name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_s2 = d->b + namelen; + dt_utag = buf + namelen + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->flags = desc->flags; d->fd = fd; d->command = command; @@ -2368,6 +2612,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_chdir; d->free = free_data; @@ -2379,6 +2627,9 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1); d->drive = *(uchar*)buf; +#ifdef USE_VM_PROBES + dt_utag = buf + 1; +#endif d->command = command; d->invoke = invoke_pwd; d->free = free_data; @@ -2394,6 +2645,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) FILENAME_CHARSIZE); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif d->dir_handle = NULL; d->command = command; d->invoke = invoke_readdir; @@ -2418,6 +2673,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) dir_handle = NULL; resbuf[0] = FILE_RESP_LFNAME; +#ifdef USE_VM_PROBES + dt_s1 = name; + dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE; +#endif /* Fill the buffer with multiple directory listings before sending it to the * receiving process. READDIR_CHUNKS is minimum number of files sent to the * receiver. @@ -2451,6 +2710,17 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) reply_error(desc, &errInfo); return; } +#ifdef USE_VM_PROBES + if (dt_utag != NULL && dt_utag[0] == '\0') { + dt_utag = NULL; + } + + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag, + dt_utag, command, name, dt_s2, + dt_i1, dt_i2, dt_i3, dt_i4, desc->port_str); + DTRACE6(efile_drv_return, dt_priv->thread_num, dt_priv->tag++, + dt_utag, command, 1, 0); +#endif TRACE_C('R'); driver_output2(desc->port, resbuf, 1, NULL, 0); return; @@ -2463,6 +2733,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d->flags = get_int32((uchar*)buf); name = buf+4; FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_i1 = d->flags; + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_open; d->free = free_data; @@ -2475,6 +2750,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data)); d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name; + dt_i1 = fd; +#endif d->command = command; d->invoke = invoke_fdatasync; d->free = free_data; @@ -2487,6 +2766,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data)); d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name; + dt_i1 = fd; +#endif d->command = command; d->invoke = invoke_fsync; d->free = free_data; @@ -2503,6 +2786,14 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) FILENAME_COPY(d->b, name); d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; + if (command == FILE_LSTAT) { + dt_s1 = d->b; + } else { + dt_i1 = fd; + } +#endif d->command = command; d->invoke = invoke_flstat; d->free = free_data; @@ -2516,6 +2807,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d->flags = desc->flags; d->fd = fd; +#ifdef USE_VM_PROBES + dt_utag = name; + dt_i1 = fd; + dt_i2 = d->flags; +#endif d->command = command; d->invoke = invoke_truncate; d->free = free_data; @@ -2536,6 +2832,13 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d->info.cTime = (time_t)((Sint64)get_int64(buf + 7 * 4)); FILENAME_COPY(d->b, buf + 9*4); +#ifdef USE_VM_PROBES + dt_i1 = d->info.mode; + dt_i2 = d->info.uid; + dt_i3 = d->info.gid; + dt_s1 = d->b; + dt_utag = buf + 9 * 4 + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_write_info; d->free = free_data; @@ -2548,6 +2851,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_readlink; d->free = free_data; @@ -2559,6 +2866,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) { d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1); FILENAME_COPY(d->b, name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE; +#endif d->command = command; d->invoke = invoke_altname; d->free = free_data; @@ -2579,6 +2890,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) FILENAME_COPY(d->b, name); FILENAME_COPY(d->b + namelen, new_name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_s2 = d->b + namelen; + dt_utag = buf + namelen + FILENAME_BYTELEN(dt_s2) + FILENAME_CHARSIZE; +#endif d->flags = desc->flags; d->fd = fd; d->command = command; @@ -2600,6 +2916,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) FILENAME_COPY(d->b, name); FILENAME_COPY(d->b + namelen, new_name); +#ifdef USE_VM_PROBES + dt_s1 = d->b; + dt_s2 = d->b + namelen; + dt_utag = buf + namelen + FILENAME_BYTELEN(dt_s2) + FILENAME_CHARSIZE; +#endif d->flags = desc->flags; d->fd = fd; d->command = command; @@ -2621,6 +2942,13 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) d->c.fadvise.offset = get_int64((uchar*) buf); d->c.fadvise.length = get_int64(((uchar*) buf) + sizeof(Sint64)); d->c.fadvise.advise = get_int32(((uchar*) buf) + 2 * sizeof(Sint64)); +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->c.fadvise.offset; + dt_i3 = d->c.fadvise.length; + dt_i4 = d->c.fadvise.advise; + dt_utag = buf + 3 * sizeof(Sint64); +#endif goto done; } @@ -2634,6 +2962,22 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count) done: if (d) { +#ifdef USE_VM_PROBES + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, command, dt_s1, dt_s2, + dt_i1, dt_i2, dt_i3, dt_i4, desc->port_str); +#endif cq_enq(desc, d); } } @@ -2647,6 +2991,9 @@ file_flush(ErlDrvData e) { #ifdef DEBUG int r; #endif +#ifdef USE_VM_PROBES + dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base); +#endif TRACE_C('f'); @@ -2657,7 +3004,11 @@ file_flush(ErlDrvData e) { #ifdef DEBUG r = #endif - flush_write(desc, NULL); + flush_write(desc, NULL +#ifdef USE_VM_PROBES + , dt_priv, (desc->d == NULL) ? NULL : desc->d->sched_utag +#endif + ); /* Only possible reason for bad return value is ENOMEM, and * there is nobody to tell... */ @@ -2699,6 +3050,9 @@ static void file_timeout(ErlDrvData e) { file_descriptor *desc = (file_descriptor *)e; enum e_timer timer_state = desc->timer_state; +#ifdef USE_VM_PROBES + dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base); +#endif TRACE_C('t'); @@ -2716,7 +3070,11 @@ file_timeout(ErlDrvData e) { #ifdef DEBUG int r = #endif - flush_write(desc, NULL); + flush_write(desc, NULL +#ifdef USE_VM_PROBES + , dt_priv, (desc->d == NULL) ? NULL : desc->d->sched_utag +#endif + ); /* Only possible reason for bad return value is ENOMEM, and * there is nobody to tell... */ @@ -2737,6 +3095,14 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { char command; int p, q; int err; + struct t_data *d = NULL; +#ifdef USE_VM_PROBES + Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0; + Sint64 dt_i4 = 0; + char *dt_utag = NULL; + char *dt_s1 = NULL; + dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base); +#endif TRACE_C('v'); @@ -2756,18 +3122,19 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { switch (command) { case FILE_CLOSE: { +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif flush_read(desc); - if (flush_write_check_error(desc, &err) < 0) { + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (ev->size != 1) { - /* Wrong command length */ - reply_posix_error(desc, EINVAL); - goto done; - } if (desc->fd != FILE_FD_INVALID) { - struct t_data *d; if (! (d = EF_ALLOC(sizeof(struct t_data)))) { reply_posix_error(desc, ENOMEM); } else { @@ -2775,6 +3142,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; d->fd = desc->fd; d->flags = desc->flags; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; +#endif d->invoke = invoke_close; d->free = free_data; d->level = 2; @@ -2790,8 +3161,21 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { case FILE_READ: { Uint32 sizeH, sizeL; size_t size, alloc_size; - struct t_data *d; - if (flush_write_check_error(desc, &err) < 0) { + + if (!EV_GET_UINT32(ev, &sizeH, &p, &q) + || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { + /* Wrong buffer length to contain the read count */ + reply_posix_error(desc, EINVAL); + goto done; + } +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } @@ -2799,19 +3183,16 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { if (desc->read_bufsize == 0 && desc->read_binp != NULL && desc->read_size > 0) { /* We have allocated a buffer for line mode but should not really have a read-ahead buffer... */ - if (lseek_flush_read(desc, &err) < 0) { + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } } #endif - if (ev->size != 1+8 - || !EV_GET_UINT32(ev, &sizeH, &p, &q) - || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { - /* Wrong buffer length to contain the read count */ - reply_posix_error(desc, EINVAL); - goto done; - } #if SIZEOF_SIZE_T == 4 if (sizeH != 0) { reply_posix_error(desc, EINVAL); @@ -2887,6 +3268,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->c.read.bin_offset = desc->read_offset + desc->read_size; d->c.read.bin_size = desc->read_binp->orig_size - d->c.read.bin_offset; d->c.read.size = size; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; + dt_i3 = d->c.read.size; +#endif driver_binary_inc_refc(d->c.read.binp); d->invoke = invoke_read; d->free = free_read; @@ -2904,12 +3290,22 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { * allocated binary + dealing with offsets and lengts are done in file_async ready * for this OP. */ - struct t_data *d; - if (flush_write_check_error(desc, &err) < 0) { +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (ev->size != 1) { + if (ev->size != 1 +#ifdef USE_VM_PROBES + + FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE +#endif + ) { /* Wrong command length */ reply_posix_error(desc, EINVAL); goto done; @@ -2965,8 +3361,16 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->c.read_line.binp = desc->read_binp; d->c.read_line.read_offset = desc->read_offset; d->c.read_line.read_size = desc->read_size; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; + dt_i3 = d->c.read_line.read_offset; +#endif #if !ALWAYS_READ_LINE_AHEAD d->c.read_line.read_ahead = (desc->read_bufsize > 0); +#ifdef USE_VM_PROBES + dt_i4 = d->c.read_line.read_ahead; +#endif #endif driver_binary_inc_refc(d->c.read.binp); d->invoke = invoke_read_line; @@ -2974,10 +3378,22 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->level = 1; cq_enq(desc, d); } goto done; - case FILE_WRITE: { + case FILE_WRITE: { /* Dtrace: The dtrace user tag is not last in message, + but follows the message tag directly. + This is handled specially in prim_file.erl */ ErlDrvSizeT skip = 1; ErlDrvSizeT size = ev->size - skip; - if (lseek_flush_read(desc, &err) < 0) { + +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); + skip += FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE; + size = ev->size - skip; +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } @@ -3004,7 +3420,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { driver_set_timer(desc->port, desc->write_delay); } } else { - if (async_write(desc, &err, !0, size) != 0) { + if ((d = async_write(desc, &err, !0, size +#ifdef USE_VM_PROBES + , &dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { MUTEX_UNLOCK(desc->q_mtx); reply_posix_error(desc, err); goto done; @@ -3014,24 +3434,49 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } } goto done; /* case FILE_WRITE */ - case FILE_PWRITEV: { + case FILE_PWRITEV: { /* Dtrace: The dtrace user tag is not last in message, + but follows the message tag directly. + This is handled specially in prim_file.erl */ Uint32 i, j, n; size_t total; - struct t_data *d; - if (lseek_flush_read(desc, &err) < 0) { - reply_Uint_posix_error(desc, 0, err); - goto done; - } - if (flush_write_check_error(desc, &err) < 0) { - reply_Uint_posix_error(desc, 0, err); - goto done; +#ifdef USE_VM_PROBES + char dt_tmp; + int dt_utag_bytes = 1; + + dt_utag = EV_CHAR_P(ev, p, q); + /* This will work for UTF-8, but not for UTF-16 - extra reminder here */ +#ifdef FILENAMES_16BIT +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif + while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') { + dt_utag_bytes++; } +#endif if (ev->size < 1+4 +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif || !EV_GET_UINT32(ev, &n, &p, &q)) { /* Buffer too short to contain even the number of pos/size specs */ reply_Uint_posix_error(desc, 0, EINVAL); goto done; } + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_Uint_posix_error(desc, 0, err); + goto done; + } + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_Uint_posix_error(desc, 0, err); + goto done; + } if (n == 0) { /* Trivial case - nothing to write */ if (ev->size != 1+4) { @@ -3041,7 +3486,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } goto done; } - if (ev->size < 1+4+8*(2*n)) { + if (ev->size < 1+4+8*(2*n) +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif + ) { /* Buffer too short to contain even the pos/size specs */ reply_Uint_posix_error(desc, 0, EINVAL); goto done; @@ -3056,6 +3505,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; d->fd = desc->fd; d->flags = desc->flags; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; +#endif d->c.pwritev.port = desc->port; d->c.pwritev.q_mtx = desc->q_mtx; d->c.pwritev.n = n; @@ -3093,13 +3546,20 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } } d->c.pwritev.size = total; +#ifdef USE_VM_PROBES + dt_i3 = d->c.pwritev.size; +#endif d->c.pwritev.free_size = 0; if (j == 0) { /* Trivial case - nothing to write */ EF_FREE(d); reply_Uint(desc, 0); } else { - ErlDrvSizeT skip = 1 + 4 + 8*(2*n); + ErlDrvSizeT skip = 1 + 4 + 8 * (2*n) +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif + ; if (skip + total != ev->size) { /* Actual amount of data does not match * total of all pos/size specs @@ -3120,27 +3580,55 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } } goto done; /* case FILE_PWRITEV: */ - case FILE_PREADV: { + case FILE_PREADV: { /* Dtrace: The dtrace user tag is not last in message, + but follows the message tag directly. + This is handled specially in prim_file.erl */ register void * void_ptr; Uint32 i, n; - struct t_data *d; ErlIOVec *res_ev; - if (lseek_flush_read(desc, &err) < 0) { +#ifdef USE_VM_PROBES + char dt_tmp; + int dt_utag_bytes = 1; + /* This will work for UTF-8, but not for UTF-16 - extra reminder here */ +#ifdef FILENAMES_16BIT +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif + dt_utag = EV_CHAR_P(ev, p, q); + while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') { + dt_utag_bytes++; + } +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (flush_write_check_error(desc, &err) < 0) { + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } if (ev->size < 1+8 +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif || !EV_GET_UINT32(ev, &n, &p, &q) || !EV_GET_UINT32(ev, &n, &p, &q)) { /* Buffer too short to contain even the number of pos/size specs */ reply_posix_error(desc, EINVAL); goto done; } - if (ev->size != 1+8+8*(2*n)) { + if (ev->size < 1+8+8*(2*n) +#ifdef USE_VM_PROBES + + dt_utag_bytes +#endif + ) { /* Buffer wrong length to contain the pos/size specs */ reply_posix_error(desc, EINVAL); goto done; @@ -3160,6 +3648,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; d->fd = desc->fd; d->flags = desc->flags; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; +#endif d->c.preadv.n = n; d->c.preadv.cnt = 0; d->c.preadv.size = 0; @@ -3187,6 +3679,9 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { #else size = ((size_t)sizeH<<32) | sizeL; #endif +#ifdef USE_VM_PROBES + dt_i3 += size; +#endif if (! (res_ev->binv[i] = driver_alloc_binary(size))) { reply_posix_error(desc, ENOMEM); break; @@ -3233,42 +3728,68 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } goto done; /* case FILE_PREADV: */ case FILE_LSEEK: { - Sint64 offset; /* Offset for seek */ + Sint64 offset; /* Offset for seek */ Uint32 origin; /* Origin of seek. */ - if (lseek_flush_read(desc, &err) < 0) { - reply_posix_error(desc, err); + + if (ev->size < 1+8+4 + || !EV_GET_UINT64(ev, &offset, &p, &q) + || !EV_GET_UINT32(ev, &origin, &p, &q)) { + /* Wrong length of buffer to contain offset and origin */ + reply_posix_error(desc, EINVAL); goto done; } - if (flush_write_check_error(desc, &err) < 0) { +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (ev->size != 1+8+4 - || !EV_GET_UINT64(ev, &offset, &p, &q) - || !EV_GET_UINT32(ev, &origin, &p, &q)) { - /* Wrong length of buffer to contain offset and origin */ - reply_posix_error(desc, EINVAL); + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_posix_error(desc, err); goto done; } - if (async_lseek(desc, &err, !0, offset, origin) < 0) { + if ((d = async_lseek(desc, &err, !0, offset, origin +#ifdef USE_VM_PROBES + , &dt_i1, &dt_i2, &dt_i3 +#endif + )) == NULL) { reply_posix_error(desc, err); goto done; } } goto done; case FILE_READ_FILE: { - struct t_data *d; char *filename; if (ev->size < 1+1) { /* Buffer contains empty name */ reply_posix_error(desc, ENOENT); goto done; } +#ifndef USE_VM_PROBES + /* In the dtrace case, the iov has an extra element, the dtrace utag - we will need + another test to see that + the filename is in a single buffer: */ if (ev->size-1 != ev->iov[q].iov_len-p) { /* Name not in one single buffer */ reply_posix_error(desc, EINVAL); goto done; } +#else + if (((byte *)ev->iov[q].iov_base)[ev->iov[q].iov_len-1] != '\0') { + /* Name not in one single buffer */ + reply_posix_error(desc, EINVAL); + goto done; + } +#endif filename = EV_CHAR_P(ev, p, q); d = EF_ALLOC(sizeof(struct t_data) -1 + FILENAME_BYTELEN(filename) + FILENAME_CHARSIZE); if (! d) { @@ -3279,6 +3800,20 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->reply = !0; /* Copy name */ FILENAME_COPY(d->b, filename); +#ifdef USE_VM_PROBES + { + char dt_tmp; + + /* This will work for UTF-8, but not for UTF-16 - extra reminder here */ +#ifdef FILENAMES_16BIT +#error 16bit characters in filenames and dtrace in combination is not supported. +#endif + while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') + ; + dt_s1 = d->b; + dt_utag = EV_CHAR_P(ev, p, q); + } +#endif d->c.read_file.binp = NULL; d->invoke = invoke_read_file; d->free = free_read_file; @@ -3298,7 +3833,6 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { char mode; Sint64 hdr_offset; Uint32 max_size; - struct t_data *d; ErlIOVec *res_ev; int vsize; if (! EV_GET_CHAR(ev, &mode, &p, &q)) { @@ -3310,14 +3844,6 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { reply_posix_error(desc, EINVAL); goto done; } - if (lseek_flush_read(desc, &err) < 0) { - reply_posix_error(desc, err); - goto done; - } - if (flush_write_check_error(desc, &err) < 0) { - reply_posix_error(desc, err); - goto done; - } if (ev->size < 1+1+8+4 || !EV_GET_UINT64(ev, &hdr_offset, &p, &q) || !EV_GET_UINT32(ev, &max_size, &p, &q)) { @@ -3326,6 +3852,25 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { reply_posix_error(desc, EINVAL); goto done; } +#ifdef USE_VM_PROBES + dt_utag = EV_CHAR_P(ev, p, q); +#endif + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_posix_error(desc, err); + goto done; + } + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { + reply_posix_error(desc, err); + goto done; + } /* Create the thread data structure with the contained ErlIOVec * and corresponding binaries for the response */ @@ -3342,6 +3887,12 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->flags = desc->flags; d->c.preadv.offsets[0] = hdr_offset; d->c.preadv.size = max_size; +#ifdef USE_VM_PROBES + dt_i1 = d->fd; + dt_i2 = d->flags; + dt_i3 = d->c.preadv.offsets[0]; + dt_i4 = d->c.preadv.size; +#endif res_ev = &d->c.preadv.eiov; /* XXX possible alignment problems here for weird machines */ res_ev->iov = void_ptr = d + 1; @@ -3356,16 +3907,24 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { case FILE_SETOPT: { char opt; + if (ev->size < 1+1 || !EV_GET_CHAR(ev, &opt, &p, &q)) { /* Buffer too short to contain even the option type */ reply_posix_error(desc, EINVAL); goto done; } +#ifdef USE_VM_PROBES + dt_i1 = opt; + dt_utag = EV_CHAR_P(ev, p, q); +#endif switch (opt) { case FILE_OPT_DELAYED_WRITE: { Uint32 sizeH, sizeL, delayH, delayL; if (ev->size != 1+1+4*sizeof(Uint32) +#ifdef USE_VM_PROBES + + FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE +#endif || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q) || !EV_GET_UINT32(ev, &delayH, &p, &q) @@ -3392,12 +3951,18 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { #else desc->write_delay = ((unsigned long)delayH << 32) | delayL; #endif +#ifdef USE_VM_PROBES + dt_i2 = desc->write_delay; +#endif TRACE_C('K'); reply_ok(desc); } goto done; case FILE_OPT_READ_AHEAD: { Uint32 sizeH, sizeL; if (ev->size != 1+1+2*sizeof(Uint32) +#ifdef USE_VM_PROBES + + FILENAME_BYTELEN(dt_utag)+FILENAME_CHARSIZE +#endif || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { /* Buffer has wrong length to contain the option values */ @@ -3413,6 +3978,9 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { #else desc->read_bufsize = ((size_t)sizeH << 32) | sizeL; #endif +#ifdef USE_VM_PROBES + dt_i2 = desc->read_bufsize; +#endif TRACE_C('K'); reply_ok(desc); } goto done; @@ -3499,11 +4067,19 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } /* switch(command) */ - if (lseek_flush_read(desc, &err) < 0) { + if (lseek_flush_read(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } - if (flush_write_check_error(desc, &err) < 0) { + if (flush_write_check_error(desc, &err +#ifdef USE_VM_PROBES + , dt_priv, dt_utag +#endif + ) < 0) { reply_posix_error(desc, err); goto done; } else { @@ -3521,5 +4097,50 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { } done: + if (d != NULL) { +#ifdef USE_VM_PROBES + /* + * If d == NULL, then either: + * 1). There was an error of some sort, or + * 2). The command given to us is actually implemented + * by file_output() instead. + * + * Case #1 is probably a TODO item, perhaps? + * Case #2 we definitely don't want to activate a probe. + */ + d->sched_i1 = dt_priv->thread_num; + d->sched_i2 = dt_priv->tag; + d->sched_utag[0] = '\0'; + if (dt_utag != NULL) { + if (dt_utag[0] == '\0') { + dt_utag = NULL; + } else { + strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1); + d->sched_utag[sizeof(d->sched_utag) - 1] = '\0'; + } + } + DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++, + dt_utag, command, dt_s1, NULL, dt_i1, dt_i2, dt_i3, dt_i4, + desc->port_str); +#endif + } cq_execute(desc); } + +#ifdef USE_VM_PROBES +dt_private * +get_dt_private(int base) +{ + dt_private *dt_priv = (dt_private *) pthread_getspecific(dt_driver_key); + + if (dt_priv == NULL) { + dt_priv = EF_SAFE_ALLOC(sizeof(dt_private)); + erts_mtx_lock(&dt_driver_mutex); + dt_priv->thread_num = (base + dt_driver_idnum++); + erts_mtx_unlock(&dt_driver_mutex); + dt_priv->tag = 0; + pthread_setspecific(dt_driver_key, dt_priv); + } + return dt_priv; +} +#endif /* USE_VM_PROBES */ diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 23a4bf1b04..1d173a758a 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -36,6 +36,7 @@ #include "global.h" #include "erl_check_io.h" #include "erl_thr_progress.h" +#include "dtrace-wrapper.h" #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS # define ERTS_DRV_EV_STATE_EXTRA_SIZE 128 @@ -314,6 +315,7 @@ forget_removed(struct pollset_info* psi) erts_smp_mtx_unlock(mtx); if (drv_ptr) { int was_unmasked = erts_block_fpe(); + DTRACE1(driver_stop_select, drv_ptr->name); (*drv_ptr->stop_select) ((ErlDrvEvent) fd, NULL); erts_unblock_fpe(was_unmasked); if (drv_ptr->handle) { @@ -496,6 +498,9 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, ErtsDrvEventState *state; int wake_poller; int ret; +#ifdef USE_VM_PROBES + DTRACE_CHARBUF(name, 64); +#endif ERTS_SMP_LC_ASSERT(erts_drvport2port(ix) && erts_lc_is_port_locked(erts_drvport2port(ix))); @@ -525,6 +530,10 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, if (IS_FD_UNKNOWN(state)) { /* fast track to stop_select callback */ stop_select_fn = erts_drvport2port(ix)->drv_ptr->stop_select; +#ifdef USE_VM_PROBES + strncpy(name, erts_drvport2port(ix)->drv_ptr->name, sizeof(name)-1); + name[sizeof(name)-1] = '\0'; +#endif ret = 0; goto done_unknown; } @@ -661,6 +670,10 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix, /* Safe to close fd now as it is not in pollset or there was no need to eject fd (kernel poll) */ stop_select_fn = drv_ptr->stop_select; +#ifdef USE_VM_PROBES + strncpy(name, erts_drvport2port(ix)->drv_ptr->name, sizeof(name)-1); + name[sizeof(name)-1] = '\0'; +#endif } else { /* Not safe to close fd, postpone stop_select callback. */ @@ -686,6 +699,7 @@ done_unknown: erts_smp_mtx_unlock(fd_mtx(fd)); if (stop_select_fn) { int was_unmasked = erts_block_fpe(); + DTRACE1(driver_stop_select, name); (*stop_select_fn)(e, NULL); erts_unblock_fpe(was_unmasked); } diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index c7617d3b90..a21b055596 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.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 @@ -28,7 +28,7 @@ types/1, t_list_to_existing_atom/1,os_env/1,otp_7526/1, binary_to_atom/1,binary_to_existing_atom/1, - atom_to_binary/1,min_max/1]). + atom_to_binary/1,min_max/1, erlang_halt/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -36,7 +36,7 @@ all() -> [types, t_list_to_existing_atom, os_env, otp_7526, display, atom_to_binary, binary_to_atom, binary_to_existing_atom, - min_max]. + min_max, erlang_halt]. groups() -> []. @@ -438,7 +438,55 @@ min_max(Config) when is_list(Config) -> ok. + + +erlang_halt(Config) when is_list(Config) -> + try erlang:halt(undefined) of + _-> ?t:fail({erlang,halt,{undefined}}) + catch error:badarg -> ok end, + try halt(undefined) of + _-> ?t:fail({halt,{undefined}}) + catch error:badarg -> ok end, + try erlang:halt(undefined, []) of + _-> ?t:fail({erlang,halt,{undefined,[]}}) + catch error:badarg -> ok end, + try halt(undefined, []) of + _-> ?t:fail({halt,{undefined,[]}}) + catch error:badarg -> ok end, + try halt(0, undefined) of + _-> ?t:fail({halt,{0,undefined}}) + catch error:badarg -> ok end, + try halt(0, [undefined]) of + _-> ?t:fail({halt,{0,[undefined]}}) + catch error:badarg -> ok end, + try halt(0, [{undefined,true}]) of + _-> ?t:fail({halt,{0,[{undefined,true}]}}) + catch error:badarg -> ok end, + try halt(0, [{flush,undefined}]) of + _-> ?t:fail({halt,{0,[{flush,undefined}]}}) + catch error:badarg -> ok end, + try halt(0, [{flush,true,undefined}]) of + _-> ?t:fail({halt,{0,[{flush,true,undefined}]}}) + catch error:badarg -> ok end, + H = hostname(), + {ok,N1} = slave:start(H, halt_node1), + {badrpc,nodedown} = rpc:call(N1, erlang, halt, []), + {ok,N2} = slave:start(H, halt_node2), + {badrpc,nodedown} = rpc:call(N2, erlang, halt, [0]), + {ok,N3} = slave:start(H, halt_node3), + {badrpc,nodedown} = rpc:call(N3, erlang, halt, [0,[]]), + ok. + + + %% Helpers id(I) -> I. +hostname() -> + hostname(atom_to_list(node())). + +hostname([$@ | Hostname]) -> + list_to_atom(Hostname); +hostname([_C | Cs]) -> + hostname(Cs). diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 58c36c3bdc..ea57000c82 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -28,6 +28,7 @@ my $verbose = 0; my $hot = 1; my $num_file_opcodes = 0; my $wordsize = 32; +my %defs; # Defines (from command line). # This is shift counts and mask for the packer. my $WHOLE_WORD = ''; @@ -96,6 +97,12 @@ my %unnumbered; my %is_transformed; # +# Pre-processor. +# +my @if_val; +my @if_line; + +# # Code transformations. # my $te_max_vars = 0; # Max number of variables ever needed. @@ -223,6 +230,7 @@ while (@ARGV && $ARGV[0] =~ /^-(.*)/) { ($outdir = shift), next if /^outdir/; ($wordsize = shift), next if /^wordsize/; ($verbose = 1), next if /^v/; + ($defs{$1} = $2), next if /^D(\w+)=(\w+)/; die "$0: Bad option: -$_\n"; } @@ -239,7 +247,43 @@ while (<>) { } next if /^\s*$/; next if /^\#/; - + + # + # Handle %if. + # + if (/^\%if (\w+)/) { + my $name = $1; + my $val = $defs{$name}; + defined $val or error("'$name' is undefined"); + push @if_val, $val; + push @if_line, $.; + next; + } elsif (/^\%unless (\w+)/) { + my $name = $1; + my $val = $defs{$name}; + defined $val or error("'$name' is undefined"); + push @if_val, !$val; + push @if_line, $.; + next; + } elsif (/^\%else$/) { + unless (@if_line) { + error("%else without a preceding %if/%unless"); + } + $if_line[$#if_line] = $.; + $if_val[$#if_val] = !$if_val[$#if_val]; + next; + } elsif (/^\%endif$/) { + unless (@if_line) { + error("%endif without a preceding %if/%unless/%else"); + } + pop @if_val; + pop @if_line; + next; + } + if (@if_val and not $if_val[$#if_val]) { + next; + } + # # Handle assignments. # @@ -349,7 +393,13 @@ while (<>) { $unnumbered{$name,$arity} = 1; } } continue { - close(ARGV) if eof(ARGV); + if (eof(ARGV)) { + close(ARGV); + if (@if_line) { + error("Unterminated %if/%unless/%else at " . + "line $if_line[$#if_line]\n"); + } + } } $num_file_opcodes = @gen_opname; diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c index 4806311dfe..162c908285 100644 --- a/erts/lib_src/common/erl_misc_utils.c +++ b/erts/lib_src/common/erl_misc_utils.c @@ -727,7 +727,7 @@ adjust_processor_nodes(erts_cpu_info_t *cpuinfo, int no_nodes) prev = NULL; this = &cpuinfo->topology[0]; - last = &cpuinfo->topology[cpuinfo->configured-1]; + last = &cpuinfo->topology[cpuinfo->topology_size-1]; while (1) { if (processor == this->processor) { if (node != this->node) @@ -939,7 +939,7 @@ read_topology(erts_cpu_info_t *cpuinfo) if (res > 1) { prev = this++; - last = &cpuinfo->topology[cpuinfo->configured-1]; + last = &cpuinfo->topology[cpuinfo->topology_size-1]; while (1) { this->thread = ((this->node == prev->node @@ -1094,7 +1094,7 @@ read_topology(erts_cpu_info_t *cpuinfo) if (res > 1) { prev = this++; - last = &cpuinfo->topology[cpuinfo->configured-1]; + last = &cpuinfo->topology[cpuinfo->topology_size-1]; while (1) { this->thread = ((this->node == prev->node diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c index 108a8bb531..399c83384e 100644 --- a/erts/lib_src/common/erl_printf.c +++ b/erts/lib_src/common/erl_printf.c @@ -173,6 +173,7 @@ typedef struct { static int write_sn(void *vwsnap, char* buf, size_t len) { + int rv = 0; write_sn_arg_t *wsnap = (write_sn_arg_t *) vwsnap; ASSERT(wsnap); ASSERT(len > 0); @@ -180,12 +181,13 @@ write_sn(void *vwsnap, char* buf, size_t len) size_t sz = len; if (sz >= wsnap->len) sz = wsnap->len; + rv = (int)sz; memcpy((void *) wsnap->buf, (void *) buf, sz); wsnap->buf += sz; wsnap->len -= sz; return sz; } - return 0; + return rv; } static int diff --git a/erts/ntbuild.erl b/erts/ntbuild.erl deleted file mode 100644 index e48be58c17..0000000000 --- a/erts/ntbuild.erl +++ /dev/null @@ -1,332 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-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% -%% -%% To be used from makefiles on the unix side executing things on the NT-side --module(ntbuild). - --export([nmake/1, omake/1, waitnode/1, restart/1, - setdir/1, run_tests/1, run_command/1]). --export([serv_nmake/2, serv_omake/2, serv_restart/0, serv_run_tests/2, - serv_run_command/1]). - -waitnode([NtNode]) -> - % First, wait for node to disappear. - case wait_disappear(NtNode, 0) of - ok -> - case wait_appear(NtNode, 0) of - ok -> - halt(0); - fail -> - halt(1) - end; - fail -> - halt(1) - end. - -% Wait for nt node to appear within 5 minutes. -wait_appear(_NtNode, 300) -> - fail; -wait_appear(NtNode, N) -> - receive after 1000 -> ok end, - case nt_node_alive(NtNode, quiet) of - no -> - wait_appear(NtNode, N+1); - yes -> - ok - end. - - - -% Waits for nt node to disappear within 3 minutes. -wait_disappear(NtNode, 300) -> - fail; -wait_disappear(NtNode, N) -> - receive after 1000 -> ok end, - case nt_node_alive(NtNode, quiet) of - yes -> - wait_disappear(NtNode, N+1); - no -> - ok - end. - -restart([NtNode]) -> - case nt_node_alive(NtNode) of - yes -> - case rpc:call(NtNode, ntbuild, serv_restart, []) of - ok -> - io:format("halt(0)~n"), - halt(); - Error -> - io:format("halt(1)~n"), - halt(1) - end; - no -> - halt(1) - end. - - -setdir([NtNode, Dir0]) -> - Dir = atom_to_list(Dir0), - case nt_node_alive(NtNode) of - yes -> - case rpc:call(NtNode, file, set_cwd, [Dir]) of - ok -> - io:format("halt(0)~n"), - halt(); - Error -> - io:format("halt(1) (Error: ~p) (~p not found) ~n", [Error, Dir]), - halt(1) - end; - no -> - halt(1) - end. - -run_tests([NtNode, Vsn0, Logdir]) -> - Vsn = atom_to_list(Vsn0), - case nt_node_alive(NtNode) of - yes -> - case rpc:call(NtNode, ntbuild, serv_run_tests, [Vsn, Logdir]) of - ok -> - io:format("halt(0)~n"), - halt(); - Error -> - io:format("RPC To Windows Node Failed: ~p~n", [Error]), - io:format("halt(1)~n"), - halt(1) - end; - no -> - halt(1) - end. - -run_command([NtNode, Cmd]) -> - case nt_node_alive(NtNode) of - yes -> - case rpc:call(NtNode, ntbuild, serv_run_command, [Cmd]) of - ok -> - io:format("halt(0)~n"), - halt(); - Error -> - io:format("RPC To Windows Node Failed: ~p~n", [Error]), - io:format("halt(1)~n"), - halt(1) - end; - no -> - halt(1) - end. - -nmake([NtNode, Path, Options]) -> -% io:format("nmake2(~w,~w)~n",[Path, Options]), - Dir=atom_to_list(Path), - Opt=atom_to_list(Options), - case nt_node_alive(NtNode) of - yes -> - case rpc:call(NtNode, ntbuild, serv_nmake, [Dir, Opt]) of - ok -> - io:format("halt(0)~n"), - halt(); - Error -> - io:format("Error: ~n", [Error]), - halt(1) - end; - no -> - halt(1) - end. - -omake([NtNode, Path, Options]) -> - Dir=atom_to_list(Path), - Opt=atom_to_list(Options), - case nt_node_alive(NtNode) of - yes -> - case rpc:call(NtNode, ntbuild, serv_omake, [Dir, Opt]) of - ok -> - io:format("halt(0)~n"), - halt(); - Error -> - io:format("RPC To Windows Node Failed: ~p~n", [Error]), - io:format("~p ~p~n", [Dir, Opt]), - io:format("halt(1)~n"), - halt(1) - end; - no -> - halt(1) - end. - - - - - -nt_node_alive(NtNode) -> - case net:ping(NtNode) of - pong -> - yes; - pang -> - io:format("The NT node (~p) is not up. ~n",[NtNode]), - no - end. - -nt_node_alive(NtNode, quiet) -> - case net:ping(NtNode) of - pong -> - yes; - pang -> - no - end. - - - -%%% -%%% The 'serv_' functions. Theese are the routines run on the WinNT node. -%%% - -%%----------------------- -%% serv_run_tests() -%% Runs the tests. -serv_run_tests(Vsn, Logdir) -> - {ok, Cwd}=file:get_cwd(), - io:format("serv_run_tests ~p ~p ~n", [Vsn, Logdir]), - Cmd0= "set central_log_dir=" ++ Logdir, - Erl = "C:/progra~1/erl"++Vsn++"/bin/erl", - Cmd1 = Erl++" -sname a -setcookie a -noshell -noinput -s ts install -s ts run -s ts save -s erlang halt", -%% Dir = "C:/temp/test_suite/test_server", - Cmd= Cmd0 ++ "/r/n" ++ Cmd1, - Dir = "C:/temp/test_server/p7a/test_server", - file:set_cwd(Dir), - Res=run_make_bat(Dir, Cmd), - file:set_cwd(Cwd), - Res. - -%%----------------------- -%% serv_run_command() -%% Runs a command. -serv_run_command(Cmd) -> - {ok, Cwd}=file:get_cwd(), - Res=run_make_bat("", Cmd), - file:set_cwd(Cwd), - Res. - -%%----------------------- -%% serv_restart() -%% Reboots the NT machine. -serv_restart() -> - Exe="\\erts\\install_nt\\reboot.exe", - open_port({spawn, Exe}, [stream, eof, in]), - ok. - - -%%----------------------- -%% serv_nmake(Path, Options) -%% Runs `nmake' in the given directory. -%% Result: ok | error -serv_nmake(Path, Options) -> - {ok, Cwd}=file:get_cwd(), - Command="nmake -e -f Makefile.win32 " ++ Options ++ " 2>&1", - Res=run_make_bat(Path, Command), - file:set_cwd(Cwd), - Res. - -%%----------------------- -%% serv_omake(Path, Options) -%% Runs `omake' in the given directory. -%% Result: ok | error -serv_omake(Path, Options) -> - {ok, Cwd}=file:get_cwd(), - Command="omake -W -E -EN -f Makefile.win32 " ++ Options ++ " 2>&1", - Res=run_make_bat(Path, Command), - file:set_cwd(Cwd), - Res. - - -read_output(Port, SoFar) -> -% io:format("(read_output)~n"), - case get_data_from_port(Port) of - eof -> - io:format("*** eof ***~n"), - io:format("Never reached a real message"), - halt(1); - {ok, Data} -> - case print_line([SoFar|Data]) of - {ok, Rest} -> - read_output(Port, Rest); - {done, Res} -> - Res - end - end. - -print_line(Data) -> - print_line(Data, []). - -print_line([], Acc) -> - {ok, lists:reverse(Acc)}; -print_line([$*,$o,$k,$*|Rest], _Acc) -> - io:format("*ok*~n"), - {done, ok}; -print_line([$*,$e,$r,$r,$o,$r|Rest], _Acc) -> - io:format("*error*~n"), - {done, error}; -print_line([$\r,$\n|Rest], Acc) -> - io:format("~s~n", [lists:reverse(Acc)]), - print_line(Rest, []); -print_line([Chr|Rest], Acc) -> - print_line(Rest, [Chr|Acc]). - -get_data_from_port(Port) -> - receive - {Port, {data, Bytes}} -> - {ok, Bytes}; - {Port, eof} -> - unlink(Port), - exit(Port, die), - eof; - Other -> - io:format("Strange message received: ~p~n", [Other]), - get_data_from_port(Port) - end. - - -run_make_bat(Dir, Make) -> - {Name, Exe, Script}=create_make_script(Dir, Make), - io:format("Exe:~p Cwd:~p Script:~p ~n",[Exe, Dir, Script]), - case file:write_file(Name, Script) of - ok -> - case catch open_port({spawn, Exe}, [stderr_to_stdout, stream, hide, - eof, in]) of - Port when port(Port) -> - read_output(Port, []); - Other -> - io:format("Error, open_port failed: ~p~n", [Other]), - {open_port, Other, Exe} - end; - Error -> - {write_file, Error, Name} - end. - -create_make_script(Dir, Make) when atom(Make) -> - create_make_script(Dir, atom_to_list(Make)); -create_make_script(Dir, Make) -> - {"run_make_bs.bat", - "run_make_bs 2>&1", - ["@echo off\r\n", - "@cd ", Dir, "\r\n", - Make++"\r\n", - "if errorlevel 1 echo *run_make_bs error*\r\n", - "if not errorlevel 1 echo *ok*\r\n"]}. - - - - - diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex 87e80aae9b..6400cda2b5 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 36cbe329e8..0ecb720726 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -268,7 +268,7 @@ advise(#file_descriptor{module = ?MODULE, data = {Port, _}}, %% Returns {error, Reason} | ok. write(#file_descriptor{module = ?MODULE, data = {Port, _}}, Bytes) -> - case drv_command(Port, [?FILE_WRITE,Bytes]) of + case drv_command_nt(Port, [?FILE_WRITE,erlang:dt_prepend_vm_tag_data(Bytes)],undefined) of {ok, _Size} -> ok; Error -> @@ -283,8 +283,8 @@ pwrite(#file_descriptor{module = ?MODULE, data = {Port, _}}, L) pwrite_int(_, [], 0, [], []) -> ok; pwrite_int(Port, [], N, Spec, Data) -> - Header = list_to_binary([<<?FILE_PWRITEV, N:32>> | reverse(Spec)]), - case drv_command_raw(Port, [Header | reverse(Data)]) of + Header = list_to_binary([?FILE_PWRITEV, erlang:dt_prepend_vm_tag_data(<<N:32>>) | reverse(Spec)]), + case drv_command_nt(Port, [Header | reverse(Data)], undefined) of {ok, _Size} -> ok; Error -> @@ -402,7 +402,7 @@ pread(#file_descriptor{module = ?MODULE, data = {Port, _}}, L) pread_int(_, [], 0, []) -> {ok, []}; pread_int(Port, [], N, Spec) -> - drv_command(Port, [<<?FILE_PREADV, 0:32, N:32>> | reverse(Spec)]); + drv_command_nt(Port, [?FILE_PREADV, erlang:dt_prepend_vm_tag_data(<<0:32, N:32>>) | reverse(Spec)],undefined); pread_int(Port, [{Offs, Size} | T], N, Spec) when is_integer(Offs), is_integer(Size), 0 =< Size -> if @@ -423,9 +423,9 @@ pread(#file_descriptor{module = ?MODULE, data = {Port, _}}, Offs, Size) if -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE, Size < ?LARGEFILESIZE -> - case drv_command(Port, - <<?FILE_PREADV, 0:32, 1:32, - Offs:64/signed, Size:64>>) of + case drv_command_nt(Port, + [?FILE_PREADV, erlang:dt_prepend_vm_tag_data(<<0:32, 1:32, + Offs:64/signed, Size:64>>)], undefined) of {ok, [eof]} -> eof; {ok, [Data]} -> @@ -923,12 +923,17 @@ drv_open(Driver, Portopts) -> %% Closes a port in a safe way. Returns ok. drv_close(Port) -> - try erlang:port_close(Port) catch error:_ -> ok end, - receive %% Ugly workaround in case the caller==owner traps exits - {'EXIT', Port, _Reason} -> - ok - after 0 -> - ok + Save = erlang:dt_spread_tag(false), + try + try erlang:port_close(Port) catch error:_ -> ok end, + receive %% Ugly workaround in case the caller==owner traps exits + {'EXIT', Port, _Reason} -> + ok + after 0 -> + ok + end + after + erlang:dt_restore_tag(Save) end. @@ -938,9 +943,6 @@ drv_close(Port) -> %% then closed after the result has been received. %% Returns {ok, Result} or {error, Reason}. -drv_command_raw(Port, Command) -> - drv_command(Port, Command, false, undefined). - drv_command(Port, Command) -> drv_command(Port, Command, undefined). @@ -956,7 +958,8 @@ drv_command(Port, Command, R) -> end. drv_command(Port, Command, Validated, R) when is_port(Port) -> - try erlang:port_command(Port, Command) of + Save = erlang:dt_spread_tag(false), + try erlang:port_command(Port, erlang:dt_append_vm_tag_data(Command)) of true -> drv_get_response(Port, R) catch @@ -975,6 +978,8 @@ drv_command(Port, Command, Validated, R) when is_port(Port) -> end; error:Reason -> {error, Reason} + after + erlang:dt_restore_tag(Save) end; drv_command({Driver, Portopts}, Command, Validated, R) -> case drv_open(Driver, Portopts) of @@ -985,6 +990,25 @@ drv_command({Driver, Portopts}, Command, Validated, R) -> Error -> Error end. +drv_command_nt(Port, Command, R) when is_port(Port) -> + Save = erlang:dt_spread_tag(false), + try erlang:port_command(Port, Command) of + true -> + drv_get_response(Port, R) + catch + error:badarg -> + try erlang:iolist_size(Command) of + _ -> % Valid + {error, einval} + catch + error:_ -> + {error, badarg} + end; + error:Reason -> + {error, Reason} + after + erlang:dt_restore_tag(Save) + end. diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk index ae92fcb11b..04f4b22421 100644 --- a/lib/asn1/vsn.mk +++ b/lib/asn1/vsn.mk @@ -1,2 +1,2 @@ -#next version number to use is 1.6.15 | 1.7 | 2.0 -ASN1_VSN = 1.6.19 +#next version number to use is 2.0 +ASN1_VSN = 1.7 diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml index 8505ee8469..c5b4fd0073 100644 --- a/lib/common_test/doc/src/ct_hooks_chapter.xml +++ b/lib/common_test/doc/src/ct_hooks_chapter.xml @@ -429,6 +429,16 @@ terminate(State) -> <seealso marker="sasl:sasl_app">SASL</seealso> events report using the normal SASL mechanisms. </cell> </row> + <row> + <cell>cth_surefire</cell> + <cell>no</cell> + <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> + Surefire XML can forinstance be used by Jenkins to display test + results.</cell> + </row> </table> </section> diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile index 125aa828fb..e9555de35a 100644 --- a/lib/common_test/src/Makefile +++ b/lib/common_test/src/Makefile @@ -69,7 +69,8 @@ MODULES= \ ct_slave \ ct_hooks\ ct_hooks_lock\ - cth_log_redirect + cth_log_redirect\ + cth_surefire TARGET_MODULES= $(MODULES:%=$(EBIN)/%) BEAM_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src index 7fba484b18..bdd48fbc6b 100644 --- a/lib/common_test/src/common_test.app.src +++ b/lib/common_test/src/common_test.app.src @@ -25,6 +25,8 @@ ct_framework, ct_ftp, ct_gen_conn, + ct_hooks, + ct_hooks_lock, ct_logs, ct_make, ct_master, @@ -45,7 +47,9 @@ ct_config, ct_config_plain, ct_config_xml, - ct_slave + ct_slave, + cth_log_redirect, + cth_surefire ]}, {registered, [ct_logs, ct_util_server, diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl new file mode 100644 index 0000000000..c42f956b3a --- /dev/null +++ b/lib/common_test/src/cth_surefire.erl @@ -0,0 +1,199 @@ +%%% @doc Common Test Framework functions handling test specifications. +%%% +%%% <p>This module creates a junit report of the test run if plugged in +%%% as a suite_callback.</p> + +-module(cth_surefire). + +%% Suite Callbacks +-export([id/1, init/2]). + +-export([pre_init_per_suite/3]). +-export([post_init_per_suite/4]). +-export([pre_end_per_suite/3]). +-export([post_end_per_suite/4]). + +-export([pre_init_per_group/3]). +-export([post_init_per_group/4]). +-export([pre_end_per_group/3]). +-export([post_end_per_group/4]). + +-export([pre_init_per_testcase/3]). +-export([post_end_per_testcase/4]). + +-export([on_tc_fail/3]). +-export([on_tc_skip/3]). + +-export([terminate/1]). + +-record(state, { filepath, axis, properties, package, hostname, + curr_suite, curr_suite_ts, curr_group = [], curr_tc, + curr_log_dir, timer, tc_log, + test_cases = [], + test_suites = [] }). + +-record(testcase, { log, group, classname, name, time, failure, timestamp }). +-record(testsuite, { errors, failures, hostname, name, tests, + time, timestamp, id, package, + properties, testcases }). + +id(Opts) -> + filename:absname(proplists:get_value(path, Opts, "junit_report.xml")). + +init(Path, Opts) -> + {ok, Host} = inet:gethostname(), + #state{ filepath = Path, + hostname = proplists:get_value(hostname,Opts,Host), + package = proplists:get_value(package,Opts), + axis = proplists:get_value(axis,Opts,[]), + properties = proplists:get_value(properties,Opts,[]), + timer = now() }. + +pre_init_per_suite(Suite,Config,State) -> + {Config, init_tc(State#state{ curr_suite = Suite, curr_suite_ts = now() }, + Config) }. + +post_init_per_suite(_Suite,Config, Result, State) -> + {Result, end_tc(init_per_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]}}. + +pre_init_per_group(Group,Config,State) -> + {Config, init_tc(State#state{ curr_group = [Group|State#state.curr_group]}, + Config)}. + +post_init_per_group(_Group,Config,Result,State) -> + {Result, end_tc(init_per_group,Config,Result,State)}. + +pre_end_per_group(_Group,Config,State) -> {Config, init_tc(State, Config)}. + +post_end_per_group(_Group,Config,Result,State) -> + NewState = end_tc(end_per_group, Config, Result, State), + {Result, NewState#state{ curr_group = tl(NewState#state.curr_group)}}. + +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) -> + TCs = State#state.test_cases, + TC = hd(State#state.test_cases), + NewTC = TC#testcase{ failure = + {fail,lists:flatten(io_lib:format("~p",[Res]))} }, + State#state{ test_cases = [NewTC | tl(TCs)]}. + +on_tc_skip(_Tc, Res, State) -> + TCs = State#state.test_cases, + TC = hd(State#state.test_cases), + NewTC = TC#testcase{ + failure = + {skipped,lists:flatten(io_lib:format("~p",[Res]))} }, + State#state{ test_cases = [NewTC | tl(TCs)]}. + +init_tc(State, Config) -> + State#state{ timer = now(), + 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); +end_tc(Name, _Config, _Res, State = #state{ curr_suite = Suite, + curr_group = Groups, + timer = TS, tc_log = Log } ) -> + ClassName = atom_to_list(Suite), + PGroup = string:join([ atom_to_list(Group)|| + Group <- lists:reverse(Groups)],"."), + TimeTakes = io_lib:format("~f",[timer:now_diff(now(),TS) / 1000000]), + State#state{ test_cases = [#testcase{ log = Log, + timestamp = now_to_string(TS), + classname = ClassName, + group = PGroup, + name = Name, + time = TimeTakes, + failure = passed }| State#state.test_cases]}. + +get_suite(State, TCs) -> + 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]), + io:format(D, "<?xml version=\"1.0\" encoding= \"UTF-8\" ?>", []), + io:format(D, to_xml(State), []), + catch file:sync(D), + catch file:close(D). + +to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, timestamp = TS, failure = F}) -> + ["<testcase ", + [["group=\"",Group,"\""]||Group /= ""]," " + "name=\"",N,"\" " + "time=\"",T,"\" " + "timestamp=\"",TS,"\" " + "log=\"",L,"\">", + case F of + passed -> + []; + {skipped,Reason} -> + ["<skipped type=\"skip\" message=\"Test ",N," in ",CL, + " skipped!\">", sanitize(Reason),"</skipped>"]; + {fail,Reason} -> + ["<failure message=\"Test ",N," in ",CL," failed!\" type=\"crash\">", + sanitize(Reason),"</failure>"] + end,"</testcase>"]; +to_xml(#testsuite{ package = P, hostname = H, errors = E, time = Time, + timestamp = TS, tests = T, name = N, testcases = Cases }) -> + ["<testsuite ", + [["package=\"",P,"\" "]||P /= undefined], + [["hostname=\"",P,"\" "]||H /= undefined], + [["name=\"",N,"\" "]||N /= undefined], + [["time=\"",Time,"\" "]||Time /= undefined], + [["timestamp=\"",TS,"\" "]||TS /= undefined], + "errors=\"",integer_to_list(E),"\" " + "tests=\"",integer_to_list(T),"\">", + [to_xml(Case) || Case <- Cases], + "</testsuite>"]; +to_xml(#state{ test_suites = TestSuites, axis = Axis, properties = Props }) -> + ["<testsuites>",properties_to_xml(Axis,Props), + [to_xml(TestSuite) || TestSuite <- TestSuites],"</testsuites>"]. + +properties_to_xml(Axis,Props) -> + ["<properties>", + [["<property name=\"",Name,"\" axis=\"yes\" value=\"",Value,"\" />"] || {Name,Value} <- Axis], + [["<property name=\"",Name,"\" value=\"",Value,"\" />"] || {Name,Value} <- Props], + "</properties>" + ]. + +sanitize([$>|T]) -> + ">" ++ sanitize(T); +sanitize([$<|T]) -> + "<" ++ sanitize(T); +sanitize([$"|T]) -> + """ ++ sanitize(T); +sanitize([$'|T]) -> + "'" ++ sanitize(T); +sanitize([$&|T]) -> + "&" ++ sanitize(T); +sanitize([H|T]) -> + [H|sanitize(T)]; +sanitize([]) -> + []. + +now_to_string(Now) -> + {{YY,MM,DD},{HH,Mi,SS}} = calendar:now_to_local_time(Now), + io_lib:format("~p-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B",[YY,MM,DD,HH,Mi,SS]). diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl index 1bb73a43b9..31a856545f 100644 --- a/lib/debugger/src/dbg_iserver.erl +++ b/lib/debugger/src/dbg_iserver.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-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,10 +188,7 @@ handle_call({new_break, Point, Options}, _From, State) -> handle_call(all_breaks, _From, State) -> {reply, State#state.breaks, State}; handle_call({all_breaks, Mod}, _From, State) -> - Reply = lists:filter(fun({{M,_L}, _Options}) -> - M =/= Mod - end, - State#state.breaks), + Reply = [Break || Break = {{M, _},_} <- State#state.breaks, M =:= Mod], {reply, Reply, State}; %% From Meta process diff --git a/lib/debugger/test/int_break_SUITE.erl b/lib/debugger/test/int_break_SUITE.erl index 159678a1f9..7bb0fc2018 100644 --- a/lib/debugger/test/int_break_SUITE.erl +++ b/lib/debugger/test/int_break_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 @@ -76,6 +76,9 @@ basic(Config) when list(Config) -> ?line S3 = [xxx,y] = ordsets1:add_element(y, S2), ?line ok = i:ib(ordsets1, union, 2), ?line [xxx,y,z] = ordsets1:union(S3, [z]), + All = [{{ordsets1,86}, _}, {{ordsets1,_},_}|_] = lists:sort(int:all_breaks()), + [] = lists:sort(int:all_breaks(foobar)), + All = lists:sort(int:all_breaks(ordsets1)), ok. cleanup(doc) -> "Make sure that the auto-attach flag is turned off."; diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 845df0ca61..0c2e846010 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -2,7 +2,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 @@ -241,6 +241,7 @@ type(erl_ddll, try_unload, 2, Xs) -> %%-- erlang ------------------------------------------------------------------- type(erlang, halt, 0, _) -> t_none(); type(erlang, halt, 1, _) -> t_none(); +type(erlang, halt, 2, _) -> t_none(); type(erlang, exit, 1, _) -> t_none(); %% Note that exit/2 sends an exit signal to another process. type(erlang, exit, 2, _) -> t_atom('true'); @@ -709,6 +710,27 @@ type(erlang, display_nl, 0, _) -> t_atom('true'); type(erlang, dist_exit, 3, Xs) -> strict(arg_types(erlang, dist_exit, 3), Xs, fun (_) -> t_atom('true') end); +type(erlang, dt_append_vm_tag_data, 1, Xs) -> + strict(arg_types(erlang, dt_append_vm_tag_data, 1), + Xs, + fun(_) -> t_iodata() end); +type(erlang, dt_get_tag, 0, _) -> + t_sup(t_binary(), t_atom('undefined')); +type(erlang, dt_get_tag_data, 0, _) -> + t_sup(t_binary(), t_atom('undefined')); +type(erlang, dt_prepend_vm_tag_data, 1, Xs) -> + strict(arg_types(erlang, dt_prepend_vm_tag_data, 1), + Xs, + fun(_) -> t_iodata() end); +type(erlang, dt_put_tag, 1, Xs) -> + strict(arg_types(erlang, dt_put_tag, 1), Xs, + fun(_) -> t_sup(t_binary(), t_atom('undefined')) end); +type(erlang, dt_restore_tag, 1, Xs) -> + strict(arg_types(erlang, dt_restore_tag, 1), Xs, fun(_) -> t_atom('true') end); +type(erlang, dt_spread_tag, 1, Xs) -> + strict(arg_types(erlang, dt_spread_tag, 1), Xs, + fun(_) -> t_sup(t_tuple([t_non_neg_integer(), t_sup(t_binary(), t_nil())]), + t_atom('true')) end); type(erlang, element, 2, Xs) -> strict(arg_types(erlang, element, 2), Xs, fun ([X1, X2]) -> @@ -3494,6 +3516,20 @@ arg_types(erlang, display_string, 1) -> [t_string()]; arg_types(erlang, dist_exit, 3) -> [t_pid(), t_dist_exit(), t_sup(t_pid(), t_port())]; +arg_types(erlang, dt_append_vm_tag_data, 1) -> + [t_iodata()]; +arg_types(erlang, dt_get_tag, 0) -> + []; +arg_types(erlang, dt_get_tag_data, 0) -> + []; +arg_types(erlang, dt_prepend_vm_tag_data, 1) -> + [t_iodata()]; +arg_types(erlang, dt_put_tag, 1) -> + [t_sup(t_binary(), t_atom('undefined'))]; +arg_types(erlang, dt_restore_tag, 1) -> + [t_sup(t_tuple([t_non_neg_integer(), t_sup(t_binary(), t_nil())]), t_atom('true'))]; +arg_types(erlang, dt_spread_tag, 1) -> + [t_boolean()]; arg_types(erlang, element, 2) -> [t_pos_fixnum(), t_tuple()]; arg_types(erlang, erase, 0) -> @@ -3553,7 +3589,10 @@ arg_types(erlang, group_leader, 2) -> arg_types(erlang, halt, 0) -> []; arg_types(erlang, halt, 1) -> - [t_sup(t_non_neg_fixnum(), t_string())]; + [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()])]; +arg_types(erlang, halt, 2) -> + [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()]), + t_list(t_tuple([t_atom('flush'), t_boolean()]))]; arg_types(erlang, hash, 2) -> [t_any(), t_integer()]; arg_types(erlang, hd, 1) -> diff --git a/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src b/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src index d5277eb256..53543a5bf0 100644 --- a/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src +++ b/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src @@ -122,24 +122,33 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(PGMS) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.built_erl +$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.built_c +$(OBJS): $(GEN_C_FILES) $(GEN_H_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c $(PGMS): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.idl +c_erl_test.built_c: c_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" c_erl_test.idl + echo done > c_erl_test.built_c -$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.idl +c_erl_test.built_erl: c_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" c_erl_test.idl + echo done > c_erl_test.built_erl -.c@obj@: +.c@obj@: $(CC) -c -o $*@obj@ $(CFLAGS) $< -.erl.@EMULATOR@: +.erl.@EMULATOR@: $(ERLC) -I $(IC_INCLUDE_PATH) $< diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src b/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src index 8bc1a907a7..7d32ee4e87 100644 --- a/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src +++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src @@ -122,25 +122,34 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(PGMS) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.built_erl +$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.built_c +$(OBJS): $(GEN_C_FILES) $(GEN_H_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c $(PGMS): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.idl +c_erl_test.built_c: c_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" \ "+{user_protocol,my}" c_erl_test.idl + echo done > c_erl_test.built_c -$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.idl +c_erl_test.built_erl: c_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" c_erl_test.idl + echo done > c_erl_test.built_erl -.c@obj@: +.c@obj@: $(CC) -c -o $*@obj@ $(CFLAGS) $< -.erl.@EMULATOR@: +.erl.@EMULATOR@: $(ERLC) -I $(IC_INCLUDE_PATH) $< diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src index 2585341791..19c67734ca 100644 --- a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src +++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src @@ -122,25 +122,33 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(PGMS) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.built_erl +$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.built_c +$(OBJS): $(GEN_C_FILES) $(GEN_H_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c $(PGMS): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.idl - $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" \ - "+{user_protocol,my}" "+{c_timeout,{5000,5000}}" c_erl_test.idl +c_erl_test.built_c: c_erl_test.idl + $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" c_erl_test.idl + echo done > c_erl_test.built_c -$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.idl +c_erl_test.built_erl: c_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" c_erl_test.idl + echo done > c_erl_test.built_erl -.c@obj@: +.c@obj@: $(CC) -c -o $*@obj@ $(CFLAGS) $< -.erl.@EMULATOR@: +.erl.@EMULATOR@: $(ERLC) -I $(IC_INCLUDE_PATH) $< diff --git a/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src b/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src index 50cf9d4445..ac53d0a657 100644 --- a/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src +++ b/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src @@ -122,25 +122,34 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(PGMS) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.built_erl +$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.built_c +$(OBJS): $(GEN_C_FILES) $(GEN_H_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c $(PGMS): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(GEN_C_FILES) $(GEN_H_FILES): erl_c_test.idl +c_erl_test.built_c: erl_c_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_server}" \ "+{scoped_op_calls,true}" erl_c_test.idl + echo done > c_erl_test.built_c # If we have scoped operation calls for C, we must have that for # Erlang as well, if we use the m_i.erl file for calling the server. -$(GEN_ERL_FILES) $(GEN_HRL_FILES): erl_c_test.idl +c_erl_test.built_erl: erl_c_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" \ "+{scoped_op_calls,true}" "+{timeout,true}" erl_c_test.idl + echo done > c_erl_test.built_erl .c@obj@: $(CC) -c -o $*@obj@ $(CFLAGS) $< diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src b/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src index 6c7701ca50..d23de04944 100644 --- a/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src +++ b/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src @@ -122,25 +122,34 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(PGMS) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.built_erl +$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.built_c +$(OBJS): $(GEN_C_FILES) $(GEN_H_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c $(PGMS): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(GEN_C_FILES) $(GEN_H_FILES): erl_c_test.idl +c_erl_test.built_c: erl_c_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_server}" \ "+{scoped_op_calls,true}" erl_c_test.idl + echo done > c_erl_test.built_c # If we have scoped operation calls for C, we must have that for # Erlang as well, if we use the m_i.erl file for calling the server. -$(GEN_ERL_FILES) $(GEN_HRL_FILES): erl_c_test.idl +c_erl_test.built_erl: erl_c_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" \ "+{scoped_op_calls,true}" "+{timeout,true}" erl_c_test.idl + echo done > c_erl_test.built_erl .c@obj@: $(CC) -c -o $*@obj@ $(CFLAGS) $< diff --git a/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src b/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src index 5e190fe1a5..0d84a62270 100644 --- a/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src +++ b/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src @@ -69,20 +69,29 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(CLASS_FILES) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): java_erl_test.built_erl +$(GEN_JAVA_FILES): java_erl_test.built_java +$(CLASS_FILES): $(GEN_JAVA_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(GEN_JAVA_FILES) $(CLASS_FILES) \ - $(GEN_ERL_FILES) $(GEN_HRL_FILES) $(EBINS) + $(GEN_ERL_FILES) $(GEN_HRL_FILES) $(EBINS) \ + java_erl_test.built_erl java_erl_test.built_java -del /F /Q $(GEN_JAVA_FILES) $(CLASS_FILES) \ - $(GEN_ERL_FILES) $(GEN_HRL_FILES) $(EBINS) + $(GEN_ERL_FILES) $(GEN_HRL_FILES) $(EBINS) \ + java_erl_test.built_erl java_erl_test.built_java -$(GEN_JAVA_FILES) : java_erl_test.idl +java_erl_test.built_java: java_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,java}" java_erl_test.idl + echo done > java_erl_test.built_java $(CLASS_FILES) : $(JAVA_FILES) $(JAVAC) -classpath $(CLASSPATH) $(JAVA_FILES) -$(GEN_ERL_FILES) $(GEN_HRL_FILES): java_erl_test.idl +java_erl_test.built_erl: java_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" java_erl_test.idl + echo done > java_erl_test.built_erl .erl.@EMULATOR@: $(ERLC) -I $(IC_INCLUDE_PATH) $< diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile index 53d505b102..c4152a1d72 100644 --- a/lib/inets/doc/src/Makefile +++ b/lib/inets/doc/src/Makefile @@ -48,6 +48,7 @@ XML_REF3_FILES = \ inets.xml \ ftp.xml \ tftp.xml \ + http_uri.xml\ httpc.xml\ httpd.xml \ httpd_conf.xml \ diff --git a/lib/inets/doc/src/book.xml b/lib/inets/doc/src/book.xml index 7da0abd98f..51cbb2d963 100644 --- a/lib/inets/doc/src/book.xml +++ b/lib/inets/doc/src/book.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE book SYSTEM "book.dtd"> <book xmlns:xi="http://www.w3.org/2001/XInclude"> <header titlestyle="normal"> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/fascicules.xml b/lib/inets/doc/src/fascicules.xml index 101e745722..ea3b988882 100644 --- a/lib/inets/doc/src/fascicules.xml +++ b/lib/inets/doc/src/fascicules.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE fascicules SYSTEM "fascicules.dtd"> <fascicules> diff --git a/lib/inets/doc/src/ftp_client.xml b/lib/inets/doc/src/ftp_client.xml index 7f62a453a6..b44674d997 100644 --- a/lib/inets/doc/src/ftp_client.xml +++ b/lib/inets/doc/src/ftp_client.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml new file mode 100644 index 0000000000..bd31ae42d2 --- /dev/null +++ b/lib/inets/doc/src/http_uri.xml @@ -0,0 +1,160 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2012</year><year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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. + + </legalnotice> + + <title>http_uri</title> + <prepared></prepared> + <responsible></responsible> + <docno></docno> + <date></date> + <rev></rev> + </header> + + <module>http_uri</module> + <modulesummary>URI utility module</modulesummary> + + <description> + <p>This module provides utility functions for working with URIs, + according to RFC 3986. </p> + + </description> + + <section> + <title>COMMON DATA TYPES </title> + <p>Type definitions that are used more than once in + this module:</p> + <code type="none"><![CDATA[ +boolean() = true | false +string() = list of ASCII characters + ]]></code> + + </section> + + <section> + <title>URI DATA TYPES </title> + <p>Type definitions that are related to URI:</p> + <p>For more information about URI, see RFC 3986. </p> + + <code type="none"><![CDATA[ +uri() = string() - Syntax according to the URI definition in rfc 3986, ex: "http://www.erlang.org/" +user_info() = string() +scheme() = atom() - Example: http, https +host() = string() +port() = pos_integer() +path() = string() - Representing a file path or directory path +query() = string() + ]]></code> + + <marker id="scheme_defaults"></marker> + </section> + + <funcs> + <func> + <name>scheme_defaults() -> SchemeDefaults</name> + <fsummary>A list of scheme and their default ports</fsummary> + <type> + <v>SchemeDefaults = [{scheme(), default_scheme_port_number()}] </v> + <v>default_scheme_port_number() = pos_integer()</v> + </type> + <desc> + <p>This function provides a list of the scheme and their default + port numbers currently supported (by default) by this utility. </p> + + <marker id="parse"></marker> + </desc> + </func> + + <func> + <name>parse(URI) -> {ok, Result} | {error, Reason}</name> + <name>parse(URI, Options) -> {ok, Result} | {error, Reason}</name> + <fsummary>Parse an URI</fsummary> + <type> + <v>URI = uri() </v> + <v>Options = [Option] </v> + <v>Option = {ipv6_host_with_brackets, boolean()} | + {scheme_defaults, scheme_defaults()}]</v> + <v>Result = {Scheme, UserInfo, Host, Port, Path, Query}</v> + <v>UserInfo = user_info()</v> + <v>Host = host()</v> + <v>Port = pos_integer()</v> + <v>Path = path()</v> + <v>Query = query()</v> + <v>Reason = term() </v> + </type> + <desc> + <p>This function is used to parse an URI. If no scheme defaults + are provided, the value of + <seealso marker="#scheme_defaults">scheme_defaults</seealso> + function will be used. </p> + + <p>Note that when parsing an URI with an unknown scheme (that is, + a scheme not found in the scheme defaults) a port number must be + provided or else the parsing will fail. </p> + + <marker id="encode"></marker> + </desc> + </func> + + <func> + <name>encode(URI) -> HexEncodedURI</name> + + <fsummary>Hex encode an URI</fsummary> + <type> + <v>URI = uri()</v> + <v>HexEncodedURI = string() - Hex encoded uri</v> + </type> + + <desc> + <p>Hex encode an URI. </p> + + <marker id="decode"></marker> + </desc> + </func> + + <func> + <name>decode(HexEncodedURI) -> URI</name> + + <fsummary>Decode a hex encoded URI</fsummary> + <type> + <v>HexEncodedURI = string() - A possibly hex encoded uri</v> + <v>URI = uri()</v> + </type> + + <desc> + <p>Decode a possibly hex encoded URI. </p> + + </desc> + </func> + + </funcs> + +<!-- + <section> + <title>SEE ALSO</title> + <p>RFC 2616, <seealso marker="inets">inets(3)</seealso>, + <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>, + <seealso marker="ssl:ssl">ssl(3)</seealso> + </p> + </section> +--> + +</erlref> diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 48a2089605..70c845bade 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2004</year><year>2011</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -541,6 +541,35 @@ apply(Module, Function, [ReplyInfo | Args]) default value of the <c>max_sessions</c> option. </p> </note> + <marker id="get_options"></marker> + </desc> + </func> + + <func> + <name>get_options(OptionItems) -> {ok, Values} | {error, Reason}</name> + <name>get_options(OptionItems, Profile) -> {ok, Values} | {error, Reason}</name> + <fsummary>Gets the currently used options.</fsummary> + <type> + <v>OptionItems = all | [option_item()]</v> + <v>option_item() = proxy | + max_sessions | + keep_alive_timeout | + max_keep_alive_length | + pipeline_timeout | + max_pipeline_length | + cookies | + ipfamily | + ip | + port | + socket_opts | + verbose</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + <v>Values = [{option_item(), term()}]</v> + <v>Reason = term() </v> + </type> + <desc> + <p>Retrieves the options currently used by the client.</p> + <marker id="stream_next"></marker> </desc> </func> diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index f88099a82e..7e21229fcf 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -55,14 +55,14 @@ <section> <title>ERLANG HTTP SERVER SERVICE START/STOP </title> <p>A web server can be configured to start when starting the inets - application or started dynamically in runtime by calling the - Inets application API <c>inets:start(httpd, ServiceConfig)</c>, or - <c>inets:start(httpd, ServiceConfig, How)</c>, - see <seealso marker="inets">inets(3)</seealso> Below follows a - description of the available configuration options, also called - properties.</p> - - <marker id="file_prop"></marker> + application or started dynamically in runtime by calling the + Inets application API <c>inets:start(httpd, ServiceConfig)</c>, or + <c>inets:start(httpd, ServiceConfig, How)</c>, + see <seealso marker="inets">inets(3)</seealso> Below follows a + description of the available configuration options, also called + properties.</p> + + <marker id="props_file"></marker> <p><em>File properties</em></p> <p>When the web server is started @@ -76,21 +76,25 @@ list.</p> <taglist> + <marker id="prop_proplist_file"></marker> <tag>{proplist_file, path()}</tag> <item> - If this property is defined inets will expect to find - all other properties defined in this file. Note that the - file must include all properties listed under mandatory - properties. </item> - <tag>{file, path()}</tag> + <p>If this property is defined inets will expect to find + all other properties defined in this file. Note that the + file must include all properties listed under mandatory + properties. </p> + </item> - <item> If this property is defined - inets will expect to find all other properties defined in this - file, that uses Apache like syntax. Note that the file must - include all properties listed under mandatory properties. The - Apache like syntax is the property, written as one word where - each new word begins with a capital, followed by a white-space - followed by the value followed by a new line. Ex: + <marker id="prop_file"></marker> + <tag>{file, path()}</tag> + <item> + <p>If this property is defined inets will expect to find all + other properties defined in this file, that uses Apache like + syntax. Note that the file must include all properties listed + under mandatory properties. The Apache like syntax is the property, + written as one word where each new word begins with a capital, + followed by a white-space followed by the value followed by a + new line. Ex: </p> <code> {server_root, "/urs/local/www"} -> ServerRoot /usr/local/www @@ -114,40 +118,51 @@ <p>The properties proplist_file and file are mutually exclusive.</p> </note> - <marker id="mand_prop"></marker> + <marker id="props_mand"></marker> <p><em>Mandatory properties</em></p> <taglist> + <marker id="prop_port"></marker> <tag>{port, integer()} </tag> <item> - The port that the HTTP server shall listen on. - If zero is specified as port, an arbitrary available port - will be picked and you can use the httpd:info/2 function to find - out which port was picked. </item> + <p>The port that the HTTP server shall listen on. + If zero is specified as port, an arbitrary available port + will be picked and you can use the httpd:info/2 function to find + out which port was picked. </p> + </item> + + <marker id="prop_server_name"></marker> <tag>{server_name, string()} </tag> <item> - The name of your server, normally a fully qualified domain - name. + <p>The name of your server, normally a fully qualified domain name. </p> </item> + + <marker id="prop_server_root"></marker> <tag>{server_root, path()} </tag> <item> - Defines the servers home directory where log files etc can + <p>Defines the servers home directory where log files etc can be stored. Relative paths specified in other properties refer - to this directory.</item> + to this directory. </p> + </item> + + <marker id="prop_doc_root"></marker> <tag>{document_root, path()}</tag> <item> Defines the top directory for the documents that - are available on the HTTP server.</item> + are available on the HTTP server. + </item> </taglist> - <marker id="comm_prop"></marker> + <marker id="props_comm"></marker> <p><em>Communication properties</em> </p> <taglist> + <marker id="prop_bind_address"></marker> <tag>{bind_address, ip_address() | hostname() | any} </tag> <item> - Defaults to <c>any</c>. Note that <c>any</c> is denoted <em>*</em> - in the apache like configuration file. + <p>Defaults to <c>any</c>. Note that <c>any</c> is denoted <em>*</em> + in the apache like configuration file. </p> </item> + <marker id="prop_socket_type"></marker> <tag>{socket_type, ip_comm | ssl | essl}</tag> <item> <p>When using ssl, there are currently only one alternative. @@ -156,6 +171,7 @@ <p>Defaults to <c>ip_comm</c>. </p> </item> + <marker id="prop_ipfamily"></marker> <tag>{ipfamily, inet | inet6 | inet6fb4}</tag> <item> <p>Defaults to <c>inet6fb4. </c> </p> @@ -165,74 +181,93 @@ </taglist> + <marker id="props_api_modules"></marker> <p><em>Erlang Web server API modules</em> </p> <taglist> + <marker id="prop_modules"></marker> <tag>{modules, [atom()]} </tag> <item> - Defines which modules the HTTP server will use to handle + <p>Defines which modules the HTTP server will use to handle requests. Defaults to: <c>[mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi, mod_dir, mod_get, mod_head, mod_log, mod_disk_log] </c> Note that some mod-modules are dependent on others, so the order can not be entirely arbitrary. See the <seealso marker="http_server"> Inets Web server Modules in the - Users guide</seealso> for more information. + Users guide</seealso> for more information. </p> </item> - </taglist> + </taglist> - <marker id="limit_prop"></marker> + <marker id="props_limit"></marker> <p><em>Limit properties</em> </p> <taglist> + <marker id="prop_disable_chunked_encoding"></marker> <tag>{disable_chunked_transfer_encoding_send, boolean()}</tag> <item> - This property allows you to disable chunked + <p>This property allows you to disable chunked transfer-encoding when sending a response to a HTTP/1.1 - client, by default this is false.</item> + client, by default this is false. </p> + </item> + <marker id="prop_keep_alive"></marker> <tag>{keep_alive, boolean()}</tag> <item> - Instructs the server whether or not to use persistent + <p>Instructs the server whether or not to use persistent connections when the client claims to be HTTP/1.1 - compliant, default is true.</item> + compliant, default is true. </p> + </item> + <marker id="prop_keep_alive_timeout"></marker> <tag>{keep_alive_timeout, integer()}</tag> <item> - The number of seconds the server will wait for a + <p>The number of seconds the server will wait for a subsequent request from the client before closing the - connection. Default is 150.</item> + connection. Default is 150. </p> + </item> + <marker id="prop_max_body_size"></marker> <tag>{max_body_size, integer()}</tag> <item> - Limits the size of the message body of HTTP request. - By the default there is no limit.</item> + <p>Limits the size of the message body of HTTP request. + By the default there is no limit. </p> + </item> + <marker id="prop_max_clients"></marker> <tag>{max_clients, integer()}</tag> <item> - Limits the number of simultaneous requests that can be - supported. Defaults to 150. </item> + <p>Limits the number of simultaneous requests that can be + supported. Defaults to 150. </p> + </item> + <marker id="prop_max_header_size"></marker> <tag>{max_header_size, integer()}</tag> <item> - Limits the size of the message header of HTTP request. - Defaults to 10240. + <p>Limits the size of the message header of HTTP request. + Defaults to 10240. </p> </item> + <marker id="prop_max_uri"></marker> <tag>{max_uri, integer()}</tag> <item> - Limits the size of the HTTP request URI. By - default there is no limit.</item> + <p>Limits the size of the HTTP request URI. By + default there is no limit. </p> + </item> + <marker id="prop_max_keep_alive_req"></marker> <tag>{max_keep_alive_requests, integer()}</tag> - <item> The number of request that a client can do on one + <item> + <p>The number of request that a client can do on one connection. When the server has responded to the number of requests defined by max_keep_alive_requests the server close the connection. The server will close it even if there are queued - request. Defaults to no limit.</item> + request. Defaults to no limit. </p> + </item> </taglist> - <marker id="admin_prop"></marker> + <marker id="props_admin"></marker> <p><em>Administrative properties</em></p> <taglist> + <marker id="prop_mime_types"></marker> <tag>{mime_types, [{MimeType, Extension}] | path()}</tag> <item> <p>Where MimeType = string() and Extension = string(). @@ -250,19 +285,43 @@ text/plain asc txt <p>Defaults to [{"html","text/html"},{"htm","text/html"}]</p> </item> + <marker id="prop_mime_type"></marker> <tag>{mime_type, string()}</tag> - <item> - When the server is asked to provide a document type which + <p>When the server is asked to provide a document type which cannot be determined by the MIME Type Settings, the server will - use this default type. </item> + use this default type. </p> + </item> + <marker id="prop_server_admin"></marker> <tag>{server_admin, string()}</tag> <item> - ServerAdmin defines the email-address of the server + <p>ServerAdmin defines the email-address of the server administrator, to be included in any error messages returned by - the server.</item> + the server. </p> + </item> + <marker id="prop_server_tokens"></marker> + <tag>{server_tokens, prod|major|minor|minimal|os|full|{private, string()}}</tag> + <item> + <p>ServerTokens defines how the value of the server header + should look. </p> + <p>Example: Assuming the version of inets is 5.8.1, + here is what the server header string could look like for + the different values of server-tokens: </p> + <pre> +prod "inets" +major "inets/5" +minor "inets/5.8" +minimal "inets/5.8.1" +os "inets/5.8.1 (unix)" +full "inets/5.8.1 (unix/linux) OTP/R15B" +{private, "foo/bar"} "foo/bar" + </pre> + <p>By default, the value is as before, which is <c>minimal</c>. </p> + </item> + + <marker id="prop_log_format"></marker> <tag>{log_format, common | combined}</tag> <item> <p>Defines if access logs should be written according to the common @@ -307,8 +366,9 @@ bytes <p>This affects the access logs written by mod_log and mod_disk_log. </p> - </item> - + </item> + + <marker id="prop_elog_format"></marker> <tag>{error_log_format, pretty | compact}</tag> <item> <p>Defaults to pretty. If the error log is meant to be read @@ -330,63 +390,77 @@ bytes </taglist> - <marker id="ssl_prop"></marker> + <marker id="props_ssl"></marker> <p><em>ssl properties</em></p> <taglist> + <marker id="prop_ssl_ca_cert_file"></marker> <tag>{ssl_ca_certificate_file, path()}</tag> <item> - Used as cacertfile option in ssl:listen/2 see - <seealso marker="ssl:ssl">ssl(3)</seealso> </item> + <p>Used as cacertfile option in ssl:listen/2 see + <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> + </item> + <marker id="prop_ssl_cert_file"></marker> <tag>{ssl_certificate_file, path()}</tag> <item> - Used as certfile option in ssl:listen/2 see - <seealso marker="ssl:ssl">ssl(3)</seealso> + <p>Used as certfile option in ssl:listen/2 see + <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> </item> + <marker id="prop_ssl_ciphers"></marker> <tag>{ssl_ciphers, list()}</tag> <item> - Used as ciphers option in ssl:listen/2 see - <seealso marker="ssl:ssl">ssl(3)</seealso> + <p>Used as ciphers option in ssl:listen/2 see + <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> </item> + <marker id="prop_ssl_verify_client"></marker> <tag>{ssl_verify_client, integer()}</tag> <item> - Used as verify option in ssl:listen/2 see - <seealso marker="ssl:ssl">ssl(3)</seealso> </item> + <p>Used as verify option in ssl:listen/2 see + <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> + </item> + <marker id="prop_ssl_verify_depth"></marker> <tag>{ssl_verify_depth, integer()}</tag> <item> - Used as depth option in ssl:listen/2 see - <seealso marker="ssl:ssl">ssl(3)</seealso> </item> + <p>Used as depth option in ssl:listen/2 see + <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> + </item> + <marker id="prop_ssl_passwd_callback_funct"></marker> <tag>{ssl_password_callback_function, atom()}</tag> <item> - Used together with ssl_password_callback_module + <p>Used together with ssl_password_callback_module to retrieve a value to use as password option to ssl:listen/2 - see <seealso marker="ssl:ssl">ssl(3)</seealso> + see <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> </item> + <marker id="prop_ssl_passwd_callback_args"></marker> <tag>{ssl_password_callback_arguments, list()}</tag> <item> - Used together with ssl_password_callback_function to supply a + <p>Used together with ssl_password_callback_function to supply a list of arguments to the callback function. If not specified - the callback function will be assumed to have arity 0. </item> + the callback function will be assumed to have arity 0. </p> + </item> + <marker id="prop_ssl_passwd_callback_mod"></marker> <tag>{ssl_password_callback_module, atom()}</tag> <item> - Used together with ssl_password_callback_function + <p>Used together with ssl_password_callback_function to retrieve a value to use as password option to ssl:listen/2 - see <seealso marker="ssl:ssl">ssl(3)</seealso></item> + see <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> + </item> </taglist> - <marker id="alias_prop"></marker> + <marker id="props_alias"></marker> <p><em>URL aliasing properties - requires mod_alias</em></p> <taglist> + <marker id="prop_alias"></marker> <tag>{alias, {Alias, RealName}}</tag> - - <item> Where Alias = string() and RealName = string(). + <item> + <p>Where Alias = string() and RealName = string(). The Alias property allows documents to be stored in the local file system instead of the document_root location. URLs with a path that begins with url-path is mapped to local files that begins with @@ -395,11 +469,13 @@ bytes <code>{alias, {"/image", "/ftp/pub/image"}</code> and an access to http://your.server.org/image/foo.gif would refer to - the file /ftp/pub/image/foo.gif.</item> + the file /ftp/pub/image/foo.gif. </p> + </item> - <tag>{re_write, {Re, Replacement}}</tag> - - <item> Where Re = string() and Replacement = string(). + <marker id="prop_re_write"></marker> + <tag>{re_write, {Re, Replacement}}</tag> + <item> + <p>Where Re = string() and Replacement = string(). The ReWrite property allows documents to be stored in the local file system instead of the document_root location. URLs are rewritten by re:replace/3 to produce a path in the local filesystem. @@ -419,13 +495,13 @@ bytes Beware of trailing space in Replacement that will be used. If you must have a space in Re use e.g the character encoding - <code>\040</code> see <seealso marker="stdlib:re">re(3)</seealso>. + <code>\040</code> see <seealso marker="stdlib:re">re(3)</seealso>. </p> </item> - <tag>{directory_index, [string()]}</tag> - + <marker id="prop_dir_idx"></marker> + <tag>{directory_index, [string()]}</tag> <item> - DirectoryIndex specifies a list of resources to look for + <p>DirectoryIndex specifies a list of resources to look for if a client requests a directory using a / at the end of the directory name. file depicts the name of a file in the directory. Several files may be given, in which case the server @@ -436,70 +512,79 @@ bytes and access to http://your.server.org/docs/ would return http://your.server.org/docs/index.html or http://your.server.org/docs/welcome.html if index.html do not - exist. + exist. </p> </item> </taglist> - <marker id="cgi_prop"></marker> + <marker id="props_cgi"></marker> <p><em>CGI properties - requires mod_cgi</em></p> <taglist> + <marker id="prop_script_alias"></marker> <tag>{script_alias, {Alias, RealName}}</tag> - <item> Where Alias = string() and RealName = string(). + <item> + <p>Where Alias = string() and RealName = string(). Has the same behavior as the Alias property, except that it also marks the target directory as containing CGI scripts. URLs with a path beginning with url-path are mapped to scripts beginning with directory-filename, for example: - <code> {script_alias, {"/cgi-bin/", "/web/cgi-bin/"}</code> + <code>{script_alias, {"/cgi-bin/", "/web/cgi-bin/"}</code> and an access to http://your.server.org/cgi-bin/foo would cause - the server to run the script /web/cgi-bin/foo. + the server to run the script /web/cgi-bin/foo. </p> </item> + <marker id="prop_script_re_write"></marker> <tag>{script_re_write, {Re, Replacement}}</tag> - <item> Where Re = string() and Replacement = string(). + <item> + <p>Where Re = string() and Replacement = string(). Has the same behavior as the ReWrite property, except that it also marks the target directory as containing CGI scripts. URLs with a path beginning with url-path are mapped to scripts beginning with directory-filename, for example: - <code> {script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}</code> + <code>{script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}</code> and an access to http://your.server.org/cgi-bin/17/foo would cause - the server to run the script /web/17/cgi-bin/foo. + the server to run the script /web/17/cgi-bin/foo. </p> </item> + <marker id="prop_script_nocache"></marker> <tag>{script_nocache, boolean()}</tag> - <item> - If ScriptNoCache is set to true the HTTP server will by + <p>If ScriptNoCache is set to true the HTTP server will by default add the header fields necessary to prevent proxies from caching the page. Generally this is something you want. Defaults - to false.</item> + to false. </p> + </item> + <marker id="prop_script_timeout"></marker> <tag>{script_timeout, integer()}</tag> - <item> - The time in seconds the web server will wait between each + <p>The time in seconds the web server will wait between each chunk of data from the script. If the CGI-script not delivers any data before the timeout the connection to the client will be - closed. Defaults to 15. </item> + closed. Defaults to 15. </p> + </item> + <marker id="prop_action"></marker> <tag>{action, {MimeType, CgiScript}} - requires mod_action</tag> - - <item>Where MimeType = string() and CgiScript = string(). + <item> + <p>Where MimeType = string() and CgiScript = string(). Action adds an action, which will activate a cgi-script whenever a file of a certain mime-type is requested. It propagates the URL and file path of the requested document using the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. - <code> {action, {"text/plain", "/cgi-bin/log_and_deliver_text"} - </code> + + <code>{action, {"text/plain", "/cgi-bin/log_and_deliver_text"}</code> + </p> </item> + <marker id="prop_script"></marker> <tag>{script, {Method, CgiScript}} - requires mod_action</tag> - - <item>Where Method = string() and CgiScript = string(). + <item> + <p>Where Method = string() and CgiScript = string(). Script adds an action, which will activate a cgi-script whenever a file is requested using a certain HTTP method. The method is either GET or POST as defined in RFC 1945. It @@ -507,18 +592,19 @@ bytes the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. - <code> {script, {"PUT", "/cgi-bin/put"} - </code> + <code>{script, {"PUT", "/cgi-bin/put"}</code> + </p> </item> </taglist> - <marker id="esi_prop"></marker> + <marker id="props_esi"></marker> <p><em>ESI properties - requires mod_esi</em></p> <taglist> - <tag>{erl_script_alias, {URLPath, [AllowedModule]}}</tag> - - <item>Where URLPath = string() and AllowedModule = atom(). + <marker id="prop_esi_alias"></marker> + <tag>{erl_script_alias, {URLPath, [AllowedModule]}}</tag> + <item> + <p>Where URLPath = string() and AllowedModule = atom(). erl_script_alias marks all URLs matching url-path as erl scheme scripts. A matching URL is mapped into a specific module and function. For example: @@ -531,140 +617,151 @@ bytes would refer to httpd_example:yahoo/3 or, if that did not exist, httpd_example:yahoo/2 and http://your.server.org/cgi-bin/example/other:yahoo would - not be allowed to execute. + not be allowed to execute. </p> </item> + <marker id="prop_esi_nocache"></marker> <tag>{erl_script_nocache, boolean()}</tag> - <item> - If erl_script_nocache is set to true the server will add + <p>If erl_script_nocache is set to true the server will add http header fields that prevents proxies from caching the page. This is generally a good idea for dynamic content, since - the content often vary between each request. Defaults to false. + the content often vary between each request. + Defaults to false. </p> </item> + <marker id="prop_esi_timeout"></marker> <tag>{erl_script_timeout, integer()}</tag> - <item> - If erl_script_timeout sets the time in seconds the server will + <p>If erl_script_timeout sets the time in seconds the server will wait between each chunk of data to be delivered through mod_esi:deliver/2. Defaults to 15. This is only relevant - for scripts that uses the erl scheme. + for scripts that uses the erl scheme. </p> </item> + <marker id="prop_esi_timeout"></marker> <tag>{eval_script_alias, {URLPath, [AllowedModule]}}</tag> - - <item>Where URLPath = string() and AllowedModule = atom(). + <item> + <p>Where URLPath = string() and AllowedModule = atom(). Same as erl_script_alias but for scripts - using the eval scheme. Note that this is only supported - for backwards compatibility. The eval scheme is deprecated.</item> + using the eval scheme. Note that this is only supported + for backwards compatibility. The eval scheme is deprecated. </p> + </item> </taglist> - <marker id="log_prop"></marker> + <marker id="props_log"></marker> <p><em>Log properties - requires mod_log</em></p> <taglist> + <marker id="prop_elog"></marker> <tag>{error_log, path()}</tag> - <item> - Defines the filename of the error log file to be used to log + <p>Defines the filename of the error log file to be used to log server errors. If the filename does not begin with a slash (/) - it is assumed to be relative to the server_root</item> + it is assumed to be relative to the server_root. </p> + </item> + <marker id="prop_slog"></marker> <tag>{security_log, path()}</tag> - <item> - Defines the filename of the access log file to be used to - log security events. If the filename does not begin with a slash - (/) it is assumed to be relative to the server_root. + <p>Defines the filename of the access log file to be used to + log security events. If the filename does not begin with a slash + (/) it is assumed to be relative to the server_root. </p> </item> + <marker id="prop_tlog"></marker> <tag>{transfer_log, path()}</tag> - <item> - Defines the filename of the access log file to be used to + <p>Defines the filename of the access log file to be used to log incoming requests. If the filename does not begin with a - slash (/) it is assumed to be relative to the server_root. + slash (/) it is assumed to be relative to the server_root. </p> </item> </taglist> - <marker id="dlog_prop"></marker> + <marker id="props_dlog"></marker> <p><em>Disk Log properties - requires mod_disk_log</em></p> <taglist> + <marker id="prop_dlog_format"></marker> <tag>{disk_log_format, internal | external}</tag> - <item> - Defines the file-format of the log files see disk_log for + <p>Defines the file-format of the log files see disk_log for more information. If the internal file-format is used, the logfile will be repaired after a crash. When a log file is repaired data might get lost. When the external file-format is used httpd will not start if the log file is broken. Defaults to - external. + external. </p> </item> + <marker id="prop_edlog"></marker> <tag>{error_disk_log, internal | external}</tag> - <item> - Defines the filename of the (disk_log(3)) error log file + <p>Defines the filename of the (disk_log(3)) error log file to be used to log server errors. If the filename does not begin - with a slash (/) it is assumed to be relative to the server_root. + with a slash (/) it is assumed to be relative to the server_root. </p> </item> + <marker id="prop_edlog_size"></marker> <tag>{error_disk_log_size, {MaxBytes, MaxFiles}}</tag> - - <item>Where MaxBytes = integer() and MaxFiles = integer(). + <item> + <p>Where MaxBytes = integer() and MaxFiles = integer(). Defines the properties of the (disk_log(3)) error log file. The disk_log(3) error log file is of type wrap log and max-bytes will be written to each file and max-files will be - used before the first file is truncated and reused. </item> + used before the first file is truncated and reused. </p> + </item> + <marker id="prop_sdlog"></marker> <tag>{security_disk_log, path()}</tag> - <item> - Defines the filename of the (disk_log(3)) access log file + <p>Defines the filename of the (disk_log(3)) access log file which logs incoming security events i.e authenticated requests. If the filename does not begin with a slash (/) it - is assumed to be relative to the server_root. + is assumed to be relative to the server_root. </p> </item> + <marker id="prop_sdlog_size"></marker> <tag>{security_disk_log_size, {MaxBytes, MaxFiles}}</tag> - - <item>Where MaxBytes = integer() and MaxFiles = integer(). + <item> + <p>Where MaxBytes = integer() and MaxFiles = integer(). Defines the properties of the disk_log(3) access log file. The disk_log(3) access log file is of type wrap log and max-bytes will be written to each file and max-files will be - used before the first file is truncated and reused.</item> + used before the first file is truncated and reused. </p> + </item> - <tag>{transfer_disk_log, path()}</tag> - + <marker id="prop_tdlog"></marker> + <tag>{transfer_disk_log, path()}</tag> <item> - Defines the filename of the (disk_log(3)) access log file + <p>Defines the filename of the (disk_log(3)) access log file which logs incoming requests. If the filename does not begin with a slash (/) it is assumed to be relative to the - server_root. + server_root. </p> </item> + <marker id="prop_tdlog_size"></marker> <tag>{transfer_disk_log_size, {MaxBytes, MaxFiles}}</tag> - - <item>Where MaxBytes = integer() and MaxFiles = integer(). + <item> + <p>Where MaxBytes = integer() and MaxFiles = integer(). Defines the properties of the disk_log(3) access log file. The disk_log(3) access log file is of type wrap log and max-bytes will be written to each file and max-files will be - used before the first file is truncated and reused.</item> + used before the first file is truncated and reused. </p> + </item> </taglist> - <marker id="auth_prop"></marker> + <marker id="props_auth"></marker> <p><em>Authentication properties - requires mod_auth</em></p> + <marker id="prop_dri"></marker> <p><em>{directory, {path(), [{property(), term()}]}}</em></p> - <marker id="dir_prop"></marker> + <marker id="props_dir"></marker> <p>Here follows the valid properties for directories </p> <taglist> + <marker id="prop_allow_from"></marker> <tag>{allow_from, all | [RegxpHostString]}</tag> - <item> - Defines a set of hosts which should be granted access to a + <p>Defines a set of hosts which should be granted access to a given directory. For example: @@ -672,34 +769,36 @@ bytes <code>{allow_from, ["123.34.56.11", "150.100.23"] </code> The host 123.34.56.11 and all machines on the 150.100.23 - subnet are allowed access.</item> + subnet are allowed access. </p> + </item> + <marker id="prop_deny_from"></marker> <tag>{deny_from, all | [RegxpHostString]}</tag> - <item> - Defines a set of hosts + <p>Defines a set of hosts which should be denied access to a given directory. For example: <code>{deny_from, ["123.34.56.11", "150.100.23"] </code> The host 123.34.56.11 and all machines on the 150.100.23 - subnet are not allowed access.</item> + subnet are not allowed access. </p> + </item> + <marker id="prop_auth_type"></marker> <tag>{auth_type, plain | dets | mnesia}</tag> - <item> - Sets the type of authentication database that is used for the + <p>Sets the type of authentication database that is used for the directory.The key difference between the different methods is that dynamic data can be saved when Mnesia and Dets is used. This property is called AuthDbType in the Apache like - configuration files. + configuration files. </p> </item> + <marker id="prop_auth_user_file"></marker> <tag>{auth_user_file, path()}</tag> - <item> - Sets the name of a file which contains the list of users and + <p>Sets the name of a file which contains the list of users and passwords for user authentication. filename can be either absolute or relative to the <c>server_root</c>. If using the plain storage method, this file is a plain text file, where @@ -717,12 +816,13 @@ bytes storage method. For security reasons, make sure that the <c>auth_user_file</c> is stored outside the document tree of the Web server. If it is placed in the directory which it protects, - clients will be able to download it. + clients will be able to download it. </p> </item> + <marker id="prop_auth_group_file"></marker> <tag>{auth_group_file, path()}</tag> - - <item> Sets the name of a file which contains the list of user + <item> + <p>Sets the name of a file which contains the list of user groups for user authentication. Filename can be either absolute or relative to the <c>server_root</c>. If you use the plain storage method, the group file is a plain text file, where @@ -738,93 +838,109 @@ bytes For security reasons, make sure that the <c>auth_group_file</c> is stored outside the document tree of the Web server. If it is placed in the directory which it protects, clients will be - able to download it.</item> + able to download it. </p> + </item> + <marker id="prop_auth_name"></marker> <tag>{auth_name, string()}</tag> - <item> - Sets the name of the authorization realm (auth-domain) for + <p>Sets the name of the authorization realm (auth-domain) for a directory. This string informs the client about which user - name and password to use. </item> + name and password to use. </p> + </item> + <marker id="prop_auth_access_passwd"></marker> <tag>{auth_access_password, string()}</tag> - - <item> If set to other than "NoPassword" the password is required + <item> + <p>If set to other than "NoPassword" the password is required for all API calls. If the password is set to "DummyPassword" the password must be changed before any other API calls. To secure the authenticating data the password must be changed after the web server is started since it otherwise is written in clear - text in the configuration file.</item> + text in the configuration file. </p> + </item> + <marker id="prop_req_user"></marker> <tag>{require_user, [string()]}</tag> <item> - Defines users which should be granted access to a given - directory using a secret password. + <p>Defines users which should be granted access to a given + directory using a secret password. </p> </item> + <marker id="prop_req_grp"></marker> <tag>{require_group, [string()]}</tag> <item> - Defines users which should be granted access to a given - directory using a secret password. + <p>Defines users which should be granted access to a given + directory using a secret password. </p> </item> </taglist> - <marker id="htaccess_prop"></marker> + <marker id="props_htaccess"></marker> <p><em>Htaccess authentication properties - requires mod_htaccess</em></p> <taglist> + <marker id="prop_access_files"></marker> <tag>{access_files, [path()]}</tag> - - <item> Specify which filenames that are used for + <item> + <p>Specify which filenames that are used for access-files. When a request comes every directory in the path to the requested asset will be searched after files with the names specified by this parameter. If such a file is found the file will be parsed and the restrictions specified in it will - be applied to the request. + be applied to the request. </p> </item> </taglist> - <marker id="sec_prop"></marker> + <marker id="props_sec"></marker> <p><em>Security properties - requires mod_security </em></p> + <marker id="prop_sec_dir"></marker> <p><em>{security_directory, {path(), [{property(), term()}]}</em></p> - <marker id="sdir_prop"></marker> - <p> Here follows the valid properties for security directories</p> + <marker id="props_sdir"></marker> + <p>Here follows the valid properties for security directories</p> <taglist> - <tag>{data_file, path()}</tag> - + <marker id="prop_data_file"></marker> + <tag>{data_file, path()}</tag> <item> - Name of the security data file. The filename can either + <p>Name of the security data file. The filename can either absolute or relative to the server_root. This file is used to - store persistent data for the mod_security module. </item> - - <tag>{max_retries, integer()}</tag> + store persistent data for the mod_security module. </p> + </item> - <item> Specifies the maximum number of tries to authenticate a + <marker id="prop_max_retries"></marker> + <tag>{max_retries, integer()}</tag> + <item> + <p>Specifies the maximum number of tries to authenticate a user has before the user is blocked out. If a user successfully authenticates when the user has been blocked, the user will receive a 403 (Forbidden) response from the server. If the user makes a failed attempt while blocked the server will return 401 (Unauthorized), for security - reasons. Defaults to 3 may also be set to infinity.</item> + reasons. + Defaults to 3 may also be set to infinity. </p> + </item> + <marker id="prop_block_time"></marker> <tag>{block_time, integer()}</tag> - - <item> Specifies the number of minutes a user is blocked. After + <item> + <p>Specifies the number of minutes a user is blocked. After this amount of time, he automatically regains access. - Defaults to 60</item> + Defaults to 60. </p> + </item> + <marker id="prop_fail_exp_time"></marker> <tag>{fail_expire_time, integer()}</tag> - <item> - Specifies the number of minutes a failed user authentication + <p>Specifies the number of minutes a failed user authentication is remembered. If a user authenticates after this amount of time, his previous failed authentications are - forgotten. Defaults to 30</item> + forgotten. + Defaults to 30. </p> + </item> + <marker id="prop_auth_timeout"></marker> <tag>{auth_timeout, integer()}</tag> - <item> Specifies the number of seconds a successful user authentication is remembered. After this time has passed, the @@ -835,6 +951,7 @@ bytes <funcs> <func> + <marker id="info1"></marker> <name>info(Pid) -></name> <name>info(Pid, Properties) -> [{Option, Value}]</name> <fsummary>Fetches information about the HTTP server</fsummary> @@ -858,6 +975,7 @@ bytes </func> <func> + <marker id="info2"></marker> <name>info(Address, Port) -> </name> <name>info(Address, Port, Properties) -> [{Option, Value}] </name> <fsummary>Fetches information about the HTTP server</fsummary> @@ -883,6 +1001,7 @@ bytes </func> <func> + <marker id="reload_config"></marker> <name>reload_config(Config, Mode) -> ok | {error, Reason}</name> <fsummary>Reloads the HTTP server configuration without restarting the server.</fsummary> @@ -1003,6 +1122,7 @@ bytes </section> <funcs> <func> + <marker id="module_do"></marker> <name>Module:do(ModData)-> {proceed, OldData} | {proceed, NewData} | {break, NewData} | done</name> <fsummary>Called for each request to the Web server.</fsummary> <type> @@ -1046,7 +1166,9 @@ bytes closing the connection.</p> </desc> </func> + <func> + <marker id="module_load"></marker> <name>Module:load(Line, AccIn)-> eof | ok | {ok, AccOut} | {ok, AccOut, {Option, Value}} | {ok, AccOut, [{Option, Value}]} | {error, Reason} </name> <fsummary>Load is used to convert a line in a Apache like config file to a <c>{Option, Value}</c> tuple.</fsummary> @@ -1068,7 +1190,9 @@ bytes </p> </desc> </func> + <func> + <marker id="module_store"></marker> <name>Module:store({Option, Value}, Config)-> {ok, {Option, NewValue}} | {error, Reason} </name> <fsummary></fsummary> <type> @@ -1092,6 +1216,7 @@ bytes </func> <func> + <marker id="module_remove"></marker> <name>Module:remove(ConfigDB) -> ok | {error, Reason} </name> <fsummary>Callback function that is called when the Web server is closed.</fsummary> <type> @@ -1112,6 +1237,7 @@ bytes </section> <funcs> <func> + <marker id="parse_query"></marker> <name>parse_query(QueryString) -> [{Key,Value}]</name> <fsummary>Parse incoming data to <c>erl </c>and <c>eval </c>scripts.</fsummary> <type> @@ -1120,7 +1246,6 @@ bytes <v>Value = string()</v> </type> <desc> - <marker id="parse_query"></marker> <p><c>parse_query/1</c> parses incoming data to <c>erl</c> and <c>eval</c> scripts (See <seealso marker="mod_esi">mod_esi(3)</seealso>) as defined in the standard URL format, that is '+' becomes 'space' and decoding of diff --git a/lib/inets/doc/src/httpd_conf.xml b/lib/inets/doc/src/httpd_conf.xml index a1ad76a8ae..fc34f14ec3 100644 --- a/lib/inets/doc/src/httpd_conf.xml +++ b/lib/inets/doc/src/httpd_conf.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,11 +33,14 @@ Web server API programmer.</modulesummary> <description> <p>This module provides the Erlang Webserver API programmer with - utility functions for adding run-time configuration directives.</p> + utility functions for adding run-time configuration directives.</p> + + <marker id="check_enum"></marker> </description> + <funcs> <func> - <name>check_enum(EnumString,ValidEnumStrings) -> Result</name> + <name>check_enum(EnumString, ValidEnumStrings) -> Result</name> <fsummary>Check if string is a valid enumeration.</fsummary> <type> <v>EnumString = string()</v> @@ -47,10 +50,13 @@ <desc> <marker id="check_enum"></marker> <p><c>check_enum/2</c> checks if <c>EnumString</c> is a valid - enumeration of <c>ValidEnumStrings</c> in which case it is - returned as an atom.</p> + enumeration of <c>ValidEnumStrings</c> in which case it is + returned as an atom.</p> + + <marker id="clean"></marker> </desc> </func> + <func> <name>clean(String) -> Stripped</name> <fsummary>Remove leading and/or trailing white spaces.</fsummary> @@ -60,9 +66,12 @@ <desc> <marker id="clean"></marker> <p><c>clean/1</c> removes leading and/or trailing white spaces - from <c>String</c>.</p> + from <c>String</c>.</p> + + <marker id="custom_clean"></marker> </desc> </func> + <func> <name>custom_clean(String,Before,After) -> Stripped</name> <fsummary>Remove leading and/or trailing white spaces and custom characters.</fsummary> @@ -73,11 +82,14 @@ <desc> <marker id="custom_clean"></marker> <p><c>custom_clean/3</c> removes leading and/or trailing white - spaces and custom characters from <c>String</c>. <c>Before</c> - and <c>After</c> are regular expressions, as defined in - <c>regexp(3)</c>, describing the custom characters.</p> + spaces and custom characters from <c>String</c>. <c>Before</c> + and <c>After</c> are regular expressions, as defined in + <c>regexp(3)</c>, describing the custom characters.</p> + + <marker id="is_directory"></marker> </desc> </func> + <func> <name>is_directory(FilePath) -> Result</name> <fsummary>Check if a file path is a directory.</fsummary> @@ -91,13 +103,16 @@ <desc> <marker id="is_directory"></marker> <p><c>is_directory/1</c> checks if <c>FilePath</c> is a - directory in which case it is returned. Please read - <c>file(3)</c> for a description of <c>enoent</c>, - <c>eaccess</c> and <c>enotdir</c>. The definition of - the file info record can be found by including <c>file.hrl</c> - from the kernel application, see file(3).</p> + directory in which case it is returned. Please read + <c>file(3)</c> for a description of <c>enoent</c>, + <c>eaccess</c> and <c>enotdir</c>. The definition of + the file info record can be found by including <c>file.hrl</c> + from the kernel application, see file(3).</p> + + <marker id="is_file"></marker> </desc> </func> + <func> <name>is_file(FilePath) -> Result</name> <fsummary>Check if a file path is a regular file.</fsummary> @@ -111,13 +126,16 @@ <desc> <marker id="is_file"></marker> <p><c>is_file/1</c> checks if <c>FilePath</c> is a regular - file in which case it is returned. Read <c>file(3)</c> for a - description of <c>enoent</c>, <c>eaccess</c> and - <c>enotdir</c>. The definition of the file info record can be - found by including <c>file.hrl</c> from the kernel application, - see file(3).</p> + file in which case it is returned. Read <c>file(3)</c> for a + description of <c>enoent</c>, <c>eaccess</c> and + <c>enotdir</c>. The definition of the file info record can be + found by including <c>file.hrl</c> from the kernel application, + see file(3).</p> + + <marker id="make_integer"></marker> </desc> </func> + <func> <name>make_integer(String) -> Result</name> <fsummary>Return an integer representation of a string.</fsummary> diff --git a/lib/inets/doc/src/httpd_socket.xml b/lib/inets/doc/src/httpd_socket.xml index fba1a58d3a..58cd2ec575 100644 --- a/lib/inets/doc/src/httpd_socket.xml +++ b/lib/inets/doc/src/httpd_socket.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,10 +33,13 @@ Web server API programmer.</modulesummary> <description> <p>This module provides the Erlang Web server API module programmer - with utility functions for generic sockets communication. The - appropriate communication mechanism is transparently used, that - is <c>ip_comm</c> or <c>ssl</c>.</p> + with utility functions for generic sockets communication. The + appropriate communication mechanism is transparently used, that + is <c>ip_comm</c> or <c>ssl</c>.</p> + + <marker id="deliver"></marker> </description> + <funcs> <func> <name>deliver(SocketType, Socket, Data) -> Result</name> @@ -50,11 +53,14 @@ <desc> <marker id="deliver"></marker> <p><c>deliver/3</c> sends the <c>Binary</c> over the - <c>Socket</c> using the specified <c>SocketType</c>. Socket - and SocketType should be the socket and the socket_type form - the mod record as defined in httpd.hrl</p> + <c>Socket</c> using the specified <c>SocketType</c>. Socket + and SocketType should be the socket and the socket_type form + the mod record as defined in httpd.hrl</p> + + <marker id="peername"></marker> </desc> </func> + <func> <name>peername(SocketType,Socket) -> {Port,IPAddress}</name> <fsummary>Return the port and IP-address of the remote socket.</fsummary> @@ -67,9 +73,12 @@ <desc> <marker id="peername"></marker> <p><c>peername/3</c> returns the <c>Port</c> and - <c>IPAddress</c> of the remote <c>Socket</c>. </p> + <c>IPAddress</c> of the remote <c>Socket</c>. </p> + + <marker id="resolve"></marker> </desc> </func> + <func> <name>resolve() -> HostName</name> <fsummary>Return the official name of the current host.</fsummary> @@ -79,7 +88,7 @@ <desc> <marker id="resolve"></marker> <p><c>resolve/0</c> returns the official <c>HostName</c> of - the current host. </p> + the current host. </p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/httpd_util.xml b/lib/inets/doc/src/httpd_util.xml index 6ac2b13c72..9f290084d2 100644 --- a/lib/inets/doc/src/httpd_util.xml +++ b/lib/inets/doc/src/httpd_util.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/inets_services.xml b/lib/inets/doc/src/inets_services.xml index c274d67f19..e282050b12 100644 --- a/lib/inets/doc/src/inets_services.xml +++ b/lib/inets/doc/src/inets_services.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/mod_alias.xml b/lib/inets/doc/src/mod_alias.xml index c783b99b23..265a1b8e76 100644 --- a/lib/inets/doc/src/mod_alias.xml +++ b/lib/inets/doc/src/mod_alias.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,8 +32,11 @@ <modulesummary>URL aliasing.</modulesummary> <description> <p>Erlang Webserver Server internal API for handling of things - such as interaction data exported by the mod_alias module.</p> + such as interaction data exported by the mod_alias module.</p> + + <marker id="default_index"></marker> </description> + <funcs> <func> <name>default_index(ConfigDB, Path) -> NewPath</name> @@ -45,17 +48,20 @@ <desc> <marker id="default_index"></marker> <p>If <c>Path</c> is a directory, <c>default_index/2</c>, it starts - searching for resources or files that are specified in the config - directive DirectoryIndex. - If an appropriate resource or file is found, it is appended to - the end of <c>Path</c> and then returned. <c>Path</c> is - returned unaltered, if no appropriate - file is found, or if <c>Path</c> is not a directory. - <c>config_db()</c> is the server config file in ETS table format - as described in - <seealso marker="http_server">Inets Users Guide.</seealso>.</p> + searching for resources or files that are specified in the config + directive DirectoryIndex. + If an appropriate resource or file is found, it is appended to + the end of <c>Path</c> and then returned. <c>Path</c> is + returned unaltered, if no appropriate + file is found, or if <c>Path</c> is not a directory. + <c>config_db()</c> is the server config file in ETS table format + as described in + <seealso marker="http_server">Inets Users Guide.</seealso>.</p> + + <marker id="path"></marker> </desc> </func> + <func> <name>path(PathData, ConfigDB, RequestURI) -> Path</name> <fsummary>Return the actual file path to a URL.</fsummary> @@ -67,15 +73,19 @@ <desc> <marker id="path"></marker> <p><c>path/3</c> returns the actual file <c>Path</c> in the - <c>RequestURI</c> (See RFC 1945). If the interaction data - <c>{real_name,{Path,AfterPath}}</c> has been exported by - mod_alias; - <c>Path</c> is returned. If no interaction data has been - exported, ServerRoot is used to - generate a file <c>Path</c>. <c>config_db()</c> and - <c>interaction_data()</c> are as defined in <seealso marker="http_server">Inets Users Guide</seealso>.</p> + <c>RequestURI</c> (See RFC 1945). If the interaction data + <c>{real_name,{Path,AfterPath}}</c> has been exported by + mod_alias; + <c>Path</c> is returned. If no interaction data has been + exported, ServerRoot is used to + generate a file <c>Path</c>. <c>config_db()</c> and + <c>interaction_data()</c> are as defined in + <seealso marker="http_server">Inets Users Guide</seealso>.</p> + + <marker id="real_name"></marker> </desc> </func> + <func> <name>real_name(ConfigDB, RequestURI, Aliases) -> Ret</name> <fsummary>Expand a request uri using Alias config directives.</fsummary> @@ -89,18 +99,24 @@ <desc> <marker id="real_name"></marker> <p><c>real_name/3</c> traverses <c>Aliases</c>, typically - extracted from <c>ConfigDB</c>, and matches each - <c>FakeName</c> with <c>RequestURI</c>. If a match is found - <c>FakeName</c> is replaced with <c>RealName</c> in the - match. The resulting path is split into two parts, that - is <c>ShortPath</c> and <c>AfterPath</c> as defined in <seealso marker="httpd_util#split_path">httpd_util:split_path/1</seealso>. - <c>Path</c> is generated from <c>ShortPath</c>, that is - the result from <seealso marker="#default_index">default_index/2</seealso> with - <c>ShortPath</c> as an argument. - <c>config_db()</c> is the server config file in ETS table - format as described in <seealso marker="http_server">Inets User Guide.</seealso>. </p> + extracted from <c>ConfigDB</c>, and matches each + <c>FakeName</c> with <c>RequestURI</c>. If a match is found + <c>FakeName</c> is replaced with <c>RealName</c> in the + match. The resulting path is split into two parts, that + is <c>ShortPath</c> and <c>AfterPath</c> as defined in + <seealso marker="httpd_util#split_path">httpd_util:split_path/1</seealso>. + <c>Path</c> is generated from <c>ShortPath</c>, that is + the result from + <seealso marker="#default_index">default_index/2</seealso> with + <c>ShortPath</c> as an argument. + <c>config_db()</c> is the server config file in ETS table + format as described in + <seealso marker="http_server">Inets User Guide.</seealso>. </p> + + <marker id="real_script_name"></marker> </desc> </func> + <func> <name>real_script_name(ConfigDB,RequestURI,ScriptAliases) -> Ret</name> <fsummary>Expand a request uri using ScriptAlias config directives.</fsummary> @@ -114,15 +130,15 @@ <desc> <marker id="real_script_name"></marker> <p><c>real_name/3</c> traverses <c>ScriptAliases</c>, - typically extracted from <c>ConfigDB</c>, and matches each - <c>FakeName</c> with <c>RequestURI</c>. If a match is found - <c>FakeName</c> is replaced with <c>RealName</c> in the - match. If the resulting match is not an executable script - <c>not_a_script</c> is returned. If it is a script the - resulting script path is in two parts, that is - <c>ShortPath</c> and <c>AfterPath</c> as defined in <seealso marker="httpd_util#split_script_path">httpd_util:split_script_path/1</seealso>. - <c>config_db()</c> is the server config file in ETS table - format as described in <seealso marker="http_server">Inets Users Guide.</seealso>.</p> + typically extracted from <c>ConfigDB</c>, and matches each + <c>FakeName</c> with <c>RequestURI</c>. If a match is found + <c>FakeName</c> is replaced with <c>RealName</c> in the + match. If the resulting match is not an executable script + <c>not_a_script</c> is returned. If it is a script the + resulting script path is in two parts, that is + <c>ShortPath</c> and <c>AfterPath</c> as defined in <seealso marker="httpd_util#split_script_path">httpd_util:split_script_path/1</seealso>. + <c>config_db()</c> is the server config file in ETS table + format as described in <seealso marker="http_server">Inets Users Guide.</seealso>.</p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/mod_auth.xml b/lib/inets/doc/src/mod_auth.xml index 2134ebeeae..7801567862 100644 --- a/lib/inets/doc/src/mod_auth.xml +++ b/lib/inets/doc/src/mod_auth.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> @@ -32,8 +32,11 @@ <modulesummary>User authentication using text files, dets or mnesia database.</modulesummary> <description> <p>This module provides for basic user authentication using - textual files, dets databases as well as mnesia databases. </p> + textual files, dets databases as well as mnesia databases. </p> + + <marker id="add_user"></marker> </description> + <funcs> <func> <name>add_user(UserName, Options) -> true| {error, Reason}</name> @@ -55,12 +58,17 @@ <desc> <marker id="user_api"></marker> <marker id="add_user"></marker> - <p><c>add_user/2, add_user/5</c> and <c>add_user/6</c> adds a user to the user - database. If the operation is successful, this function returns - <c>true</c>. If an error occurs, <c>{error,Reason}</c> is returned. When <c>add_user/2</c> - is called the Password, UserData Port and Dir options is mandatory.</p> + <p><c>add_user/2, add_user/5</c> and <c>add_user/6</c> adds a + user to the user + database. If the operation is successful, this function returns + <c>true</c>. If an error occurs, <c>{error,Reason}</c> is returned. + When <c>add_user/2</c> is called the Password, + UserData Port and Dir options is mandatory.</p> + + <marker id="delete_user"></marker> </desc> </func> + <func> <name>delete_user(UserName,Options) -> true | {error, Reason}</name> <name>delete_user(UserName, Port, Dir) -> true | {error, Reason}</name> @@ -79,13 +87,16 @@ <desc> <marker id="delete_user"></marker> <p><c>delete_user/2, delete_user/3</c> and <c>delete_user/4</c> - deletes a user - from the user database. If the operation is successful, this - function returns <c>true</c>. If an error occurs, - <c>{error,Reason}</c> is returned. When <c>delete_user/2</c> is - called the Port and Dir options are mandatory.</p> + deletes a user from the user database. + If the operation is successful, this function returns <c>true</c>. + If an error occurs, <c>{error,Reason}</c> is returned. + When <c>delete_user/2</c> is called the Port and Dir options + are mandatory.</p> + + <marker id="get_user"></marker> </desc> </func> + <func> <name>get_user(UserName,Options) -> {ok, #httpd_user} |{error, Reason}</name> <name>get_user(UserName, Port, Dir) -> {ok, #httpd_user} | {error, Reason}</name> @@ -104,12 +115,15 @@ <desc> <marker id="get_user"></marker> <p><c>get_user/2, get_user/3</c> and <c>get_user/4</c> returns a - <c>httpd_user</c> record containing the userdata for a - specific user. If the user cannot be found, <c>{error, Reason}</c> - is returned. When <c>get_user/2</c> is called the Port and Dir - options are mandatory.</p> + <c>httpd_user</c> record containing the userdata for a + specific user. If the user cannot be found, <c>{error, Reason}</c> + is returned. When <c>get_user/2</c> is called the Port and Dir + options are mandatory.</p> + + <marker id="list_users"></marker> </desc> </func> + <func> <name>list_users(Options) -> {ok, Users} | {error, Reason}</name> <name>list_users(Port, Dir) -> {ok, Users} | {error, Reason}</name> @@ -127,12 +141,16 @@ </type> <desc> <marker id="list_users"></marker> - <p><c>list_users/1, list_users/2</c> and <c>list_users/3</c> returns a list - of users in the user database for a specific <c>Port/Dir</c>. - When <c>list_users/1</c> is called the Port and Dir - options are mandatory.</p> + <p><c>list_users/1, list_users/2</c> and <c>list_users/3</c> + returns a list + of users in the user database for a specific <c>Port/Dir</c>. + When <c>list_users/1</c> is called the Port and Dir + options are mandatory.</p> + + <marker id="add_group_member"></marker> </desc> </func> + <func> <name>add_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name> <name>add_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name> @@ -151,13 +169,18 @@ </type> <desc> <marker id="add_group_member"></marker> - <p><c>add_group_member/3, add_group_member/4</c> and <c>add_group_member/5</c> - adds a user to a group. If the group does not exist, it - is created and the user is added to the group. Upon successful - operation, this function returns <c>true</c>. When <c>add_group_members/3</c> - is called the Port and Dir options are mandatory.</p> + <p><c>add_group_member/3, add_group_member/4</c> and + <c>add_group_member/5</c> + adds a user to a group. If the group does not exist, it + is created and the user is added to the group. Upon successful + operation, this function returns <c>true</c>. + When <c>add_group_members/3</c> + is called the Port and Dir options are mandatory.</p> + + <marker id="delete_group_member"></marker> </desc> </func> + <func> <name>delete_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name> <name>delete_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name> @@ -176,13 +199,17 @@ </type> <desc> <marker id="delete_group_member"></marker> - <p><c>delete_group_member/3, delete_group_member/4</c> and <c>delete_group_member/5</c> deletes a user from a group. - If the group or the user does not exist, - this function returns an error, otherwise it returns <c>true</c>. - When <c>delete_group_member/3</c> is called the Port and Dir options - are mandatory.</p> + <p><c>delete_group_member/3, delete_group_member/4</c> and + <c>delete_group_member/5</c> deletes a user from a group. + If the group or the user does not exist, + this function returns an error, otherwise it returns <c>true</c>. + When <c>delete_group_member/3</c> is called the Port and Dir options + are mandatory.</p> + + <marker id="list_group_members"></marker> </desc> </func> + <func> <name>list_group_members(GroupName, Options) -> {ok, Users} | {error, Reason}</name> <name>list_group_members(GroupName, Port, Dir) -> {ok, Users} | {error, Reason}</name> @@ -201,13 +228,17 @@ </type> <desc> <marker id="list_group_members"></marker> - <p><c>list_group_members/2, list_group_members/3</c> and <c>list_group_members/4</c> - lists the members of a specified group. If the group does not - exist or there is an error, <c>{error, Reason}</c> is returned. - When <c>list_group_members/2</c> is called the Port and Dir options - are mandatory.</p> + <p><c>list_group_members/2, list_group_members/3</c> and + <c>list_group_members/4</c> + lists the members of a specified group. If the group does not + exist or there is an error, <c>{error, Reason}</c> is returned. + When <c>list_group_members/2</c> is called the Port and Dir options + are mandatory.</p> + + <marker id="list_groups"></marker> </desc> </func> + <func> <name>list_groups(Options) -> {ok, Groups} | {error, Reason}</name> <name>list_groups(Port, Dir) -> {ok, Groups} | {error, Reason}</name> @@ -225,12 +256,16 @@ </type> <desc> <marker id="list_groups"></marker> - <p><c>list_groups/1, list_groups/2</c> and <c>list_groups/3</c> lists all - the groups available. If there is an error, <c>{error, Reason}</c> - is returned. When <c>list_groups/1</c> is called the Port and Dir options - are mandatory.</p> + <p><c>list_groups/1, list_groups/2</c> and <c>list_groups/3</c> + lists all the groups available. + If there is an error, <c>{error, Reason}</c> is returned. + When <c>list_groups/1</c> is called the Port and Dir options + are mandatory.</p> + + <marker id="delete_group"></marker> </desc> </func> + <func> <name>delete_group(GroupName, Options) -> true | {error,Reason} <name>delete_group(GroupName, Port, Dir) -> true | {error, Reason}</name> <name>delete_group(GroupName, Address, Port, Dir) -> true | {error, Reason}</name> @@ -247,12 +282,16 @@ </type> <desc> <marker id="delete_group"></marker> - <p><c>delete_group/2, delete_group/3</c> and <c>delete_group/4</c> deletes the - group specified and returns <c>true</c>. If there is an error, - <c>{error, Reason}</c> is returned. When <c>delete_group/2</c> is called the - Port and Dir options are mandatory.</p> + <p><c>delete_group/2, delete_group/3</c> and <c>delete_group/4</c> + deletes the group specified and returns <c>true</c>. + If there is an error, <c>{error, Reason}</c> is returned. + When <c>delete_group/2</c> is called the + Port and Dir options are mandatory.</p> + + <marker id="update_password"></marker> </desc> </func> + <func> <name>update_password(Port, Dir, OldPassword, NewPassword, NewPassword) -> ok | {error, Reason}</name> <name>update_password(Address,Port, Dir, OldPassword, NewPassword, NewPassword) -> ok | {error, Reason}</name> @@ -268,10 +307,12 @@ </type> <desc> <marker id="update_password"></marker> - <p><c>update_password/5</c> and <c>update_password/6</c> Updates the AuthAccessPassword - for the specified directory. If NewPassword is equal to "NoPassword" no password is requires to - change authorisation data. If NewPassword is equal to "DummyPassword" no changes can be done - without changing the password first.</p> + <p><c>update_password/5</c> and <c>update_password/6</c> + Updates the AuthAccessPassword for the specified directory. + If NewPassword is equal to "NoPassword" no password is requires to + change authorisation data. + If NewPassword is equal to "DummyPassword" no changes can be done + without changing the password first.</p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index cfc58b8ddb..dfdeb4016c 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -33,6 +33,96 @@ </header> + <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>. + The value of the server header, which was previously hard-coded + (at compile time), is now possible to manipulate through the means + of the + <seealso marker="httpd#prop_server_tokens">server_tokens</seealso> + config option. </p> + <p>Own Id: OTP-9805</p> + </item> + + <item> + <p>Improve inets support for inets as an included application. </p> + <p><c>inets_app</c> calls <c>supervisor:start_link/3</c> directly + rather than calling the root supervisor function + <c>inets_sup:start_link/0</c>. + This precludes using included_applications to start inets without + having a wrapper function. </p> + <p>Jay Nelson</p> + <p>Own Id: OTP-9960</p> + </item> + + <item> + <p>[httpc] Add function for retrieving current options, + <seealso marker="httpc#get_options">get_options/1,2</seealso>. </p> + <p>Own Id: OTP-9979</p> + </item> + + <item> + <p>Utility module + <seealso marker="http_uri">http_uri</seealso> + now officially supported. </p> + <p>Also, the + <seealso marker="http_uri#parse">parse</seealso> + function has been extended with more + scheme support and a way to provide your own scheme info. </p> + <p>Own Id: OTP-9983</p> + <p>Aux Id: Seq 12022</p> + </item> + + </list> + + </section> + + <section><title>Fixed Bugs and Malfunctions</title> + <p>-</p> + +<!-- + <list> + <item> + <p>[httpd] Fix logging of content length in mod_log. </p> + <p>Garrett Smith</p> + <p>Own Id: OTP-9715</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 --> + + <section><title>Inets 5.8.1</title> <section><title>Improvements and New Features</title> <p>-</p> @@ -417,7 +507,7 @@ <p><c>ossl</c> will work for as long as the ssl application supports it. </p> <p>See the httpd - <seealso marker="httpd#comm_prop">socket_type</seealso> + <seealso marker="httpd#props_comm">socket_type</seealso> communication property or the httpc <seealso marker="httpc#request2">request/4,5</seealso> function for more info. </p> @@ -436,7 +526,7 @@ <list> <item> <p>[httpd] Wrong - <seealso marker="httpd#sec_prop">security property</seealso> + <seealso marker="httpd#props_sec">security property</seealso> names used in documentation. </p> <p><c>security_data_file</c> used instead of <c>data_file</c>. </p> <p><c>security_max_retries</c> used instead of <c>max_retries</c>. </p> @@ -620,7 +710,7 @@ the <c>essl</c> tag instead. </p> <p>See the <c>http_option</c> option in the <seealso marker="httpc#request2">request/4,5</seealso> or - the <seealso marker="httpd#comm_prop">socket-type</seealso> + the <seealso marker="httpd#props_comm">socket-type</seealso> section of the Communication properties chapter for more info, </p> <p>Own Id: OTP-7907</p> </item> @@ -637,9 +727,9 @@ <p>[httpd] - Improved mod_alias. Now able to do better URL rewrites. </p> <p>See - <seealso marker="httpd#alias_prop">URL aliasing properties</seealso> + <seealso marker="httpd#props_alias">URL aliasing properties</seealso> and the - <seealso marker="httpd#cgi_prop">CGI properties</seealso> + <seealso marker="httpd#props_cgi">CGI properties</seealso> section(s) for more info, </p> <p>Own Id: OTP-8573</p> </item> @@ -1225,7 +1315,7 @@ <p>Default is <c>inet6fb4</c> which emulates the behaviour of the previous version. </p> <p>See the - <seealso marker="httpd#comm_prop">Communication properties</seealso> + <seealso marker="httpd#props_comm">Communication properties</seealso> section for more info. </p> <p>Own Id: OTP-8069</p> <p>Aux Id: seq11086</p> diff --git a/lib/inets/doc/src/notes_history.xml b/lib/inets/doc/src/notes_history.xml index 151bec375e..bd59c1ba47 100644 --- a/lib/inets/doc/src/notes_history.xml +++ b/lib/inets/doc/src/notes_history.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>2004</year><year>2011</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/part.xml b/lib/inets/doc/src/part.xml index 36955df6b3..3b6734a9b8 100644 --- a/lib/inets/doc/src/part.xml +++ b/lib/inets/doc/src/part.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/part_notes.xml b/lib/inets/doc/src/part_notes.xml index 21f464318b..81b0dedbfa 100644 --- a/lib/inets/doc/src/part_notes.xml +++ b/lib/inets/doc/src/part_notes.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2002</year><year>2009</year> + <year>2002</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/part_notes_history.xml b/lib/inets/doc/src/part_notes_history.xml index 3c1e6f5232..f714a6d2e3 100644 --- a/lib/inets/doc/src/part_notes_history.xml +++ b/lib/inets/doc/src/part_notes_history.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/ref_man.xml b/lib/inets/doc/src/ref_man.xml index 45d5dfcd0e..e44829827c 100644 --- a/lib/inets/doc/src/ref_man.xml +++ b/lib/inets/doc/src/ref_man.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE application SYSTEM "application.dtd"> <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -30,8 +30,8 @@ </header> <description> <p>Inets is a container for Internet clients and - servers. Currently a FTP client, a HTTP client and server, and - a tftp client and server has been incorporated in Inets.</p> + servers. Currently a FTP client, a HTTP client and server, and + a tftp client and server has been incorporated in Inets.</p> </description> <xi:include href="inets.xml"/> <xi:include href="ftp.xml"/> @@ -45,6 +45,7 @@ <xi:include href="mod_auth.xml"/> <xi:include href="mod_esi.xml"/> <xi:include href="mod_security.xml"/> + <xi:include href="http_uri.xml"/> </application> diff --git a/lib/inets/doc/src/tftp.xml b/lib/inets/doc/src/tftp.xml index 96d6ae0dd5..0b3e93a153 100644 --- a/lib/inets/doc/src/tftp.xml +++ b/lib/inets/doc/src/tftp.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2006</year><year>2009</year> + <year>2006</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -218,6 +218,8 @@ <c>5</c> times when the timeout expires.</p> </item> </taglist> + + <marker id="start1"></marker> </section> <funcs> @@ -231,11 +233,14 @@ </type> <desc> <p>Starts a daemon process which listens for udp packets on a - port. When it receives a request for read or write it spawns - a temporary server process which handles the actual transfer - of the (virtual) file.</p> + port. When it receives a request for read or write it spawns + a temporary server process which handles the actual transfer + of the (virtual) file.</p> + + <marker id="read_file"></marker> </desc> </func> + <func> <name>read_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name> <fsummary>Read a (virtual) file from a TFTP server</fsummary> @@ -248,23 +253,26 @@ </type> <desc> <p>Reads a (virtual) file <c>RemoteFilename</c> from a TFTP - server.</p> - <p>If <c>LocalFilename</c> is the atom <c>binary</c>, - <c>tftp_binary</c> is used as callback module. It concatenates - all transferred blocks and returns them as one single binary - in <c>LastCallbackState</c>.</p> - <p>If <c>LocalFilename</c> is a string and there are no - registered callback modules, <c>tftp_file</c> is used as - callback module. It writes each transferred block to the file - named <c>LocalFilename</c> and returns the number of - transferred bytes in <c>LastCallbackState</c>.</p> - <p>If <c>LocalFilename</c> is a string and there are registered - callback modules, <c>LocalFilename</c> is tested against - the regexps of these and the callback module corresponding to - the first match is used, or an error tuple is returned if no - matching regexp is found.</p> + server.</p> + <p>If <c>LocalFilename</c> is the atom <c>binary</c>, + <c>tftp_binary</c> is used as callback module. It concatenates + all transferred blocks and returns them as one single binary + in <c>LastCallbackState</c>.</p> + <p>If <c>LocalFilename</c> is a string and there are no + registered callback modules, <c>tftp_file</c> is used as + callback module. It writes each transferred block to the file + named <c>LocalFilename</c> and returns the number of + transferred bytes in <c>LastCallbackState</c>.</p> + <p>If <c>LocalFilename</c> is a string and there are registered + callback modules, <c>LocalFilename</c> is tested against + the regexps of these and the callback module corresponding to + the first match is used, or an error tuple is returned if no + matching regexp is found.</p> </desc> + + <marker id="write_file"></marker> </func> + <func> <name>write_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name> <fsummary>Write a (virtual) file to a TFTP server</fsummary> @@ -288,10 +296,12 @@ block by block and returns the number of transferred bytes in <c>LastCallbackState</c>.</p> <p>If <c>LocalFilename</c> is a string and there are registered - callback modules, <c>LocalFilename</c> is tested against - the regexps of these and the callback module corresponding to - the first match is used, or an error tuple is returned if no - matching regexp is found.</p> + callback modules, <c>LocalFilename</c> is tested against + the regexps of these and the callback module corresponding to + the first match is used, or an error tuple is returned if no + matching regexp is found.</p> + + <marker id="info_daemons"></marker> </desc> </func> @@ -304,8 +314,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Returns info about all TFTP daemon processes. - </p> + <p>Returns info about all TFTP daemon processes. </p> + + <marker id="info_servers"></marker> </desc> </func> @@ -318,8 +329,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Returns info about all TFTP server processes. - </p> + <p>Returns info about all TFTP server processes. </p> + + <marker id="info_pid"></marker> </desc> </func> @@ -332,6 +344,8 @@ </type> <desc> <p>Returns info about a TFTP daemon, server or client process.</p> + + <marker id="change_config_daemons"></marker> </desc> </func> @@ -346,8 +360,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Changes config for all TFTP daemon processes - </p> + <p>Changes config for all TFTP daemon processes. </p> + + <marker id="change_config_servers"></marker> </desc> </func> @@ -362,8 +377,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Changes config for all TFTP server processes - </p> + <p>Changes config for all TFTP server processes. </p> + + <marker id="change_config_pid"></marker> </desc> </func> @@ -378,8 +394,11 @@ </type> <desc> <p>Changes config for a TFTP daemon, server or client process</p> + + <marker id="start2"></marker> </desc> </func> + <func> <name>start() -> ok | {error, Reason}</name> <fsummary>Start the Inets application</fsummary> @@ -442,8 +461,9 @@ by the already ongoing connection on the server side. By not setting up yet another connection, in parallel with the ongoing one, the server will - consumer lesser resources. - </p> + consumer lesser resources. </p> + + <marker id="prepare"></marker> </section> <funcs> @@ -468,17 +488,20 @@ <v>Text = string()</v> </type> <desc> - <p>Prepares to open a file on the client side.</p> - <p>No new options may be added, but the ones that are present in - <c>SuggestedOptions</c> may be omitted or replaced with new - values in <c>AcceptedOptions</c>.</p> - <p>Will be followed by a call to <c>open/4</c> before any - read/write access is performed. <c>AcceptedOptions</c> is - sent to the server which replies with those options that it - accepts. These will be forwarded to <c>open/4</c> as - <c>SuggestedOptions</c>.</p> + <p>Prepares to open a file on the client side.</p> + <p>No new options may be added, but the ones that are present in + <c>SuggestedOptions</c> may be omitted or replaced with new + values in <c>AcceptedOptions</c>.</p> + <p>Will be followed by a call to <c>open/4</c> before any + read/write access is performed. <c>AcceptedOptions</c> is + sent to the server which replies with those options that it + accepts. These will be forwarded to <c>open/4</c> as + <c>SuggestedOptions</c>.</p> + + <marker id="open"></marker> </desc> </func> + <func> <name>open(Peer, Access, Filename, Mode, SuggestedOptions, State) -> {ok, AcceptedOptions, NewState} | {error, {Code, Text}}</name> <fsummary>Open a file for read or write access</fsummary> @@ -503,14 +526,17 @@ <desc> <p>Opens a file for read or write access.</p> <p>On the client side where the <c>open/5</c> call has been - preceded by a call to <c>prepare/5</c>, all options must be - accepted or rejected.</p> - <p>On the server side, where there is no preceding - <c>prepare/5</c> call, no new options may be added, but - the ones that are present in <c>SuggestedOptions</c> may be - omitted or replaced with new values in <c>AcceptedOptions</c>.</p> + preceded by a call to <c>prepare/5</c>, all options must be + accepted or rejected.</p> + <p>On the server side, where there is no preceding + <c>prepare/5</c> call, no new options may be added, but + the ones that are present in <c>SuggestedOptions</c> may be + omitted or replaced with new values in <c>AcceptedOptions</c>.</p> + + <marker id="read"></marker> </desc> </func> + <func> <name>read(State) -> {more, Bin, NewState} | {last, Bin, FileSize} | {error, {Code, Text}}</name> <fsummary>Read a chunk from the file</fsummary> @@ -526,15 +552,18 @@ <desc> <p>Read a chunk from the file.</p> <p>The callback function is expected to close - the file when the last file chunk is - encountered. When an error is encountered - the callback function is expected to clean - up after the aborted file transfer, such as - closing open file descriptors etc. In both - cases there will be no more calls to any of - the callback functions.</p> + the file when the last file chunk is + encountered. When an error is encountered + the callback function is expected to clean + up after the aborted file transfer, such as + closing open file descriptors etc. In both + cases there will be no more calls to any of + the callback functions.</p> + + <marker id="write"></marker> </desc> </func> + <func> <name>write(Bin, State) -> {more, NewState} | {last, FileSize} | {error, {Code, Text}}</name> <fsummary>Write a chunk to the file</fsummary> @@ -550,15 +579,18 @@ <desc> <p>Write a chunk to the file.</p> <p>The callback function is expected to close - the file when the last file chunk is - encountered. When an error is encountered - the callback function is expected to clean - up after the aborted file transfer, such as - closing open file descriptors etc. In both - cases there will be no more calls to any of - the callback functions.</p> + the file when the last file chunk is + encountered. When an error is encountered + the callback function is expected to clean + up after the aborted file transfer, such as + closing open file descriptors etc. In both + cases there will be no more calls to any of + the callback functions.</p> + + <marker id="abort"></marker> </desc> </func> + <func> <name>abort(Code, Text, State) -> ok</name> <fsummary>Abort the file transfer</fsummary> @@ -572,14 +604,14 @@ <desc> <p>Invoked when the file transfer is aborted.</p> <p>The callback function is expected to clean - up its used resources after the aborted file - transfer, such as closing open file - descriptors etc. The function will not be - invoked if any of the other callback - functions returns an error, as it is - expected that they already have cleaned up - the necessary resources. It will however be - invoked if the functions fails (crashes).</p> + up its used resources after the aborted file + transfer, such as closing open file + descriptors etc. The function will not be + invoked if any of the other callback + functions returns an error, as it is + expected that they already have cleaned up + the necessary resources. It will however be + invoked if the functions fails (crashes).</p> </desc> </func> </funcs> @@ -589,7 +621,9 @@ <title>LOGGER FUNCTIONS</title> <p>A <c>tftp_logger</c> callback module should be implemented as a - <c>tftp_logger</c> behavior and export the functions listed below.</p> + <c>tftp_logger</c> behavior and export the functions listed below.</p> + + <marker id="error_msg"></marker> </section> <funcs> @@ -602,7 +636,10 @@ <v>Reason = term()</v> </type> <desc> - <p>Log an error message. See <c>error_logger:error_msg/2 for details.</c> </p> + <p>Log an error message. + See <c>error_logger:error_msg/2 for details.</c> </p> + + <marker id="warning_msg"></marker> </desc> </func> @@ -615,7 +652,10 @@ <v>Reason = term()</v> </type> <desc> - <p>Log a warning message. See <c>error_logger:warning_msg/2 for details.</c> </p> + <p>Log a warning message. + See <c>error_logger:warning_msg/2 for details.</c> </p> + + <marker id="info_msg"></marker> </desc> </func> @@ -628,7 +668,8 @@ <v>Reason = term()</v> </type> <desc> - <p>Log an info message. See <c>error_logger:info_msg/2 for details.</c> </p> + <p>Log an info message. + See <c>error_logger:info_msg/2 for details.</c> </p> </desc> </func> </funcs> diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index ae87ceed93..0a30fe1e20 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -31,8 +31,10 @@ -export([ request/1, request/2, request/4, request/5, cancel_request/1, cancel_request/2, - set_option/2, set_option/3, + set_option/2, set_option/3, set_options/1, set_options/2, + get_option/1, get_option/2, + get_options/1, get_options/2, store_cookies/2, store_cookies/3, cookie_header/1, cookie_header/2, cookie_header/3, which_cookies/0, which_cookies/1, @@ -156,7 +158,7 @@ request(Method, {http_options, HTTPOptions}, {options, Options}, {profile, Profile}]), - case http_uri:parse(Url, Options) of + case uri_parse(Url, Options) of {error, Reason} -> {error, Reason}; {ok, ParsedUrl} -> @@ -177,7 +179,7 @@ request(Method, {http_options, HTTPOptions}, {options, Options}, {profile, Profile}]), - case http_uri:parse(Url, Options) of + case uri_parse(Url, Options) of {error, Reason} -> {error, Reason}; {ok, ParsedUrl} -> @@ -230,7 +232,7 @@ cancel_request(RequestId, Profile) set_options(Options) -> set_options(Options, default_profile()). set_options(Options, Profile) when is_atom(Profile) orelse is_pid(Profile) -> - ?hcrt("set cookies", [{options, Options}, {profile, Profile}]), + ?hcrt("set options", [{options, Options}, {profile, Profile}]), case validate_options(Options) of {ok, Opts} -> try @@ -253,6 +255,58 @@ set_option(Key, Value, Profile) -> %%-------------------------------------------------------------------------- +%% get_options(OptionItems) -> {ok, Values} | {error, Reason} +%% get_options(OptionItems, Profile) -> {ok, Values} | {error, Reason} +%% OptionItems - all | [option_item()] +%% option_item() - proxy | pipeline_timeout | max_pipeline_length | +%% keep_alive_timeout | max_keep_alive_length | +%% max_sessions | verbose | +%% cookies | ipfamily | ip | port | socket_opts +%% Profile - atom() +%% Values - [{option_item(), term()}] +%% Reason - term() +%% Description: Retrieves the current options. +%%------------------------------------------------------------------------- +get_options() -> + record_info(fields, options). + +get_options(Options) -> + get_options(Options, default_profile()). + +get_options(all = _Options, Profile) -> + get_options(get_options(), Profile); +get_options(Options, Profile) + when (is_list(Options) andalso + (is_atom(Profile) orelse is_pid(Profile))) -> + ?hcrt("get options", [{options, Options}, {profile, Profile}]), + case Options -- get_options() of + [] -> + try + begin + {ok, httpc_manager:get_options(Options, + profile_name(Profile))} + end + catch + exit:{noproc, _} -> + {error, inets_not_started} + end; + InvalidGetOptions -> + {error, {invalid_options, InvalidGetOptions}} + end. + +get_option(Key) -> + get_option(Key, default_profile()). + +get_option(Key, Profile) -> + case get_options([Key], Profile) of + {ok, [{Key, Value}]} -> + {ok, Value}; + Error -> + Error + end. + + +%%-------------------------------------------------------------------------- %% store_cookies(SetCookieHeaders, Url [, Profile]) -> ok | {error, reason} %% %% @@ -274,7 +328,7 @@ store_cookies(SetCookieHeaders, Url, Profile) %% Since the Address part is not actually used %% by the manager when storing cookies, we dont %% care about ipv6-host-with-brackets. - {ok, {_, _, Host, Port, Path, _}} = http_uri:parse(Url), + {ok, {_, _, Host, Port, Path, _}} = uri_parse(Url), Address = {Host, Port}, ProfileName = profile_name(Profile), Cookies = httpc_cookie:cookies(SetCookieHeaders, Path, Host), @@ -347,7 +401,7 @@ which_cookies(Profile) -> %% info() -> list() %% info(Profile) -> list() %% -%% Description: Debug function, retreive info about the profile +%% Description: Debug function, retrieve info about the profile %%------------------------------------------------------------------------- info() -> info(default_profile()). @@ -1144,6 +1198,22 @@ validate_headers(RequestHeaders, _, _) -> RequestHeaders. +%%-------------------------------------------------------------------------- +%% These functions is just simple wrappers to parse specifically HTTP URIs +%%-------------------------------------------------------------------------- + +scheme_defaults() -> + [{http, 80}, {https, 443}]. + +uri_parse(URI) -> + http_uri:parse(URI, [{scheme_defaults, scheme_defaults()}]). + +uri_parse(URI, Opts) -> + http_uri:parse(URI, [{scheme_defaults, scheme_defaults()} | Opts]). + + +%%-------------------------------------------------------------------------- + child_name2info(undefined) -> {error, no_such_service}; child_name2info(httpc_manager) -> diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index bfe9b14ef6..b8c34bd99b 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.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 @@ -851,14 +851,17 @@ connect(SocketType, ToAddress, case IpFamily of inet6fb4 -> Opts3 = [inet6 | Opts2], - case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of - {error, _Reason} = Error -> + case http_transport:connect(SocketType, + ToAddress, Opts3, Timeout) of + {error, Reason6} -> Opts4 = [inet | Opts2], case http_transport:connect(SocketType, ToAddress, Opts4, Timeout) of - {error, _} -> - %% Reply with the "original" error - Error; + {error, Reason4} -> + {error, {failed_connect, + [{to_address, ToAddress}, + {inet6, Opts3, Reason6}, + {inet, Opts4, Reason4}]}}; OK -> OK end; @@ -867,7 +870,13 @@ connect(SocketType, ToAddress, end; _ -> Opts3 = [IpFamily | Opts2], - http_transport:connect(SocketType, ToAddress, Opts3, Timeout) + case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of + {error, Reason} -> + {error, {failed_connect, [{to_address, ToAddress}, + {IpFamily, Opts3, Reason}]}}; + Else -> + Else + end end. connect_and_send_first_request(Address, Request, #state{options = Options} = State) -> diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 453081da21..b225b43214 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -37,6 +37,7 @@ update_session/4, delete_session/2, set_options/2, + get_options/2, store_cookies/3, which_cookies/1, which_cookies/2, which_cookies/3, reset_cookies/1, @@ -250,6 +251,21 @@ set_options(Options, ProfileName) -> %%-------------------------------------------------------------------- +%% Function: get_options(OptionItems, ProfileName) -> Values +%% +%% OptionItems = [OptionItem] +%% OptionItem = Any or all fields of the current #options{} record +%% Values = [{OptionItem, Value}] +%% Value = term() +%% +%% Description: Gets the specified options used by the client. +%%-------------------------------------------------------------------- + +get_options(Options, ProfileName) -> + call(ProfileName, {get_options, Options}). + + +%%-------------------------------------------------------------------- %% Function: store_cookies(Cookies, Address, ProfileName) -> ok %% %% Cookies = [Cookie] @@ -430,7 +446,7 @@ handle_call(which_cookies, _, #state{cookie_db = CookieDb} = State) -> handle_call({which_cookies, Url, Options}, _, #state{cookie_db = CookieDb} = State) -> ?hcrv("which cookies", [{url, Url}, {options, Options}]), - case http_uri:parse(Url, Options) of + case uri_parse(Url, Options) of {ok, {Scheme, _, Host, Port, Path, _}} -> CookieHeaders = httpc_cookie:header(CookieDb, Scheme, {Host, Port}, Path), @@ -439,6 +455,12 @@ handle_call({which_cookies, Url, Options}, _, {reply, ERROR, State} end; +handle_call({get_options, OptionItems}, _, #state{options = Options} = State) -> + ?hcrv("get options", [{option_items, OptionItems}]), + Reply = [{OptionItem, get_option(OptionItem, Options)} || OptionItem <- + OptionItems], + {reply, Reply, State}; + handle_call(info, _, State) -> ?hcrv("info", []), Info = get_manager_info(State), @@ -872,6 +894,19 @@ make_db_name(ProfileName, Post) -> list_to_atom(atom_to_list(ProfileName) ++ Post). +%%-------------------------------------------------------------------------- +%% These functions is just simple wrappers to parse specifically HTTP URIs +%%-------------------------------------------------------------------------- + +scheme_defaults() -> + [{http, 80}, {https, 443}]. + +uri_parse(URI, Opts) -> + http_uri:parse(URI, [{scheme_defaults, scheme_defaults()} | Opts]). + + +%%-------------------------------------------------------------------------- + call(ProfileName, Msg) -> Timeout = infinity, @@ -883,6 +918,31 @@ cast(ProfileName, Msg) -> gen_server:cast(ProfileName, Msg). +get_option(proxy, #options{proxy = Proxy}) -> + Proxy; +get_option(pipeline_timeout, #options{pipeline_timeout = Timeout}) -> + Timeout; +get_option(max_pipeline_length, #options{max_pipeline_length = Length}) -> + Length; +get_option(keep_alive_timeout, #options{keep_alive_timeout = Timeout}) -> + Timeout; +get_option(max_keep_alive_length, #options{max_keep_alive_length = Length}) -> + Length; +get_option(max_sessions, #options{max_sessions = MaxSessions}) -> + MaxSessions; +get_option(cookies, #options{cookies = Cookies}) -> + Cookies; +get_option(verbose, #options{verbose = Verbose}) -> + Verbose; +get_option(ipfamily, #options{ipfamily = IpFamily}) -> + IpFamily; +get_option(ip, #options{ip = IP}) -> + IP; +get_option(port, #options{port = Port}) -> + Port; +get_option(socket_opts, #options{socket_opts = SocketOpts}) -> + SocketOpts. + get_proxy(Opts, #options{proxy = Default}) -> proplists:get_value(proxy, Opts, Default). diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl index 919115a23a..23924e355e 100644 --- a/lib/inets/src/http_client/httpc_response.erl +++ b/lib/inets/src/http_client/httpc_response.erl @@ -342,7 +342,7 @@ redirect(Response = {StatusLine, Headers, Body}, Request) -> RedirUrl -> UrlParseOpts = [{ipv6_host_with_brackets, Request#request.ipv6_host_with_brackets}], - case http_uri:parse(RedirUrl, UrlParseOpts) of + case uri_parse(RedirUrl, UrlParseOpts) of {error, no_scheme} when (Request#request.settings)#http_options.relaxed -> NewLocation = fix_relative_uri(Request, RedirUrl), @@ -437,3 +437,17 @@ format_response({StatusLine, Headers, Body}) -> end, {{StatusLine, http_response:header_list(Headers), NewBody}, Data}. +%%-------------------------------------------------------------------------- +%% These functions is just simple wrappers to parse specifically HTTP URIs +%%-------------------------------------------------------------------------- + +scheme_defaults() -> + [{http, 80}, {https, 443}]. + +uri_parse(URI, Opts) -> + http_uri:parse(URI, [{scheme_defaults, scheme_defaults()} | Opts]). + + +%%-------------------------------------------------------------------------- + + diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl index 32c6305a79..5962001c3a 100644 --- a/lib/inets/src/http_lib/http_uri.erl +++ b/lib/inets/src/http_lib/http_uri.erl @@ -1,7 +1,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 @@ -17,39 +17,95 @@ %% %CopyrightEnd% %% %% -%% RFC 3986 +%% This is from chapter 3, Syntax Components, of RFC 3986: +%% +%% The generic URI syntax consists of a hierarchical sequence of +%% components referred to as the scheme, authority, path, query, and +%% fragment. +%% +%% URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] +%% +%% hier-part = "//" authority path-abempty +%% / path-absolute +%% / path-rootless +%% / path-empty +%% +%% The scheme and path components are required, though the path may be +%% empty (no characters). When authority is present, the path must +%% either be empty or begin with a slash ("/") character. When +%% authority is not present, the path cannot begin with two slash +%% characters ("//"). These restrictions result in five different ABNF +%% rules for a path (Section 3.3), only one of which will match any +%% given URI reference. +%% +%% The following are two example URIs and their component parts: +%% +%% foo://example.com:8042/over/there?name=ferret#nose +%% \_/ \______________/\_________/ \_________/ \__/ +%% | | | | | +%% scheme authority path query fragment +%% | _____________________|__ +%% / \ / \ +%% urn:example:animal:ferret:nose +%% +%% scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) +%% authority = [ userinfo "@" ] host [ ":" port ] +%% userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) +%% %% -module(http_uri). -export([parse/1, parse/2, + scheme_defaults/0, encode/1, decode/1]). +-export_type([scheme/0, default_scheme_port_number/0]). + %%%========================================================================= %%% API %%%========================================================================= + +-type scheme() :: atom(). +-type default_scheme_port_number() :: pos_integer(). + +-spec scheme_defaults() -> + [{scheme(), default_scheme_port_number()}]. + +scheme_defaults() -> + [{http, 80}, + {https, 443}, + {ftp, 21}, + {ssh, 22}, + {sftp, 22}, + {tftp, 69}]. + parse(AbsURI) -> parse(AbsURI, []). parse(AbsURI, Opts) -> - case parse_scheme(AbsURI) of + case parse_scheme(AbsURI, Opts) of {error, Reason} -> {error, Reason}; - {Scheme, Rest} -> - case (catch parse_uri_rest(Scheme, Rest, Opts)) of - {UserInfo, Host, Port, Path, Query} -> + {Scheme, DefaultPort, Rest} -> + case (catch parse_uri_rest(Scheme, DefaultPort, Rest, Opts)) of + {ok, {UserInfo, Host, Port, Path, Query}} -> {ok, {Scheme, UserInfo, Host, Port, Path, Query}}; + {error, Reason} -> + {error, {Reason, Scheme, AbsURI}}; _ -> - {error, {malformed_url, AbsURI}} + {error, {malformed_url, Scheme, AbsURI}} end end. +reserved() -> + sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?, + $#, $[, $], $<, $>, $\", ${, $}, $|, + $\\, $', $^, $%, $ ]). + encode(URI) -> - Reserved = sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?, - $#, $[, $], $<, $>, $\", ${, $}, $|, - $\\, $', $^, $%, $ ]), - %% lists:append(lists:map(fun(Char) -> uri_encode(Char, Reserved) end, URI)). + Reserved = reserved(), lists:append([uri_encode(Char, Reserved) || Char <- URI]). decode(String) -> @@ -67,20 +123,31 @@ do_decode([]) -> %%% Internal functions %%%======================================================================== -parse_scheme(AbsURI) -> +which_scheme_defaults(Opts) -> + Key = scheme_defaults, + case lists:keysearch(Key, 1, Opts) of + {value, {Key, SchemeDefaults}} -> + SchemeDefaults; + false -> + scheme_defaults() + end. + +parse_scheme(AbsURI, Opts) -> case split_uri(AbsURI, ":", {error, no_scheme}, 1, 1) of {error, no_scheme} -> {error, no_scheme}; - {StrScheme, Rest} -> - case list_to_atom(http_util:to_lower(StrScheme)) of - Scheme when (Scheme =:= http) orelse (Scheme =:= https) -> - {Scheme, Rest}; - Scheme -> - {error, {not_supported_scheme, Scheme}} + {SchemeStr, Rest} -> + Scheme = list_to_atom(http_util:to_lower(SchemeStr)), + SchemeDefaults = which_scheme_defaults(Opts), + case lists:keysearch(Scheme, 1, SchemeDefaults) of + {value, {Scheme, DefaultPort}} -> + {Scheme, DefaultPort, Rest}; + false -> + {Scheme, no_default_port, Rest} end end. -parse_uri_rest(Scheme, "//" ++ URIPart, Opts) -> +parse_uri_rest(Scheme, DefaultPort, "//" ++ URIPart, Opts) -> {Authority, PathQuery} = case split_uri(URIPart, "/", URIPart, 1, 0) of Split = {_, _} -> @@ -93,26 +160,25 @@ parse_uri_rest(Scheme, "//" ++ URIPart, Opts) -> {URIPart,""} end end, - {UserInfo, HostPort} = split_uri(Authority, "@", {"", Authority}, 1, 1), - {Host, Port} = parse_host_port(Scheme, HostPort, Opts), + {Host, Port} = parse_host_port(Scheme, DefaultPort, HostPort, Opts), {Path, Query} = parse_path_query(PathQuery), - {UserInfo, Host, Port, Path, Query}. + {ok, {UserInfo, Host, Port, Path, Query}}. parse_path_query(PathQuery) -> {Path, Query} = split_uri(PathQuery, "\\?", {PathQuery, ""}, 1, 0), {path(Path), Query}. -parse_host_port(Scheme,"[" ++ HostPort, Opts) -> %ipv6 - DefaultPort = default_port(Scheme), +%% In this version of the function, we no longer need +%% the Scheme argument, but just in case... +parse_host_port(_Scheme, DefaultPort, "[" ++ HostPort, Opts) -> %ipv6 {Host, ColonPort} = split_uri(HostPort, "\\]", {HostPort, ""}, 1, 1), Host2 = maybe_ipv6_host_with_brackets(Host, Opts), {_, Port} = split_uri(ColonPort, ":", {"", DefaultPort}, 0, 1), {Host2, int_port(Port)}; -parse_host_port(Scheme, HostPort, _Opts) -> - DefaultPort = default_port(Scheme), +parse_host_port(_Scheme, DefaultPort, HostPort, _Opts) -> {Host, Port} = split_uri(HostPort, ":", {HostPort, DefaultPort}, 1, 1), {Host, int_port(Port)}. @@ -133,15 +199,14 @@ maybe_ipv6_host_with_brackets(Host, Opts) -> Host end. -default_port(http) -> - 80; -default_port(https) -> - 443. int_port(Port) when is_integer(Port) -> Port; int_port(Port) when is_list(Port) -> - list_to_integer(Port). + list_to_integer(Port); +%% This is the case where no port was found and there was no default port +int_port(no_default_port) -> + throw({error, no_default_port}). path("") -> "/"; diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile index 55cc68dede..c341a2cec7 100644 --- a/lib/inets/src/http_server/Makefile +++ b/lib/inets/src/http_server/Makefile @@ -88,6 +88,8 @@ ERL_FILES = $(MODULES:%=%.erl) TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' + # ---------------------------------------------------- # FLAGS diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index 7646300409..b575d7331b 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.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 @@ -210,12 +210,32 @@ load("MaxBodySize " ++ MaxBodySize, []) -> {ok, Integer} -> {ok, [], {max_body_size,Integer}}; {error, _} -> - {error, ?NICE(clean(MaxBodySize)++ + {error, ?NICE(clean(MaxBodySize) ++ " is an invalid number of MaxBodySize")} end; load("ServerName " ++ ServerName, []) -> - {ok,[],{server_name,clean(ServerName)}}; + {ok,[], {server_name, clean(ServerName)}}; + +load("ServerTokens " ++ ServerTokens, []) -> + %% These are the valid *plain* server tokens: + %% sprod, major, minor, minimum, os, full + %% It can also be a "private" server token: private:<any string> + case string:tokens(ServerTokens, [$:]) of + ["private", Private] -> + {ok,[], {server_tokens, clean(Private)}}; + [TokStr] -> + Tok = list_to_atom(clean(TokStr)), + case lists:member(Tok, [prod, major, minor, minimum, os, full]) of + true -> + {ok,[], {server_tokens, Tok}}; + false -> + {error, ?NICE(clean(ServerTokens) ++ + " is an invalid ServerTokens")} + end; + _ -> + {error, ?NICE(clean(ServerTokens) ++ " is an invalid ServerTokens")} + end; load("SocketType " ++ SocketType, []) -> %% ssl is the same as HTTP_DEFAULT_SSL_KIND @@ -475,7 +495,7 @@ validate_properties(Properties) -> validate_properties2(Properties) -> case proplists:get_value(bind_address, Properties) of undefined -> - case proplists:get_value(sock_type, Properties, ip_comm) of + case proplists:get_value(sock_type, Properties, ip_comm) of ip_comm -> case proplists:get_value(ipfamily, Properties) of undefined -> @@ -537,6 +557,20 @@ validate_config_params([{server_name, Value} | Rest]) validate_config_params([{server_name, Value} | _]) -> throw({server_name, Value}); +validate_config_params([{server_tokens, Value} | Rest]) + when is_atom(Value) -> + case lists:member(Value, plain_server_tokens()) of + true -> + validate_config_params(Rest); + false -> + throw({server_tokens, Value}) + end; +validate_config_params([{server_tokens, {private, Value}} | Rest]) + when is_list(Value) -> + validate_config_params(Rest); +validate_config_params([{server_tokens, Value} | _]) -> + throw({server_tokens, Value}); + validate_config_params([{socket_type, Value} | Rest]) when (Value =:= ip_comm) orelse (Value =:= ssl) orelse @@ -737,9 +771,73 @@ store({log_format, LogFormat}, _ConfigList) store({log_format, LogFormat}, _ConfigList) when (LogFormat =:= compact) orelse (LogFormat =:= pretty) -> {ok, {log_format, LogFormat}}; +store({server_tokens, ServerTokens} = Entry, _ConfigList) -> + Server = server(ServerTokens), + {ok, [Entry, {server, Server}]}; store(ConfigListEntry, _ConfigList) -> {ok, ConfigListEntry}. + +%% The SERVER_SOFTWARE macro has the following structure: +%% <product>/<version> +%% Example: "inets/1.2.3" +%% So, with this example (on a linux machine, with OTP R15B), +%% this will result in: +%% prod: "inets" +%% major: "inets/1" +%% minor: "inets/1.2" +%% minimal: "inets/1.2.3" +%% os: "inets/1.2.3 (unix) +%% full: "inets/1.2.3 (unix/linux) OTP/R15B" +%% Note that the format of SERVER_SOFTWARE is that of 'minimal'. +%% Also, there will always be atleast two digits in a version: +%% Not just 1 but 1.0 +%% +%% We have already checked that the value is valid, +%% so there is no need to check enything here. +%% +server(prod = _ServerTokens) -> + [Prod|_Version] = string:tokens(?SERVER_SOFTWARE, [$/]), + Prod; +server(major = _ServerTokens) -> + [Prod|Version] = string:tokens(?SERVER_SOFTWARE, [$/]), + [Major|_] = string:tokens(Version, [$.]), + Prod ++ "/" ++ Major; +server(minor = _ServerTokens) -> + [Prod|Version] = string:tokens(?SERVER_SOFTWARE, [$/]), + [Major,Minor|_] = string:tokens(Version, [$.]), + Prod ++ "/" ++ Major ++ "." ++ Minor; +server(minimal = _ServerTokens) -> + %% This is the default + ?SERVER_SOFTWARE; +server(os = _ServerTokens) -> + OS = os_info(partial), + lists:flatten(io_lib:format("~s ~s", [?SERVER_SOFTWARE, OS])); +server(full = _ServerTokens) -> + OTPRelease = otp_release(), + OS = os_info(full), + lists:flatten( + io_lib:format("~s ~s OTP/~s", [?SERVER_SOFTWARE, OS, OTPRelease])); +server({private, Server} = _ServerTokens) when is_list(Server) -> + %% The user provide its own + Server; +server(_) -> + ?SERVER_SOFTWARE. + +os_info(Info) -> + case os:type() of + {OsFamily, _OsName} when Info =:= partial -> + lists:flatten(io_lib:format("(~w)", [OsFamily])); + {OsFamily, OsName} -> + lists:flatten(io_lib:format("(~w/~w)", [OsFamily, OsName])); + OsFamily -> + lists:flatten(io_lib:format("(~w)", [OsFamily])) + end. + +otp_release() -> + erlang:system_info(otp_release). + + %% Phase 3: Remove remove_all(ConfigDB) -> Modules = httpd_util:lookup(ConfigDB,modules,[]), @@ -1159,6 +1257,10 @@ ssl_ca_certificate_file(ConfigDB) -> [{cacertfile, File}] end. +plain_server_tokens() -> + [prod, major, minor, minimum, os, full]. + error_report(Where,M,F,Error) -> error_logger:error_report([{?MODULE, Where}, {apply, {M, F, []}}, Error]). + diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl index dd7223876e..2dedb088e4 100644 --- a/lib/inets/src/http_server/httpd_response.erl +++ b/lib/inets/src/http_server/httpd_response.erl @@ -144,10 +144,14 @@ send_response(ModData, Header, Body) -> end end. -send_header(#mod{socket_type = Type, socket = Sock, - http_version = Ver, connection = Conn} = _ModData, +send_header(#mod{socket_type = Type, + socket = Sock, + http_version = Ver, + connection = Conn, + config_db = ConfigDb} = _ModData, StatusCode, KeyValueTupleHeaders) -> - Headers = create_header(lists:map(fun transform/1, KeyValueTupleHeaders)), + Headers = create_header(ConfigDb, + lists:map(fun transform/1, KeyValueTupleHeaders)), NewVer = case {Ver, StatusCode} of {[], _} -> %% May be implicit! @@ -275,13 +279,20 @@ cache_headers(#mod{config_db = Db}) -> [] end. -create_header(KeyValueTupleHeaders) -> - NewHeaders = add_default_headers([{"date", httpd_util:rfc1123_date()}, - {"content-type", "text/html"}, - {"server", ?SERVER_SOFTWARE}], - KeyValueTupleHeaders), +create_header(ConfigDb, KeyValueTupleHeaders) -> + Date = httpd_util:rfc1123_date(), + ContentType = "text/html", + Server = server(ConfigDb), + NewHeaders = add_default_headers([{"date", Date}, + {"content-type", ContentType}, + {"server", Server}], + KeyValueTupleHeaders), lists:map(fun fix_header/1, NewHeaders). + +server(ConfigDb) -> + httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE). + fix_header({Key0, Value}) -> %% make sure first letter is capital Words1 = string:tokens(Key0, "-"), diff --git a/lib/inets/src/http_server/httpd_script_env.erl b/lib/inets/src/http_server/httpd_script_env.erl index d3115150b0..a5613ba4a4 100644 --- a/lib/inets/src/http_server/httpd_script_env.erl +++ b/lib/inets/src/http_server/httpd_script_env.erl @@ -50,29 +50,44 @@ create_env(ScriptType, ModData, ScriptElements) -> %%%======================================================================== %%% Internal functions %%%======================================================================== + +which_server(#mod{config_db = ConfigDb}) -> + httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE). + +which_port(#mod{config_db = ConfigDb}) -> + httpd_util:lookup(ConfigDb, port, 80). + +which_peername(#mod{init_data = #init_data{peername = {_, RemoteAddr}}}) -> + RemoteAddr. + +which_resolve(#mod{init_data = #init_data{resolve = Resolve}}) -> + Resolve. + +which_method(#mod{method = Method}) -> + Method. + +which_request_uri(#mod{request_uri = RUri}) -> + RUri. + create_basic_elements(esi, ModData) -> - {_, RemoteAddr} = (ModData#mod.init_data)#init_data.peername, - [{server_software, ?SERVER_SOFTWARE}, - {server_name, (ModData#mod.init_data)#init_data.resolve}, - {gateway_interface,?GATEWAY_INTERFACE}, - {server_protocol, ?SERVER_PROTOCOL}, - {server_port, httpd_util:lookup(ModData#mod.config_db,port,80)}, - {request_method, ModData#mod.method}, - {remote_addr, RemoteAddr}, - {script_name, ModData#mod.request_uri}]; + [{server_software, which_server(ModData)}, + {server_name, which_resolve(ModData)}, + {gateway_interface, ?GATEWAY_INTERFACE}, + {server_protocol, ?SERVER_PROTOCOL}, + {server_port, which_port(ModData)}, + {request_method, which_method(ModData)}, + {remote_addr, which_peername(ModData)}, + {script_name, which_request_uri(ModData)}]; create_basic_elements(cgi, ModData) -> - {_, RemoteAddr} = (ModData#mod.init_data)#init_data.peername, - [{"SERVER_SOFTWARE",?SERVER_SOFTWARE}, - {"SERVER_NAME", (ModData#mod.init_data)#init_data.resolve}, - {"GATEWAY_INTERFACE",?GATEWAY_INTERFACE}, - {"SERVER_PROTOCOL",?SERVER_PROTOCOL}, - {"SERVER_PORT", - integer_to_list(httpd_util:lookup( - ModData#mod.config_db, port, 80))}, - {"REQUEST_METHOD", ModData#mod.method}, - {"REMOTE_ADDR", RemoteAddr}, - {"SCRIPT_NAME", ModData#mod.request_uri}]. + [{"SERVER_SOFTWARE", which_server(ModData)}, + {"SERVER_NAME", which_resolve(ModData)}, + {"GATEWAY_INTERFACE", ?GATEWAY_INTERFACE}, + {"SERVER_PROTOCOL", ?SERVER_PROTOCOL}, + {"SERVER_PORT", integer_to_list(which_port(ModData))}, + {"REQUEST_METHOD", which_method(ModData)}, + {"REMOTE_ADDR", which_peername(ModData)}, + {"SCRIPT_NAME", which_request_uri(ModData)}]. create_http_header_elements(ScriptType, Headers) -> create_http_header_elements(ScriptType, Headers, []). @@ -80,7 +95,7 @@ create_http_header_elements(ScriptType, Headers) -> create_http_header_elements(_, [], Acc) -> Acc; create_http_header_elements(ScriptType, [{Name, [Value | _] = Values } | - Headers], Acc) + Headers], Acc) when is_list(Value) -> NewName = lists:map(fun(X) -> if X == $- -> $_; true -> X end end, Name), Element = http_env_element(ScriptType, NewName, multi_value(Values)), diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index 264dc9f006..8f3e8f9500 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -162,17 +162,30 @@ httpd_config([Value| _] = Config) when is_tuple(Value) -> httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug) when is_tuple(Value) -> + ?hdrt("httpd_child_spec - entry", [{accept_timeout, AcceptTimeout}, + {debug, Debug}]), Address = proplists:get_value(bind_address, Config, any), Port = proplists:get_value(port, Config, 80), httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port); -httpd_child_spec(ConfigFile, AcceptTimeout, Debug) -> +%% In this case the AcceptTimeout and Debug will only have default values... +httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) -> + ?hdrt("httpd_child_spec - entry", [{config_file, ConfigFile}, + {accept_timeout_def, AcceptTimeoutDef}, + {debug_def, DebugDef}]), case httpd_conf:load(ConfigFile) of {ok, ConfigList} -> + ?hdrt("httpd_child_spec - loaded", [{config_list, ConfigList}]), case (catch httpd_conf:validate_properties(ConfigList)) of {ok, Config} -> + ?hdrt("httpd_child_spec - validated", [{config, Config}]), Address = proplists:get_value(bind_address, Config, any), Port = proplists:get_value(port, Config, 80), + AcceptTimeout = + proplists:get_value(accept_timeout, Config, + AcceptTimeoutDef), + Debug = + proplists:get_value(debug, Config, DebugDef), httpd_child_spec([{file, ConfigFile} | Config], AcceptTimeout, Debug, Address, Port); Error -> @@ -183,7 +196,7 @@ httpd_child_spec(ConfigFile, AcceptTimeout, Debug) -> end. httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) -> - case Port == 0 orelse proplists:is_defined(fd, Config) of + case (Port =:= 0) orelse proplists:is_defined(fd, Config) of true -> httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port); false -> diff --git a/lib/inets/src/http_server/mod_get.erl b/lib/inets/src/http_server/mod_get.erl index 5cb30e3d97..c58d1f3508 100644 --- a/lib/inets/src/http_server/mod_get.erl +++ b/lib/inets/src/http_server/mod_get.erl @@ -18,9 +18,16 @@ %% %% -module(mod_get). + -export([do/1]). + -include("httpd.hrl"). -include("httpd_internal.hrl"). +-include("inets_internal.hrl"). + +-define(VMODULE,"GET"). + + %% do do(Info) -> @@ -84,15 +91,16 @@ send_response(_Socket, _SocketType, Path, Info)-> file:close(FileDescriptor), {proceed,[{response,{already_sent,200, FileInfo#file_info.size}}, - {mime_type,MimeType}|Info#mod.data]}; + {mime_type,MimeType} | Info#mod.data]}; {error, Reason} -> + ?hdrt("send_response -> failed open file", + [{path, Path}, {reason, Reason}]), Status = httpd_file:handle_error(Reason, "open", Info, Path), - {proceed, - [{status, Status}| Info#mod.data]} + {proceed, [{status, Status} | Info#mod.data]} end. %% send - + send(#mod{socket = Socket, socket_type = SocketType} = Info, StatusCode, Headers, FileDescriptor) -> ?DEBUG("send -> send header",[]), diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile index d99e33b4ea..6da6a1d79f 100644 --- a/lib/inets/src/inets_app/Makefile +++ b/lib/inets/src/inets_app/Makefile @@ -46,7 +46,8 @@ MODULES = \ inets_service \ inets_app \ inets_sup \ - inets_regexp + inets_regexp \ + inets_trace INTERNAL_HRL_FILES = inets_internal.hrl EXTERNAL_HRL_FILES = ../../include/httpd.hrl \ diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src index 1db7ed2c30..4aea2ef3d7 100644 --- a/lib/inets/src/inets_app/inets.app.src +++ b/lib/inets/src/inets_app/inets.app.src @@ -18,14 +18,15 @@ %% {application,inets, - [{description,"INETS CXC 138 49"}, - {vsn,"%VSN%"}, + [{description, "INETS CXC 138 49"}, + {vsn, "%VSN%"}, {modules,[ inets, inets_sup, inets_app, inets_service, inets_regexp, + inets_trace, %% FTP ftp, diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index e80cb2a23b..c7029f7b31 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,65 +18,115 @@ {"%VSN%", [ + {"5.8.1", + [ + {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, []}, + + {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, inets, soft_purge, soft_purge, [inets_trace]}, + {update, httpc_handler, soft, soft_purge, soft_purge, []}, + {update, httpd_sup, soft, soft_purge, soft_purge, []}, + {add_module, inets_trace} + ] + }, {"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, [httpc_handler]} + soft_purge, soft_purge, [http_uri, httpc_handler]}, + {update, httpd_sup, soft, soft_purge, soft_purge, []}, + + {add_module, inets_trace} ] }, {"5.7.2", [ {restart_application, inets} ] - }, - {"5.7.1", - [ - {restart_application, inets} - ] - }, - {"5.7", + } + ], + [ + {"5.8.1", [ - {restart_application, inets} + {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, []}, + + {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, inets, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, []}, + {update, httpd_sup, soft, soft_purge, soft_purge, []}, + {remove, {inets_trace, soft_purge, brutal_purge}} ] }, - {"5.6", - [ - {restart_application, inets} - ] - } - ], - [ {"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, [httpc_handler]} + soft_purge, soft_purge, [http_uri, httpc_handler]}, + {update, httpd_sup, soft, soft_purge, soft_purge, []}, + + {remove, {inets_trace, soft_purge, brutal_purge}} ] }, {"5.7.2", [ {restart_application, inets} ] - }, - {"5.7.1", - [ - {restart_application, inets} - ] - }, - {"5.7", - [ - {restart_application, inets} - ] - }, - {"5.6", - [ - {restart_application, inets} - ] } ] }. diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl index 054468e445..8e79f8d456 100644 --- a/lib/inets/src/inets_app/inets.erl +++ b/lib/inets/src/inets_app/inets.erl @@ -28,7 +28,7 @@ stop/0, stop/2, services/0, services_info/0, service_names/0]). --export([enable_trace/2, enable_trace/3, disable_trace/0, set_trace/1, +-export([enable_trace/2, enable_trace/3, disable_trace/0, set_trace/1, report_event/4]). -export([versions/0, print_version_info/0, print_version_info/1]). @@ -409,47 +409,8 @@ service_names() -> %% Severity withing Limit) will be written to stdout using io:format. %% %%----------------------------------------------------------------- -enable_trace(Level, Dest) -> - enable_trace(Level, Dest, all). - -enable_trace(Level, Dest, Service) -> - case valid_trace_service(Service) of - true -> - enable_trace2(Level, Dest, Service); - false -> - {error, {invalid_service, Service}} - end. - -enable_trace2(Level, File, Service) - when is_list(File) -> - case file:open(File, [write]) of - {ok, Fd} -> - HandleSpec = {fun handle_trace/2, {Service, Fd}}, - do_enable_trace(Level, process, HandleSpec); - Err -> - Err - end; -enable_trace2(Level, Port, _) when is_integer(Port) -> - do_enable_trace(Level, port, dbg:trace_port(ip, Port)); -enable_trace2(Level, io, Service) -> - HandleSpec = {fun handle_trace/2, {Service, standard_io}}, - do_enable_trace(Level, process, HandleSpec); -enable_trace2(Level, {Fun, _Data} = HandleSpec, _) when is_function(Fun) -> - do_enable_trace(Level, process, HandleSpec). - -do_enable_trace(Level, Type, HandleSpec) -> - case dbg:tracer(Type, HandleSpec) of - {ok, _} -> - set_trace(Level), - ok; - Error -> - Error - end. - -valid_trace_service(all) -> - true; -valid_trace_service(Service) -> - lists:member(Service, [httpc, httpd, ftpc, tftp]). +enable_trace(Level, Dest) -> inets_trace:enable(Level, Dest). +enable_trace(Level, Dest, Service) -> inets_trace:enable(Level, Dest, Service). %%----------------------------------------------------------------- @@ -458,12 +419,7 @@ valid_trace_service(Service) -> %% Description: %% This function is used to stop tracing. %%----------------------------------------------------------------- -disable_trace() -> - %% This is to make handle_trace/2 close the output file (if the - %% event gets there before dbg closes) - inets:report_event(100, "stop trace", stop_trace, [stop_trace]), - dbg:stop(). - +disable_trace() -> inets_trace:disable(). %%----------------------------------------------------------------- @@ -476,60 +432,7 @@ disable_trace() -> %% This function is used to change the trace level when tracing has %% already been started. %%----------------------------------------------------------------- -set_trace(Level) -> - set_trace(Level, all). - -set_trace(Level, Service) -> - Pat = make_pattern(?MODULE, Service, Level), - change_pattern(Pat). - -make_pattern(Mod, Service, Level) - when is_atom(Mod) andalso is_atom(Service) -> - case Level of - min -> - {Mod, Service, []}; - max -> - Head = ['$1', '_', '_', '_'], - Body = [], - Cond = [], - {Mod, Service, [{Head, Cond, Body}]}; - DetailLevel when is_integer(DetailLevel) -> - Head = ['$1', '_', '_', '_'], - Body = [], - Cond = [{ '=<', '$1', DetailLevel}], - {Mod, Service, [{Head, Cond, Body}]}; - _ -> - exit({bad_level, Level}) - end. - -change_pattern({Mod, Service, Pattern}) - when is_atom(Mod) andalso is_atom(Service) -> - MFA = {Mod, report_event, 4}, - case Pattern of - [] -> - try - error_to_exit(ctp, dbg:ctp(MFA)), - error_to_exit(p, dbg:p(all, clear)) - catch - exit:{Where, Reason} -> - {error, {Where, Reason}} - end; - List when is_list(List) -> - try - error_to_exit(ctp, dbg:ctp(MFA)), - error_to_exit(tp, dbg:tp(MFA, Pattern)), - error_to_exit(p, dbg:p(all, [call, timestamp])) - catch - exit:{Where, Reason} -> - {error, {Where, Reason}} - end - end, - ok. - -error_to_exit(_Where, {ok, _} = OK) -> - OK; -error_to_exit(Where, {error, Reason}) -> - exit({Where, Reason}). +set_trace(Level) -> inets_trace:set_level(Level). %%----------------------------------------------------------------- @@ -546,164 +449,8 @@ error_to_exit(Where, {error, Reason}) -> %% put trace on this function. %%----------------------------------------------------------------- -report_event(Severity, Label, Service, Content) - when (is_integer(Severity) andalso - (Severity >= 0) andalso (100 >= Severity)) andalso - is_list(Label) andalso - is_atom(Service) andalso - is_list(Content) -> - hopefully_traced. - - -%% ---------------------------------------------------------------------- -%% handle_trace(Event, Fd) -> Verbosity -%% -%% Parameters: -%% Event -> The trace event (only megaco 'trace_ts' events are printed) -%% Fd -> standard_io | file_descriptor() | trace_port() -%% -%% Description: -%% This function is used to "receive" and print the trace events. -%% Events are printed if: -%% - Verbosity is max -%% - Severity is =< Verbosity (e.g. Severity = 30, and Verbosity = 40) -%% Events are not printed if: -%% - Verbosity is min -%% - Severity is > Verbosity -%%----------------------------------------------------------------- - -handle_trace(_, closed_file = Fd) -> - Fd; -handle_trace({trace_ts, _Who, call, - {?MODULE, report_event, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - {_, standard_io} = Fd) -> - (catch io:format(standard_io, "stop trace at ~s~n", [format_timestamp(Timestamp)])), - Fd; -handle_trace({trace_ts, _Who, call, - {?MODULE, report_event, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - standard_io = Fd) -> - (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), - Fd; -handle_trace({trace_ts, _Who, call, - {?MODULE, report_event, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - {_Service, Fd}) -> - (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), - (catch file:close(Fd)), - closed_file; -handle_trace({trace_ts, _Who, call, - {?MODULE, report_event, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - Fd) -> - (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), - (catch file:close(Fd)), - closed_file; -handle_trace({trace_ts, Who, call, - {?MODULE, report_event, - [Sev, Label, Service, Content]}, Timestamp}, - Fd) -> - (catch print_inets_trace(Fd, Sev, Timestamp, Who, - Label, Service, Content)), - Fd; -handle_trace(Event, Fd) -> - (catch print_trace(Fd, Event)), - Fd. - - -print_inets_trace({Service, Fd}, - Sev, Timestamp, Who, Label, Service, Content) -> - do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content); -print_inets_trace({ServiceA, Fd}, - Sev, Timestamp, Who, Label, ServiceB, Content) - when (ServiceA =:= all) -> - do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, ServiceB, Content); -print_inets_trace({ServiceA, _Fd}, - _Sev, _Timestamp, _Who, _Label, ServiceB, _Content) - when ServiceA =/= ServiceB -> - ok; -print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> - do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content). - -do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> - Ts = format_timestamp(Timestamp), - io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s " - "~n Content: ~p" - "~n", - [Service, Sev, Who, Ts, Label, Content]). - -print_trace({_, Fd}, Event) -> - do_print_trace(Fd, Event); -print_trace(Fd, Event) -> - do_print_trace(Fd, Event). - -do_print_trace(Fd, {trace, Who, What, Where}) -> - io:format(Fd, "[trace]" - "~n Who: ~p" - "~n What: ~p" - "~n Where: ~p" - "~n", [Who, What, Where]); - -do_print_trace(Fd, {trace, Who, What, Where, Extra}) -> - io:format(Fd, "[trace]" - "~n Who: ~p" - "~n What: ~p" - "~n Where: ~p" - "~n Extra: ~p" - "~n", [Who, What, Where, Extra]); - -do_print_trace(Fd, {trace_ts, Who, What, Where, When}) -> - Ts = format_timestamp(When), - io:format(Fd, "[trace ~s]" - "~n Who: ~p" - "~n What: ~p" - "~n Where: ~p" - "~n", [Ts, Who, What, Where]); - -do_print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) -> - Ts = format_timestamp(When), - io:format(Fd, "[trace ~s]" - "~n Who: ~p" - "~n What: ~p" - "~n Where: ~p" - "~n Extra: ~p" - "~n", [Ts, Who, What, Where, Extra]); - -do_print_trace(Fd, {seq_trace, What, Where}) -> - io:format(Fd, "[seq trace]" - "~n What: ~p" - "~n Where: ~p" - "~n", [What, Where]); - -do_print_trace(Fd, {seq_trace, What, Where, When}) -> - Ts = format_timestamp(When), - io:format(Fd, "[seq trace ~s]" - "~n What: ~p" - "~n Where: ~p" - "~n", [Ts, What, Where]); - -do_print_trace(Fd, {drop, Num}) -> - io:format(Fd, "[drop trace] ~p~n", [Num]); - -do_print_trace(Fd, Trace) -> - io:format(Fd, "[trace] " - "~n ~p" - "~n", [Trace]). - - -format_timestamp({_N1, _N2, N3} = Now) -> - {Date, Time} = calendar:now_to_datetime(Now), - {YYYY,MM,DD} = Date, - {Hour,Min,Sec} = Time, - FormatDate = - io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", - [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), - lists:flatten(FormatDate). +report_event(Severity, Label, Service, Content) -> + inets_trace:report_event(Severity, Label, Service, Content). %%-------------------------------------------------------------------- diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk index 194b4ca2b1..d24cc0aea3 100644 --- a/lib/inets/src/inets_app/inets.mk +++ b/lib/inets/src/inets_app/inets.mk @@ -41,8 +41,6 @@ INETS_APP_VSN_COMPILE_FLAGS = \ +'{parse_transform,sys_pre_attributes}' \ +'{attribute,insert,app_vsn,$(APP_VSN)}' -INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' - INETS_ERL_COMPILE_FLAGS += \ -pa $(ERL_TOP)/lib/inets/ebin \ $(INETS_APP_VSN_COMPILE_FLAGS) diff --git a/lib/inets/src/inets_app/inets_app.erl b/lib/inets/src/inets_app/inets_app.erl index cae79a6767..77c39d85eb 100644 --- a/lib/inets/src/inets_app/inets_app.erl +++ b/lib/inets/src/inets_app/inets_app.erl @@ -24,7 +24,7 @@ -export([start/2, stop/1]). start(_Type, _State) -> - supervisor:start_link({local, inets_sup}, inets_sup, []). + inets_sup:start_link(). stop(_State) -> ok. diff --git a/lib/inets/src/inets_app/inets_internal.hrl b/lib/inets/src/inets_app/inets_internal.hrl index 55c3669e4a..e56af3b59d 100644 --- a/lib/inets/src/inets_app/inets_internal.hrl +++ b/lib/inets/src/inets_app/inets_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -24,8 +24,8 @@ %% Various trace macros -define(report(Severity, Label, Service, Content), - inets:report_event(Severity, Label, Service, - [{module, ?MODULE}, {line, ?LINE} | Content])). + inets_trace:report_event(Severity, Label, Service, + [{module, ?MODULE}, {line, ?LINE} | Content])). -define(report_important(Label, Service, Content), ?report(20, Label, Service, Content)). -define(report_verbose(Label, Service, Content), diff --git a/lib/inets/src/inets_app/inets_sup.erl b/lib/inets/src/inets_app/inets_sup.erl index 20d5ef343e..0382362778 100644 --- a/lib/inets/src/inets_app/inets_sup.erl +++ b/lib/inets/src/inets_app/inets_sup.erl @@ -25,9 +25,19 @@ -behaviour(supervisor). +%% External API +-export([start_link/0]). + +%% Supervisor callbacks -export([init/1]). %%%========================================================================= +%%% External functions +%%%========================================================================= +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%%%========================================================================= %%% Supervisor callback %%%========================================================================= init([]) -> diff --git a/lib/inets/src/inets_app/inets_trace.erl b/lib/inets/src/inets_app/inets_trace.erl new file mode 100644 index 0000000000..8911f65897 --- /dev/null +++ b/lib/inets/src/inets_app/inets_trace.erl @@ -0,0 +1,357 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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% +%% +%% +%%---------------------------------------------------------------------- +%% Purpose: Module for debug trace functions of the inets application +%%---------------------------------------------------------------------- + +-module(inets_trace). + +%% API +-export([enable/2, enable/3, + disable/0, + set_level/1, set_level/2, + report_event/4]). + + +%%==================================================================== +%% API +%%==================================================================== + +%%----------------------------------------------------------------- +%% enable(Level, Destination) -> void() +%% enable(Level, Destination, Service) -> void() +%% +%% Parameters: +%% Level -> max | min | integer() +%% Destination -> File | Port | io | HandlerSpec +%% Service -> httpc | httpd | ftpc | tftp | all +%% File -> string() +%% Port -> integer() +%% Verbosity -> true | false +%% HandlerSpec = {function(), Data} +%% Data = term() +%% +%% Description: +%% This function is used to start tracing at level Level and send +%% the result either to the file File, the port Port or to a +%% trace handler. +%% Note that it starts a tracer server. +%% When Destination is the atom io (or the tuple {io, Verbosity}), +%% all (printable) inets trace events (trace_ts events which has +%% Severity withing Limit) will be written to stdout using io:format. +%% +%%----------------------------------------------------------------- +enable(Level, Dest) -> + enable(Level, Dest, all). + +enable(Level, Dest, Service) -> + case valid_trace_service(Service) of + true -> + enable2(Level, Dest, Service); + false -> + {error, {invalid_service, Service}} + end. + +enable2(Level, File, Service) + when is_list(File) -> + case file:open(File, [write]) of + {ok, Fd} -> + HandleSpec = {fun handle_trace/2, {Service, Fd}}, + do_enable(Level, process, HandleSpec); + Err -> + Err + end; +enable2(Level, Port, _) when is_integer(Port) -> + do_enable(Level, port, dbg:trace_port(ip, Port)); +enable2(Level, io, Service) -> + HandleSpec = {fun handle_trace/2, {Service, standard_io}}, + do_enable(Level, process, HandleSpec); +enable2(Level, {Fun, _Data} = HandleSpec, _) when is_function(Fun) -> + do_enable(Level, process, HandleSpec). + +do_enable(Level, Type, HandleSpec) -> + case dbg:tracer(Type, HandleSpec) of + {ok, _} -> + set_level(Level), + ok; + Error -> + Error + end. + +valid_trace_service(all) -> + true; +valid_trace_service(Service) -> + lists:member(Service, [httpc, httpd, ftpc, tftp]). + + +%%----------------------------------------------------------------- +%% disable() -> void() +%% +%% Description: +%% This function is used to stop tracing. +%%----------------------------------------------------------------- + +disable() -> + %% This is to make handle_trace/2 close the output file (if the + %% event gets there before dbg closes) + inets_trace:report_event(100, "stop trace", stop_trace, [stop_trace]), + dbg:stop(). + + + +%%----------------------------------------------------------------- +%% set_level(Level) -> void() +%% +%% Parameters: +%% Level -> max | min | integer() +%% +%% Description: +%% This function is used to change the trace level when tracing has +%% already been started. +%%----------------------------------------------------------------- +set_level(Level) -> + set_level(Level, all). + +set_level(Level, Service) -> + Pat = make_pattern(?MODULE, Service, Level), + change_pattern(Pat). + +make_pattern(Mod, Service, Level) + when is_atom(Mod) andalso is_atom(Service) -> + case Level of + min -> + {Mod, Service, []}; + max -> + Head = ['$1', '_', '_', '_'], + Body = [], + Cond = [], + {Mod, Service, [{Head, Cond, Body}]}; + DetailLevel when is_integer(DetailLevel) -> + Head = ['$1', '_', '_', '_'], + Body = [], + Cond = [{ '=<', '$1', DetailLevel}], + {Mod, Service, [{Head, Cond, Body}]}; + _ -> + exit({bad_level, Level}) + end. + +change_pattern({Mod, Service, Pattern}) + when is_atom(Mod) andalso is_atom(Service) -> + MFA = {Mod, report_event, 4}, + case Pattern of + [] -> + try + error_to_exit(ctp, dbg:ctp(MFA)), + error_to_exit(p, dbg:p(all, clear)) + catch + exit:{Where, Reason} -> + {error, {Where, Reason}} + end; + List when is_list(List) -> + try + error_to_exit(ctp, dbg:ctp(MFA)), + error_to_exit(tp, dbg:tp(MFA, Pattern)), + error_to_exit(p, dbg:p(all, [call, timestamp])) + catch + exit:{Where, Reason} -> + {error, {Where, Reason}} + end + end. + +error_to_exit(_Where, {ok, _} = OK) -> + OK; +error_to_exit(Where, {error, Reason}) -> + exit({Where, Reason}). + + +%%----------------------------------------------------------------- +%% report_event(Severity, Label, Service, Content) +%% +%% Parameters: +%% Severity -> 0 =< integer() =< 100 +%% Label -> string() +%% Service -> httpd | httpc | ftp | tftp +%% Content -> [{tag, term()}] +%% +%% Description: +%% This function is used to generate trace events, that is, +%% put trace on this function. +%%----------------------------------------------------------------- + +report_event(Severity, Label, Service, Content) + when (is_integer(Severity) andalso + (Severity >= 0) andalso (100 >= Severity)) andalso + is_list(Label) andalso + is_atom(Service) andalso + is_list(Content) -> + hopefully_traced. + + +%% ---------------------------------------------------------------------- +%% handle_trace(Event, Fd) -> Verbosity +%% +%% Parameters: +%% Event -> The trace event +%% Fd -> standard_io | file_descriptor() | trace_port() +%% +%% Description: +%% This function is used to "receive" and pretty print the trace events. +%% Events are printed if: +%% - Verbosity is max +%% - Severity is =< Verbosity (e.g. Severity = 30, and Verbosity = 40) +%% Events are not printed if: +%% - Verbosity is min +%% - Severity is > Verbosity +%%----------------------------------------------------------------- + +handle_trace(_, closed_file = Fd) -> + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + {_, standard_io} = Fd) -> + (catch io:format(standard_io, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + standard_io = Fd) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + {_Service, Fd}) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + (catch file:close(Fd)), + closed_file; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + Fd) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + (catch file:close(Fd)), + closed_file; +handle_trace({trace_ts, Who, call, + {?MODULE, report_event, + [Sev, Label, Service, Content]}, Timestamp}, + Fd) -> + (catch print_inets_trace(Fd, Sev, Timestamp, Who, + Label, Service, Content)), + Fd; +handle_trace(Event, Fd) -> + (catch print_trace(Fd, Event)), + Fd. + + +print_inets_trace({Service, Fd}, + Sev, Timestamp, Who, Label, Service, Content) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content); +print_inets_trace({ServiceA, Fd}, + Sev, Timestamp, Who, Label, ServiceB, Content) + when (ServiceA =:= all) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, ServiceB, Content); +print_inets_trace({ServiceA, _Fd}, + _Sev, _Timestamp, _Who, _Label, ServiceB, _Content) + when ServiceA =/= ServiceB -> + ok; +print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content). + +do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> + Ts = format_timestamp(Timestamp), + io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s " + "~n Content: ~p" + "~n", + [Service, Sev, Who, Ts, Label, Content]). + +print_trace({_, Fd}, Event) -> + do_print_trace(Fd, Event); +print_trace(Fd, Event) -> + do_print_trace(Fd, Event). + +do_print_trace(Fd, {trace, Who, What, Where}) -> + io:format(Fd, "[trace]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n", [Who, What, Where]); + +do_print_trace(Fd, {trace, Who, What, Where, Extra}) -> + io:format(Fd, "[trace]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n Extra: ~p" + "~n", [Who, What, Where, Extra]); + +do_print_trace(Fd, {trace_ts, Who, What, Where, When}) -> + Ts = format_timestamp(When), + io:format(Fd, "[trace ~s]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n", [Ts, Who, What, Where]); + +do_print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) -> + Ts = format_timestamp(When), + io:format(Fd, "[trace ~s]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n Extra: ~p" + "~n", [Ts, Who, What, Where, Extra]); + +do_print_trace(Fd, {seq_trace, What, Where}) -> + io:format(Fd, "[seq trace]" + "~n What: ~p" + "~n Where: ~p" + "~n", [What, Where]); + +do_print_trace(Fd, {seq_trace, What, Where, When}) -> + Ts = format_timestamp(When), + io:format(Fd, "[seq trace ~s]" + "~n What: ~p" + "~n Where: ~p" + "~n", [Ts, What, Where]); + +do_print_trace(Fd, {drop, Num}) -> + io:format(Fd, "[drop trace] ~p~n", [Num]); + +do_print_trace(Fd, Trace) -> + io:format(Fd, "[trace] " + "~n ~p" + "~n", [Trace]). + + +format_timestamp({_N1, _N2, N3} = Now) -> + {Date, Time} = calendar:now_to_datetime(Now), + {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", + [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), + lists:flatten(FormatDate). + + diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 881266b70a..a116edef77 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -146,6 +146,20 @@ groups() -> ]. +init_per_group(ipv6 = _GroupName, Config) -> + case inets_test_lib:has_ipv6_support() of + {ok, _} -> + Config; + _ -> + {skip, "Host does not support IPv6"} + end; +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -156,6 +170,9 @@ groups() -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> + + ?PRINT_SYSTEM_INFO([]), + PrivDir = ?config(priv_dir, Config), DataDir = ?config(data_dir, Config), ServerRoot = filename:join(PrivDir, "server_root"), @@ -179,10 +196,11 @@ init_per_suite(Config) -> {ok, FileInfo} = file:read_file_info(Cgi), ok = file:write_file_info(Cgi, FileInfo#file_info{mode = 8#00755}), - [{server_root, ServerRoot}, - {doc_root, DocRoot}, - {local_port, ?IP_PORT}, - {local_ssl_port, ?SSL_PORT} | Config]. + [{has_ipv6_support, inets_test_lib:has_ipv6_support()}, + {server_root, ServerRoot}, + {doc_root, DocRoot}, + {local_port, ?IP_PORT}, + {local_ssl_port, ?SSL_PORT} | Config]. %%-------------------------------------------------------------------- @@ -198,6 +216,7 @@ end_per_suite(Config) -> application:stop(ssl), ok. + %%-------------------------------------------------------------------- %% Function: init_per_testcase(Case, Config) -> Config %% Case - atom() @@ -229,12 +248,12 @@ init_per_testcase(initial_server_connect = Case, Config) -> "~n ~p", [Case, App, ActualError]), SkipString = "Could not start " ++ atom_to_list(App), - {skip, SkipString}; + skip(SkipString); _:X -> SkipString = lists:flatten( io_lib:format("Failed starting apps: ~p", [X])), - {skip, SkipString} + skip(SkipString) end; init_per_testcase(Case, Config) -> @@ -244,6 +263,7 @@ init_per_testcase(Case, Timeout, Config) -> io:format(user, "~n~n*** INIT ~w:~w[~w] ***" "~n~n", [?MODULE, Case, Timeout]), + PrivDir = ?config(priv_dir, Config), application:stop(inets), Dog = test_server:timetrap(inets_test_lib:minutes(Timeout)), @@ -337,7 +357,8 @@ init_per_testcase(Case, Timeout, Config) -> inets:start(httpc, [{profile, Profile}, {data_dir, PrivDir}], stand_alone), - httpc:set_options([{ipfamily, inet6}], ProfilePid), + ok = httpc:set_options([{ipfamily, inet6}], + ProfilePid), tsp("httpc profile pid: ~p", [ProfilePid]), [{watchdog, Dog}, {profile, ProfilePid}| TmpConfig] catch @@ -346,27 +367,67 @@ init_per_testcase(Case, Timeout, Config) -> "~n ~p", [Case, App, ActualError]), SkipString = "Could not start " ++ atom_to_list(App), - {skip, SkipString}; + skip(SkipString); _:X -> SkipString = lists:flatten( io_lib:format("Failed starting apps: ~p", [X])), - {skip, SkipString} + skip(SkipString) end; _ -> + %% Try inet6fb4 on windows... + %% No need? Since it is set above? + + %% tsp("init_per_testcase -> allways try IPv6 on windows"), + %% ?RUN_ON_WINDOWS( + %% fun() -> + %% tsp("init_per_testcase:set_options_fun -> " + %% "set-option ipfamily to inet6fb4"), + %% Res = httpc:set_options([{ipfamily, inet6fb4}]), + %% tsp("init_per_testcase:set_options_fun -> " + %% "~n Res: ~p", [Res]), + %% Res + %% end), + TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig), %% Will start inets + tsp("init_per_testcase -> try start server"), Server = start_http_server(PrivDir, IpConfFile), [{watchdog, Dog}, {local_server, Server} | TmpConfig2] end, + %% <IPv6> + %% Set default ipfamily to the same as the main server has by default + %% This makes the client try w/ ipv6 before falling back to ipv4, + %% as that is what the server is configured to do. + %% Note that this is required for the tests to run on *BSD w/ ipv6 enabled + %% as well as on Windows. The Linux behaviour of allowing ipv4 connects + %% to ipv6 sockets is not required or even encouraged. + + tsp("init_per_testcase -> Options before ipfamily set: ~n~p", + [httpc:get_options(all)]), + ok = httpc:set_options([{ipfamily, inet6fb4}]), + tsp("init_per_testcase -> Options after ipfamily set: ~n~p", + [httpc:get_options(all)]), + + %% Note that the IPv6 test case(s) *must* use inet6, + %% so this value will be overwritten (see "ipv6_" below). + %% </IPv6> + %% This will fail for the ipv6_ - cases (but that is ok) ProxyExceptions = ["localhost", ?IPV6_LOCAL_HOST], - httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ProxyExceptions}}]), + tsp("init_per_testcase -> Options before proxy set: ~n~p", + [httpc:get_options(all)]), + ok = httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ProxyExceptions}}]), + tsp("init_per_testcase -> Options after proxy set: ~n~p", + [httpc:get_options(all)]), inets:enable_trace(max, io, httpc), %% inets:enable_trace(max, io, all), %% snmp:set_trace([gen_tcp]), + tsp("init_per_testcase(~w) -> done when" + "~n NewConfig: ~p" + "~n~n", [Case, NewConfig]), NewConfig. @@ -405,6 +466,7 @@ end_per_testcase(http_save_to_file = Case, Config) -> end_per_testcase(Case, Config) -> io:format(user, "~n~n*** END ~w:~w ***~n~n", [?MODULE, Case]), + dbg:stop(), % ? case atom_to_list(Case) of "ipv6_" ++ _Rest -> tsp("end_per_testcase(~w) -> stop ssl", [Case]), @@ -449,29 +511,39 @@ http_options(doc) -> http_options(suite) -> []; http_options(Config) when is_list(Config) -> - {skip, "Not supported by httpd"}. + skip("Not supported by httpd"). http_head(doc) -> ["Test http head request against local server."]; http_head(suite) -> []; http_head(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - case httpc:request(head, {URL, []}, [], []) of - {ok, {{_,200,_}, [_ | _], []}} -> - ok; - {ok, WrongReply} -> - tsf({wrong_reply, WrongReply}); - Error -> - tsf({failed, Error}) - end; - _ -> - {skip, "Failed to start local http-server"} - end. + tsp("http_head -> entry with" + "~n Config: ~p", [Config]), + Method = head, + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + Request = {URL, []}, + HttpOpts = [], + Opts = [], + VerifyResult = + fun({ok, {{_,200,_}, [_ | _], []}}) -> + ok; + ({ok, UnexpectedReply}) -> + tsp("http_head:verify_fun -> Unexpected Reply: " + "~n ~p", [UnexpectedReply]), + tsf({unexpected_reply, UnexpectedReply}); + ({error, Reason} = Error) -> + tsp("http_head:verify_fun -> Error reply: " + "~n Reason: ~p", [Reason]), + tsf({bad_reply, Error}) + end, + simple_request_and_verify(Config, + Method, Request, HttpOpts, Opts, VerifyResult). + + %%------------------------------------------------------------------------- + http_get(doc) -> ["Test http get request against local server"]; http_get(suite) -> @@ -488,7 +560,8 @@ http_get(Config) when is_list(Config) -> Request = {URL, []}, Timeout = timer:seconds(1), ConnTimeout = Timeout + timer:seconds(1), - HttpOptions1 = [{timeout, Timeout}, {connect_timeout, ConnTimeout}], + HttpOptions1 = [{timeout, Timeout}, + {connect_timeout, ConnTimeout}], Options1 = [], Body = case httpc:request(Method, Request, HttpOptions1, Options1) of @@ -516,14 +589,15 @@ http_get(Config) when is_list(Config) -> tsf({bad_reply, Error2}) end; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- + http_post(doc) -> - ["Test http post request against local server. We do in this case" - " only care about the client side of the the post. The server" - " script will not actually use the post data."]; + ["Test http post request against local server. We do in this case " + "only care about the client side of the the post. The server " + "script will not actually use the post data."]; http_post(suite) -> []; http_post(Config) when is_list(Config) -> @@ -551,7 +625,7 @@ http_post(Config) when is_list(Config) -> httpc:request(post, {URL, [{"expect","100-continue"}], "text/plain", "foobar"}, [], []); _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- @@ -597,7 +671,7 @@ http_post_streaming(Config) when is_list(Config) -> "text/plain", {BodyFun, 10}}, [], []); _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -621,7 +695,7 @@ http_emulate_lower_versions(Config) when is_list(Config) -> httpc:request(get, {URL, []}, [{version, "HTTP/1.1"}], []), inets_test_lib:check_body(Body2); _-> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -682,7 +756,7 @@ http_inets_pipe(Config) when is_list(Config) -> URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", test_pipeline(URL); _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -818,7 +892,7 @@ http_trace(Config) when is_list(Config) -> tsf({failed, Error}) end; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- http_async(doc) -> @@ -853,7 +927,7 @@ http_async(Config) when is_list(Config) -> ok end; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- @@ -874,7 +948,7 @@ http_save_to_file(Config) when is_list(Config) -> {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), Bin == Body; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -904,7 +978,7 @@ http_save_to_file_async(Config) when is_list(Config) -> {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), Bin == Body; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- http_headers(doc) -> @@ -961,7 +1035,7 @@ http_headers(Config) when is_list(Config) -> ]}, [], []), ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- @@ -1096,9 +1170,9 @@ ssl_head(SslTag, Config) -> {ok, {{_,200, _}, [_ | _], []}} = httpc:request(head, {URL, []}, [{ssl, SSLConfig}], []); {ok, _} -> - {skip, "local http-server not started"}; + skip("local http-server not started"); _ -> - {skip, "SSL not started"} + skip("SSL not started") end. @@ -1136,13 +1210,24 @@ ssl_get(SslTag, Config) when is_list(Config) -> "~n URL: ~p" "~n SslTag: ~p" "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), - {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} = - httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []), - inets_test_lib:check_body(Body); + case httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []) of + {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} -> + inets_test_lib:check_body(Body), + ok; + {ok, {StatusLine, Headers, _Body}} -> + tsp("ssl_get -> unexpected result: " + "~n StatusLine: ~p" + "~n Headers: ~p", [StatusLine, Headers]), + tsf({unexpected_response, StatusLine, Headers}); + {error, Reason} -> + tsp("ssl_get -> request failed: " + "~n Reason: ~p", [Reason]), + tsf({request_failed, Reason}) + end; {ok, _} -> - {skip, "local http-server not started"}; + skip("local http-server not started"); _ -> - {skip, "SSL not started"} + skip("SSL not started") end. @@ -1191,9 +1276,9 @@ ssl_trace(SslTag, Config) when is_list(Config) -> tsf({failed, Error}) end; {ok, _} -> - {skip, "local http-server not started"}; + skip("local http-server not started"); _ -> - {skip, "SSL not started"} + skip("SSL not started") end. @@ -1208,8 +1293,8 @@ http_redirect(Config) when is_list(Config) -> "~n Config: ~p", [Config]), case ?config(local_server, Config) of ok -> - tsp("http_redirect -> set ipfamily option to inet"), - ok = httpc:set_options([{ipfamily, inet}]), + %% tsp("http_redirect -> set ipfamily option to inet"), + %% ok = httpc:set_options([{ipfamily, inet}]), tsp("http_redirect -> start dummy server inet"), {DummyServerPid, Port} = dummy_server(ipv4), @@ -1312,7 +1397,7 @@ http_redirect(Config) when is_list(Config) -> ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -1449,7 +1534,7 @@ proxy_options(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1470,7 +1555,7 @@ proxy_head(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1489,7 +1574,7 @@ proxy_get(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. %%------------------------------------------------------------------------- @@ -1530,12 +1615,13 @@ proxy_emulate_lower_versions(Config) when is_list(Config) -> end; Reason -> - {skip, Reason} + skip(Reason) end. pelv_get(Version) -> httpc:request(get, {?PROXY_URL, []}, [{version, Version}], []). + %%------------------------------------------------------------------------- proxy_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; @@ -1544,8 +1630,8 @@ proxy_trace(suite) -> proxy_trace(Config) when is_list(Config) -> %%{ok, {{_,200,_}, [_ | _], "TRACE " ++ _}} = %% httpc:request(trace, {?PROXY_URL, []}, [], []), - {skip, "HTTP TRACE is no longer allowed on the ?PROXY_URL server due " - "to security reasons"}. + skip("HTTP TRACE is no longer allowed on the ?PROXY_URL server due " + "to security reasons"). %%------------------------------------------------------------------------- @@ -1568,7 +1654,7 @@ proxy_post(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1593,7 +1679,7 @@ proxy_put(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1618,7 +1704,7 @@ proxy_delete(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1652,9 +1738,10 @@ proxy_headers(Config) when is_list(Config) -> ]}, [], []), ok; Reason -> - {skip, Reason} + skip(Reason) end. + %%------------------------------------------------------------------------- proxy_auth(doc) -> ["Test the code for sending of proxy authorization."]; @@ -1674,7 +1761,7 @@ proxy_auth(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1718,7 +1805,7 @@ proxy_page_does_not_exist(Config) when is_list(Config) -> httpc:request(get, {URL, []}, [], []), ok; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1737,6 +1824,7 @@ proxy_https_not_supported(Config) when is_list(Config) -> tsf({unexpected_reason, Result}) end. + %%------------------------------------------------------------------------- http_stream(doc) -> @@ -1863,7 +1951,7 @@ proxy_stream(Config) when is_list(Config) -> Body == binary_to_list(StreamedBody); Reason -> - {skip, Reason} + skip(Reason) end. @@ -1886,7 +1974,7 @@ parse_url(Config) when is_list(Config) -> http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", [{foo, false}]), {error, - {malformed_url,"http://2010:836B:4179::836B:4179/foobar.html"}} = + {malformed_url, _, "http://2010:836B:4179::836B:4179/foobar.html"}} = http_uri:parse("http://2010:836B:4179::836B:4179/foobar.html"), %% ipv4 @@ -1902,8 +1990,8 @@ parse_url(Config) when is_list(Config) -> http_uri:parse("http://nisse:foobar@localhost:8888/foobar.html"), %% Scheme error - {error,no_scheme} = http_uri:parse("localhost/foobar.html"), - {error,{not_supported_scheme,localhost}} = + {error, no_scheme} = http_uri:parse("localhost/foobar.html"), + {error, {malformed_url, _, _}} = http_uri:parse("localhost:8888/foobar.html"), %% Query @@ -1966,7 +2054,7 @@ ipv6_essl(Config) when is_list(Config) -> ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) -> %% Check if we are a IPv6 host - tsp("ipv6 -> verify ipv6 support", []), + tsp("ipv6 -> verify ipv6 support"), case inets_test_lib:has_ipv6_support(Config) of {ok, Addr} -> tsp("ipv6 -> ipv6 supported: ~p", [Addr]), @@ -1997,8 +2085,8 @@ ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) -> end, ok; _ -> - tsp("ipv6 -> ipv6 not supported", []), - {skip, "Host does not support IPv6"} + tsp("ipv6 -> ipv6 not supported"), + skip("Host does not support IPv6") end. @@ -2355,7 +2443,7 @@ options(Config) when is_list(Config) -> = httpc:request(get, {URL, []}, [{timeout, infinity}], [{full_result, false}]); _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -2468,7 +2556,7 @@ proxy_not_modified_otp_6821(Config) when is_list(Config) -> undefined -> provocate_not_modified_bug(?PROXY_URL); Reason -> - {skip, Reason} + skip(Reason) end. @@ -2870,7 +2958,7 @@ otp_8106_pid(Config) when is_list(Config) -> ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -2890,7 +2978,7 @@ otp_8106_fun(Config) when is_list(Config) -> ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -2910,7 +2998,7 @@ otp_8106_mfa(Config) when is_list(Config) -> ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -3059,7 +3147,7 @@ otp_8352(Config) when is_list(Config) -> ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -3254,7 +3342,11 @@ create_config(FileName, ComType, Port, PrivDir, ServerRoot, DocRoot, " mod_include mod_dir mod_get mod_head" " mod_log mod_disk_log mod_trace", + %% BindAddress = "*|inet", % Force the use of IPv4 + BindAddress = "*", % This corresponds to using IpFamily inet6fb4 + HttpConfig = [ + cline(["BindAddress ", BindAddress]), cline(["Port ", integer_to_list(Port)]), cline(["ServerName ", "httpc_test"]), cline(["SocketType ", atom_to_list(ComType)]), @@ -3813,6 +3905,34 @@ pick_header(Headers, Name) -> Val end. + +%% ------------------------------------------------------------------------- + +simple_request_and_verify(Config, + Method, Request, HttpOpts, Opts, VerifyResult) + when (is_list(Config) andalso + is_atom(Method) andalso + is_list(HttpOpts) andalso + is_list(Opts) andalso + is_function(VerifyResult, 1)) -> + tsp("request_and_verify -> entry with" + "~n Method: ~p" + "~n Request: ~p" + "~n HttpOpts: ~p" + "~n Opts: ~p", [Method, Request, HttpOpts, Opts]), + case ?config(local_server, Config) of + ok -> + tsp("request_and_verify -> local-server running"), + Result = (catch httpc:request(Method, Request, HttpOpts, Opts)), + VerifyResult(Result); + _ -> + tsp("request_and_verify -> local-server *not* running - skip"), + hard_skip("Local http-server not running") + end. + + + + not_implemented_yet() -> exit(not_implemented_yet). @@ -3823,9 +3943,9 @@ p(F, A) -> io:format("~p ~w:" ++ F ++ "~n", [self(), ?MODULE | A]). tsp(F) -> - inets_test_lib:tsp(F). + inets_test_lib:tsp("[~w]" ++ F, [?MODULE]). tsp(F, A) -> - inets_test_lib:tsp(F, A). + inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]). tsf(Reason) -> test_server:fail(Reason). @@ -3864,6 +3984,8 @@ dummy_ssl_server_hang_loop(_) -> ok end. +hard_skip(Reason) -> + throw(skip(Reason)). skip(Reason) -> {skip, Reason}. diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index a4bb8f7159..41e4188e5f 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.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,7 +224,8 @@ all() -> ]. groups() -> - [{ip, [], + [ + {ip, [], [ip_mod_alias, ip_mod_actions, ip_mod_security, ip_mod_auth, ip_mod_auth_api, ip_mod_auth_mnesia_api, ip_mod_htaccess, ip_mod_cgi, ip_mod_esi, ip_mod_get, @@ -293,6 +294,14 @@ groups() -> [ticket_5775, ticket_5865, ticket_5913, ticket_6003, ticket_7304]}]. + +init_per_group(ipv6 = _GroupName, Config) -> + case inets_test_lib:has_ipv6_support() of + {ok, _} -> + Config; + _ -> + {skip, "Host does not support IPv6"} + end; init_per_group(_GroupName, Config) -> Config. @@ -314,6 +323,8 @@ init_per_suite(Config) -> "~n Config: ~p" "~n", [Config]), + ?PRINT_SYSTEM_INFO([]), + PrivDir = ?config(priv_dir, Config), SuiteTopDir = filename:join(PrivDir, ?MODULE), case file:make_dir(SuiteTopDir) of @@ -325,10 +336,11 @@ init_per_suite(Config) -> throw({error, {failed_creating_suite_top_dir, Error}}) end, - [{suite_top_dir, SuiteTopDir}, - {node, node()}, - {host, inets_test_lib:hostname()}, - {address, getaddr()} | Config]. + [{has_ipv6_support, inets_test_lib:has_ipv6_support()}, + {suite_top_dir, SuiteTopDir}, + {node, node()}, + {host, inets_test_lib:hostname()}, + {address, getaddr()} | Config]. %%-------------------------------------------------------------------- @@ -363,10 +375,9 @@ init_per_testcase(Case, Config) -> init_per_testcase2(Case, Config) -> - io:format(user, "~w:init_per_testcase2(~w) -> entry with" - "~n Config: ~p" - "~n", [?MODULE, Case, Config]), - + tsp("init_per_testcase2 -> entry with" + "~n Config: ~p", [Config]), + IpNormal = integer_to_list(?IP_PORT) ++ ".conf", IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf", SslNormal = integer_to_list(?SSL_PORT) ++ ".conf", @@ -375,39 +386,33 @@ init_per_testcase2(Case, Config) -> DataDir = ?config(data_dir, Config), SuiteTopDir = ?config(suite_top_dir, Config), - io:format(user, "~w:init_per_testcase2(~w) -> " - "~n SuiteDir: ~p" - "~n DataDir: ~p" - "~n", [?MODULE, Case, SuiteTopDir, DataDir]), + tsp("init_per_testcase2 -> " + "~n SuiteDir: ~p" + "~n DataDir: ~p", [SuiteTopDir, DataDir]), TcTopDir = filename:join(SuiteTopDir, Case), ?line ok = file:make_dir(TcTopDir), - io:format(user, "~w:init_per_testcase2(~w) -> " - "~n TcTopDir: ~p" - "~n", [?MODULE, Case, TcTopDir]), + tsp("init_per_testcase2 -> " + "~n TcTopDir: ~p", [TcTopDir]), DataSrc = filename:join([DataDir, "server_root"]), ServerRoot = filename:join([TcTopDir, "server_root"]), - io:format(user, "~w:init_per_testcase2(~w) -> " - "~n DataSrc: ~p" - "~n ServerRoot: ~p" - "~n", [?MODULE, Case, DataSrc, ServerRoot]), + tsp("init_per_testcase2 -> " + "~n DataSrc: ~p" + "~n ServerRoot: ~p", [DataSrc, ServerRoot]), ok = file:make_dir(ServerRoot), ok = file:make_dir(filename:join([TcTopDir, "logs"])), NewConfig = [{tc_top_dir, TcTopDir}, {server_root, ServerRoot} | Config], - io:format(user, "~w:init_per_testcase2(~w) -> " - "copy DataSrc to ServerRoot~n", - [?MODULE, Case]), + tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"), inets_test_lib:copy_dirs(DataSrc, ServerRoot), - io:format(user, "~w:init_per_testcase2(~w) -> fix cgi~n", - [?MODULE, Case]), + tsp("init_per_testcase2 -> fix cgi"), EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]), {ok, FileInfo} = file:read_file_info(EnvCGI), ok = file:write_file_info(EnvCGI, @@ -427,16 +432,14 @@ init_per_testcase2(Case, Config) -> FileInfo1#file_info{mode = 8#00755}), %% To be used by IP test cases - io:format(user, "~w:init_per_testcase2(~w) -> ip testcase setups~n", - [?MODULE, Case]), + tsp("init_per_testcase2 -> ip testcase setups"), create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], normal_access, IpNormal), create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], mod_htaccess, IpHtaccess), %% To be used by SSL test cases - io:format(user, "~w:init_per_testcase2(~w) -> ssl testcase setups~n", - [?MODULE, Case]), + tsp("init_per_testcase2 -> ssl testcase setups"), SocketType = case atom_to_list(Case) of [X, $s, $s, $l | _] -> @@ -460,8 +463,7 @@ init_per_testcase2(Case, Config) -> %% when you run the whole test suite due to shortcomings %% of the test server. - io:format(user, "~w:init_per_testcase2(~w) -> " - "maybe generate IPv6 config file(s)", [?MODULE, Case]), + tsp("init_per_testcase2 -> maybe generate IPv6 config file(s)"), NewConfig2 = case atom_to_list(Case) of "ipv6_" ++ _ -> @@ -502,15 +504,15 @@ init_per_testcase2(Case, Config) -> NewConfig end, - io:format(user, "~w:init_per_testcase2(~w) -> done~n", - [?MODULE, Case]), + tsp("init_per_testcase2 -> done when" + "~n NewConfig2: ~p", [NewConfig2]), NewConfig2. init_per_testcase3(Case, Config) -> - io:format(user, "~w:init_per_testcase3(~w) -> entry with" - "~n Config: ~p", [?MODULE, Case, Config]), + tsp("init_per_testcase3(~w) -> entry with" + "~n Config: ~p", [Case, Config]), %% %% Create a new fresh node to be used by the server in this test-case @@ -532,12 +534,10 @@ init_per_testcase3(Case, Config) -> %% Set trace level case lists:reverse(atom_to_list(Case)) of "tset_emit" ++ _Rest -> % test-cases ending with time_test - io:format(user, "~w:init_per_testcase3(~w) -> disabling trace", - [?MODULE, Case]), + tsp("init_per_testcase3(~w) -> disabling trace", [Case]), inets:disable_trace(); _ -> - io:format(user, "~w:init_per_testcase3(~w) -> enabling trace", - [?MODULE, Case]), + tsp("init_per_testcase3(~w) -> enabling trace", [Case]), %% TraceLevel = 70, TraceLevel = max, TraceDest = io, @@ -545,8 +545,7 @@ init_per_testcase3(Case, Config) -> end, %% Start initialization - io:format(user, "~w:init_per_testcase3(~w) -> start init", - [?MODULE, Case]), + tsp("init_per_testcase3(~w) -> start init", [Case]), Dog = test_server:timetrap(inets_test_lib:minutes(10)), @@ -627,26 +626,32 @@ init_per_testcase3(Case, Config) -> end end, - case CaseRest of - {skip, _} = Skip -> - Skip; - "mod_auth_" ++ _ -> - start_mnesia(?config(node, Config)), - [{watchdog, Dog} | NewConfig]; - "mod_htaccess" -> - ServerRoot = ?config(server_root, Config), - Path = filename:join([ServerRoot, "htdocs"]), - catch remove_htaccess(Path), - create_htaccess_data(Path, ?config(address, Config)), - [{watchdog, Dog} | NewConfig]; - "range" -> - ServerRoot = ?config(server_root, Config), - Path = filename:join([ServerRoot, "htdocs"]), - create_range_data(Path), - [{watchdog, Dog} | NewConfig]; - _ -> - [{watchdog, Dog} | NewConfig] - end. + InitRes = + case CaseRest of + {skip, _} = Skip -> + Skip; + "mod_auth_" ++ _ -> + start_mnesia(?config(node, Config)), + [{watchdog, Dog} | NewConfig]; + "mod_htaccess" -> + ServerRoot = ?config(server_root, Config), + Path = filename:join([ServerRoot, "htdocs"]), + catch remove_htaccess(Path), + create_htaccess_data(Path, ?config(address, Config)), + [{watchdog, Dog} | NewConfig]; + "range" -> + ServerRoot = ?config(server_root, Config), + Path = filename:join([ServerRoot, "htdocs"]), + create_range_data(Path), + [{watchdog, Dog} | NewConfig]; + _ -> + [{watchdog, Dog} | NewConfig] + end, + + tsp("init_per_testcase3(~w) -> done when" + "~n InitRes: ~p", [Case, InitRes]), + + InitRes. %%-------------------------------------------------------------------- @@ -664,16 +669,14 @@ end_per_testcase(Case, Config) -> ok. end_per_testcase2(Case, Config) -> - io:format(user, "~w:end_per_testcase2(~w) -> entry with" - "~n Config: ~p~n", - [?MODULE, Case, Config]), + tsp("end_per_testcase2(~w) -> entry with" + "~n Config: ~p", [Case, Config]), application:unset_env(inets, services), application:stop(inets), application:stop(ssl), application:stop(crypto), % used by the new ssl (essl test cases) cleanup_mnesia(), - io:format(user, "~w:end_per_testcase2(~w) -> done~n", - [?MODULE, Case]), + tsp("end_per_testcase2(~w) -> done", [Case]), ok. @@ -2354,32 +2357,33 @@ create_config(Config, Access, FileName) -> true -> [] end, - ModOrder = case Access of - mod_htaccess -> - "Modules mod_alias mod_htaccess mod_auth " - "mod_security " - "mod_responsecontrol mod_trace mod_esi " - "mod_actions mod_cgi mod_include mod_dir " - "mod_range mod_get " - "mod_head mod_log mod_disk_log"; - _ -> - "Modules mod_alias mod_auth mod_security " - "mod_responsecontrol mod_trace mod_esi " - "mod_actions mod_cgi mod_include mod_dir " + ModOrder = + case Access of + mod_htaccess -> + "Modules mod_alias mod_htaccess mod_auth " + "mod_security " + "mod_responsecontrol mod_trace mod_esi " + "mod_actions mod_cgi mod_include mod_dir " + "mod_range mod_get " + "mod_head mod_log mod_disk_log"; + _ -> + "Modules mod_alias mod_auth mod_security " + "mod_responsecontrol mod_trace mod_esi " + "mod_actions mod_cgi mod_include mod_dir " "mod_range mod_get " - "mod_head mod_log mod_disk_log" - end, + "mod_head mod_log mod_disk_log" + end, -%% The test suite currently does not handle an explicit BindAddress. -%% They assume any has been used, that is Addr is always set to undefined! + %% The test suite currently does not handle an explicit BindAddress. + %% They assume any has been used, that is Addr is always set to undefined! -%% {ok, Hostname} = inet:gethostname(), -%% {ok, Addr} = inet:getaddr(Hostname, inet6), -%% AddrStr = make_ipv6(Addr), -%% BindAddress = lists:flatten(io_lib:format("~s|inet6", [AddrStr])), + %% {ok, Hostname} = inet:gethostname(), + %% {ok, Addr} = inet:getaddr(Hostname, inet6), + %% AddrStr = make_ipv6(Addr), + %% BindAddress = lists:flatten(io_lib:format("~s|inet6", [AddrStr])), - %% BindAddress = "*|inet", - BindAddress = "*", + BindAddress = "*|inet", + %% BindAddress = "*", HttpConfig = [ cline(["Port ", integer_to_list(Port)]), @@ -2770,10 +2774,10 @@ create_ipv6_config(Config, FileName, Ipv6Address) -> ok = file:close(Fd). -%% tsp(F) -> -%% inets_test_lib:tsp(F). +tsp(F) -> + inets_test_lib:tsp("[~w]" ++ F, [?MODULE]). tsp(F, A) -> - inets_test_lib:tsp(F, A). + inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]). tsf(Reason) -> inets_test_lib:tsf(Reason). diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index 4cd38f2ec4..7a476ea14a 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_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 @@ -20,6 +20,8 @@ -module(httpd_basic_SUITE). -include_lib("common_test/include/ct.hrl"). +-include("inets_test_lib.hrl"). + %% Note: This directive should only be used in test suites. -compile(export_all). @@ -184,6 +186,15 @@ escaped_url_in_error_body(doc) -> escaped_url_in_error_body(suite) -> []; escaped_url_in_error_body(Config) when is_list(Config) -> + %% <CONDITIONAL-SKIP> + %% This skip is due to a problem on windows with long path's + %% If a path is too long file:open fails with, for example, eio. + %% Until that problem is fixed, we skip this case... + Skippable = [win32], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% </CONDITIONAL-SKIP> + tsp("escaped_url_in_error_body -> entry"), HttpdConf = ?config(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]), @@ -192,6 +203,7 @@ escaped_url_in_error_body(Config) when is_list(Config) -> _Address = proplists:get_value(bind_address, Info), %% Request 1 + tss(1000), tsp("escaped_url_in_error_body -> request 1"), URL1 = ?URL_START ++ integer_to_list(Port), %% Make sure the server is ok, by making a request for a valid page @@ -202,11 +214,12 @@ escaped_url_in_error_body(Config) when is_list(Config) -> {ok, {200, _}} -> %% Don't care about the the body, just that we get a ok response ok; - {ok, UnexpectedOK1} -> - tsf({unexpected_ok_1, UnexpectedOK1}) + {ok, {StatusCode1, Body1}} -> + tsf({unexpected_ok_1, StatusCode1, Body1}) end, %% Request 2 + tss(1000), tsp("escaped_url_in_error_body -> request 2"), %% Make sure the server is ok, by making a request for a valid page case httpc:request(get, {URL1 ++ "/dummy.html", []}, @@ -216,11 +229,12 @@ escaped_url_in_error_body(Config) when is_list(Config) -> {ok, {200, _}} -> %% Don't care about the the body, just that we get a ok response ok; - {ok, UnexpectedOK2} -> - tsf({unexpected_ok_2, UnexpectedOK2}) + {ok, {StatusCode2, Body2}} -> + tsf({unexpected_ok_2, StatusCode2, Body2}) end, %% Request 3 + tss(1000), tsp("escaped_url_in_error_body -> request 3"), %% Ask for a non-existing page(1) Path = "/<b>this_is_bold<b>", @@ -238,10 +252,11 @@ escaped_url_in_error_body(Config) when is_list(Config) -> tsf({unexpected_path_3, HTMLEncodedPath, BadPath3}) end; {ok, UnexpectedOK3} -> - tsf({unexpected_ok_1, UnexpectedOK3}) + tsf({unexpected_ok_3, UnexpectedOK3}) end, %% Request 4 + tss(1000), tsp("escaped_url_in_error_body -> request 4"), %% Ask for a non-existing page(2) case httpc:request(get, {URL2, []}, @@ -253,11 +268,12 @@ escaped_url_in_error_body(Config) when is_list(Config) -> HTMLEncodedPath -> ok; BadPath4 -> - tsf({unexpected_path_2, HTMLEncodedPath, BadPath4}) + tsf({unexpected_path_4, HTMLEncodedPath, BadPath4}) end; {ok, UnexpectedOK4} -> tsf({unexpected_ok_4, UnexpectedOK4}) end, + tss(1000), tsp("escaped_url_in_error_body -> stop inets"), inets:stop(httpd, Pid), tsp("escaped_url_in_error_body -> done"), @@ -277,7 +293,12 @@ tsp(F, A) -> inets_test_lib:tsp(F, A). tsf(Reason) -> - test_server:fail(Reason). + inets_test_lib:tsf(Reason). + +tss(Time) -> + inets_test_lib:tss(Time). + + skip(Reason) -> diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index 5016cdb9e6..cb1214b7fb 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.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 @@ -39,14 +39,10 @@ %% Test cases starts here. %%------------------------------------------------------------------------- alias(Type, Port, Host, Node) -> -%% io:format(user, "~w:alias -> entry with" -%% "~n Type: ~p" -%% "~n Port: ~p" -%% "~n Host: ~p" -%% "~n Node: ~p" -%% "~n", [?MODULE, Type, Port, Host, Node]), - - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + %% This is very crude, but... + tsp("alias -> Has IPv6 support: ~p", [inets_test_lib:has_ipv6_support()]), + Opts = [], + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, "GET /pics/icon.sheet.gif " "HTTP/1.0\r\n\r\n", [{statuscode, 200}, @@ -55,7 +51,7 @@ alias(Type, Port, Host, Node) -> {header, "Date"}, {version, "HTTP/1.0"}]), - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, "GET / HTTP/1.0\r\n\r\n", [{statuscode, 200}, {header, "Content-Type","text/html"}, @@ -63,7 +59,7 @@ alias(Type, Port, Host, Node) -> {header, "Date"}, {version, "HTTP/1.0"}]), - ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, "GET /misc/ HTTP/1.0\r\n\r\n", [{statuscode, 200}, {header, "Content-Type","text/html"}, @@ -71,8 +67,8 @@ alias(Type, Port, Host, Node) -> {header, "Date"}, {version, "HTTP/1.0"}]), - %% Check redirection if trailing slash is missing. - ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + %% Check redirection if trailing slash is missing. + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, "GET /misc HTTP/1.0\r\n\r\n", [{statuscode, 301}, {header, "Location"}, @@ -1032,9 +1028,10 @@ check_lists_members1(L1,L2) -> %% tsp(F) -> -%% tsp(F, []). -%% tsp(F, A) -> -%% test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). +%% inets_test_lib:tsp(F). +tsp(F, A) -> + inets_test_lib:tsp(F, A). + tsf(Reason) -> test_server:fail(Reason). diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index 2f5867559a..3e1213376d 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -79,19 +79,19 @@ %%-------------------------------------------------------------------- %% API %%------------------------------------------------------------------ + verify_request(SocketType, Host, Port, Node, RequestStr, Options) -> - verify_request(SocketType, Host, Port, Node, RequestStr, - Options, 30000). + verify_request(SocketType, Host, Port, Node, RequestStr, Options, 30000). + verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options) when is_list(TranspOpts) -> - verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, - Options, 30000); + verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, 30000); + verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) when (is_integer(TimeOut) orelse (TimeOut =:= infinity)) -> - verify_request(SocketType, Host, Port, [], Node, RequestStr, - Options, TimeOut). -verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, - Options, TimeOut) -> + verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut). + +verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut) -> tsp("verify_request -> entry with" "~n SocketType: ~p" "~n Host: ~p" @@ -101,7 +101,7 @@ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, "~n Options: ~p" "~n TimeOut: ~p", [SocketType, Host, Port, TranspOpts, Node, Options, TimeOut]), - case (catch inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts)) of + try inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts) of {ok, Socket} -> tsp("verify_request -> connected - now send message"), SendRes = inets_test_lib:send(SocketType, Socket, RequestStr), @@ -117,25 +117,37 @@ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, case request(State#state{request = RequestStr, socket = Socket}, TimeOut) of {error, Reason} -> - tsp("request failed: " + tsp("verify_request -> request failed: " "~n Reason: ~p", [Reason]), {error, Reason}; NewState -> - tsp("validate reply: " + tsp("verify_request -> validate reply: " "~n NewState: ~p", [NewState]), ValidateResult = validate(RequestStr, NewState, Options, Node, Port), - tsp("validation result: " + tsp("verify_request -> validation result: " "~n ~p", [ValidateResult]), inets_test_lib:close(SocketType, Socket), ValidateResult end; ConnectError -> - tsp("verify_request -> connect failed: " + tsp("verify_request -> connect error: " "~n ~p" "~n", [ConnectError]), - tsf({connect_failure, ConnectError}) + tsf({connect_error, ConnectError, + [SocketType, Host, Port, TranspOpts]}) + catch + T:E -> + tsp("verify_request -> connect failed: " + "~n E: ~p" + "~n T: ~p" + "~n", [E, T]), + tsf({connect_failure, + [{type, T}, + {error, E}, + {stacktrace, erlang:get_stacktrace()}, + {args, [SocketType, Host, Port, TranspOpts]}]}) end. request(#state{mfa = {Module, Function, Args}, diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index bbed35e1f8..c94be796cd 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.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 @@ -31,42 +31,48 @@ 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([tsp/1, tsp/2, tsf/1]). +-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]). --export([oscmd/1, has_ipv6_support/1]). +-export([oscmd/1, has_ipv6_support/0, has_ipv6_support/1, print_system_info/1]). +-export([run_on_os/2, run_on_windows/1]). -export([ensure_started/1]). -export([non_pc_tc_maybe_skip/4, os_based_skip/1, skip/3, fail/3]). -export([flush/0]). -export([start_node/1, stop_node/1]). + %% -- Misc os command and stuff +has_ipv6_support() -> + tsp("has_ipv6_support -> no ipv6_hosts config"), + {ok, Hostname} = inet:gethostname(), + case inet:getaddrs(Hostname, inet6) of + {ok, [Addr|_]} when is_tuple(Addr) andalso + (element(1, Addr) =/= 0) -> + %% We actually need to test that the addr can be used, + %% this is done by attempting to create a (tcp) + %% listen socket + tsp("has_ipv6_support -> check Addr: ~p", [Addr]), + case (catch gen_tcp:listen(0, [inet6, {ip, Addr}])) of + {ok, LSock} -> + tsp("has_ipv6_support -> we are ipv6 host"), + gen_tcp:close(LSock), + {ok, Addr}; + _ -> + undefined + end; + _ -> + undefined + end. + has_ipv6_support(Config) -> case lists:keysearch(ipv6_hosts, 1, Config) of false -> %% Do a basic check to se if %% our own host has a working IPv6 address... - tsp("has_ipv6_support -> no ipv6_hosts config"), - {ok, Hostname} = inet:gethostname(), - case inet:getaddrs(Hostname, inet6) of - {ok, [Addr|_]} when is_tuple(Addr) andalso - (element(1, Addr) =/= 0) -> - %% We actually need to test that the addr can be used, - %% this is done by attempting to create a (tcp) - %% listen socket - tsp("has_ipv6_support -> check Addr: ~p", [Addr]), - case (catch gen_tcp:listen(0, [inet6, {ip, Addr}])) of - {ok, LSock} -> - tsp("has_ipv6_support -> we are ipv6 host"), - gen_tcp:close(LSock), - {ok, Addr}; - _ -> - undefined - end; - _ -> - undefined - end; + has_ipv6_support(); + {value, {_, Hosts}} when is_list(Hosts) -> %% Check if our host is in the list of *known* IPv6 hosts tsp("has_ipv6_support -> Hosts: ~p", [Hosts]), @@ -88,6 +94,49 @@ has_ipv6_support(Config) -> oscmd(Cmd) -> string:strip(os:cmd(Cmd), right, $\n). + +print_system_info([]) -> + do_print_system_info("System Info"); +print_system_info(Prefix) when is_list(Prefix) -> + NewPrefix = lists:flatten(io_lib:format("~s: System Info", [Prefix])), + do_print_system_info(NewPrefix). + +do_print_system_info(Prefix) -> + tsp("~s => " + "~n" + "~n OS Type: ~p" + "~n OS version: ~p" + "~n Sys Arch: ~p" + "~n CPU Topology: ~p" + "~n Num logical procs: ~p" + "~n SMP support: ~p" + "~n Num schedulers: ~p" + "~n Scheduler bindings: ~p" + "~n Wordsize: ~p" + "~n~n", [Prefix, + os:type(), os:version(), + erlang:system_info(system_architecture), + erlang:system_info(cpu_topology), + erlang:system_info(logical_processors), + erlang:system_info(smp_support), + erlang:system_info(schedulers), + erlang:system_info(scheduler_bindings), + erlang:system_info(wordsize)]), + ok. + + +run_on_windows(Fun) -> + run_on_os(windows, Fun). + +run_on_os(windows, Fun) -> + case os:type() of + {win32, _} -> + Fun(); + _ -> + ok + end. + + %% -- Misc node operation wrapper functions -- start_node(Name) -> @@ -156,6 +205,17 @@ do_ensure_started(App, Start) when is_function(Start) -> end. +ensure_loaded(App) -> + case application:load(App) of + ok -> + ok; + {error, {already_loaded,inets}} -> + ok; + Error -> + Error + end. + + %% ---------------------------------------------------------------- %% HTTPD starter functions @@ -165,8 +225,9 @@ start_http_server(Conf) -> start_http_server(Conf, ?HTTP_DEFAULT_SSL_KIND). start_http_server(Conf, essl = _SslTag) -> - tsp("start_http_server(essl) -> entry - try start crypto and public_key"), + tsp("start_http_server(essl) -> try start crypto"), application:start(crypto), + tsp("start_http_server(essl) -> try start public_key"), application:start(public_key), do_start_http_server(Conf); start_http_server(Conf, SslTag) -> @@ -177,23 +238,32 @@ do_start_http_server(Conf) -> tsp("do_start_http_server -> entry with" "~n Conf: ~p" "~n", [Conf]), - application:load(inets), - case application:set_env(inets, services, [{httpd, Conf}]) of + tsp("do_start_http_server -> load inets"), + case ensure_loaded(inets) of ok -> - tsp("start_http_server -> httpd conf stored in inets app env"), - case application:start(inets) of + tsp("do_start_http_server -> inets loaded - now set_env for httpd"), + case application:set_env(inets, services, [{httpd, Conf}]) of ok -> - tsp("start_http_server -> inets started"), - ok; - Error1 -> - tsp("<ERROR> Failed starting application: " - "~n Error1: ~p", [Error1]), - Error1 + tsp("do_start_http_server -> " + "httpd conf stored in inets app env"), + case (catch application:start(inets)) of + ok -> + tsp("do_start_http_server -> inets started"), + ok; + Error1 -> + tsp("<ERROR> Failed starting application: " + "~n Error1: ~p", [Error1]), + tsf({failed_starting_inets, Error1}) + end; + Error2 -> + tsp("<ERROR> Failed set application env: " + "~n Error: ~p", [Error2]), + tsf({failed_set_env, Error2}) end; - Error2 -> - tsp("<ERROR> Failed set application env: " - "~n Error: ~p", [Error2]), - Error2 + {error, Reason} -> + tsp("do_start_http_server -> failed loading inets" + "~n Reason: ~p", [Reason]), + tsf({failed_loading_inets, Reason}) end. start_http_server_ssl(FileName) -> @@ -213,6 +283,7 @@ do_start_http_server_ssl(FileName) -> catch do_start_http_server(FileName). + %% ---------------------------------------------------------------------- %% print functions %% @@ -257,20 +328,49 @@ copy_files(FromDir, ToDir) -> copy_dirs(FromDirRoot, ToDirRoot) -> - {ok, Files} = file:list_dir(FromDirRoot), - lists:foreach( - fun(FileOrDir) -> - %% Check if it's a directory or a file - case filelib:is_dir(filename:join(FromDirRoot, FileOrDir)) of - true -> - FromDir = filename:join([FromDirRoot, FileOrDir]), - ToDir = filename:join([ToDirRoot, FileOrDir]), - ok = file:make_dir(ToDir), - copy_dirs(FromDir, ToDir); - false -> - copy_file(FileOrDir, FromDirRoot, ToDirRoot) - end - end, Files). + case file:list_dir(FromDirRoot) of + {ok, Files} -> + lists:foreach( + fun(FileOrDir) -> + %% Check if it's a directory or a file + case filelib:is_dir(filename:join(FromDirRoot, + FileOrDir)) of + true -> + FromDir = filename:join([FromDirRoot, FileOrDir]), + ToDir = filename:join([ToDirRoot, FileOrDir]), + case file:make_dir(ToDir) of + ok -> + copy_dirs(FromDir, ToDir); + {error, Reason} -> + tsp("<ERROR> Failed creating directory: " + "~n ToDir: ~p" + "~n Reason: ~p" + "~nwhen" + "~n ToDirRoot: ~p" + "~n ToDirRoot file info: ~p", + [ToDir, + Reason, + ToDirRoot, + file:read_file_info(ToDirRoot)]), + tsf({failed_copy_dir, ToDir, Reason}) + end; + false -> + copy_file(FileOrDir, FromDirRoot, ToDirRoot) + end + end, Files); + {error, Reason} -> + tsp("<ERROR> Failed get directory file list: " + "~n FromDirRoot: ~p" + "~n Reason: ~p" + "~nwhen" + "~n FromDirRoot file info: ~p", + [FromDirRoot, + Reason, + file:read_file_info(FromDirRoot)]), + tsf({failed_list_dir, FromDirRoot, Reason}) + end. + + del_dirs(Dir) -> case file:list_dir(Dir) of @@ -394,56 +494,80 @@ connect_byte(ip_comm, Host, Port, Opts0) -> connect(ip_comm, Host, Port, Opts). -connect(ssl, Host, Port, Opts) -> +%% This always falls back on IPV4, but tries IPV6 first. +connect(Proto, Host, Port, Opts0) -> + Opts = Opts0 -- [inet, inet6], + connect(Proto, Host, Port, Opts ++ [inet6], inet6). + +connect(ssl, Host, Port, Opts, Type) -> tsp("connect(ssl) -> entry with" "~n Host: ~p" "~n Port: ~p" - "~n Opts: ~p", [Host, Port, Opts]), + "~n Opts: ~p" + "~n Type: ~p", [Host, Port, Opts, Type]), ssl:start(), - %% Does not support ipv6 in old ssl + %% We ignore this option for ssl... + %% ...maybe we should really treat this in the same way as ip_comm... case ssl:connect(Host, Port, Opts) of {ok, Socket} -> {ok, Socket}; + {error, Reason} when Type =:= inet6 -> + tsp("connect(ssl) -> failed connecting with inet6: " + "~n Reason: ~p" + "~n trying inet", [Reason]), + connect(ssl, Host, Port, Opts -- [inet6], inet); {error, Reason} -> - {error, Reason}; + tsp("connect(ssl) -> failed connecting: " + "~n Reason: ~p", [Reason]), + {error, Reason}; Error -> Error end; -connect(ip_comm, Host, Port, Opts) -> +connect(ip_comm, Host, Port, Opts, Type) -> tsp("connect(ip_comm) -> entry with" "~n Host: ~p" "~n Port: ~p" - "~n Opts: ~p", [Host, Port, Opts]), - case gen_tcp:connect(Host,Port, Opts) of + "~n Opts: ~p" + "~n Type: ~p", [Host, Port, Opts, Type]), + + case gen_tcp:connect(Host, Port, Opts) of {ok, Socket} -> tsp("connect success"), {ok, Socket}; - {error, nxdomain} -> - tsp("connect error nxdomain when opts: ~p", [Opts]), - connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); - {error, eafnosupport} -> - tsp("connect error eafnosupport when opts: ~p", [Opts]), - connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); - {error, econnreset} -> - tsp("connect error econnreset when opts: ~p", [Opts]), - connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); - {error, enetunreach} -> - tsp("connect error eafnosupport when opts: ~p", [Opts]), - connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); - {error, {enfile,_}} -> - tsp("connect error enfile when opts: ~p", [Opts]), - {error, enfile}; + + {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]), + connect(ip_comm, Host, Port, Opts -- [inet6], inet); + Error -> - tsp("Unexpected error: " + tsp("connect(ip_conn) -> Fatal connect error: " "~n Error: ~p" "~nwhen" "~n Host: ~p" "~n Port: ~p" "~n Opts: ~p" - "~n", [Error, Host, Port, Opts]), + "~n Type: ~p" + "~n", [Error, Host, Port, Opts, Type]), Error end. - + send(ssl, Socket, Data) -> ssl:send(Socket, Data); @@ -509,12 +633,15 @@ tsp(F) -> tsp(F, []). tsp(F, A) -> Timestamp = formated_timestamp(), - test_server:format("*** ~s ~p ~p ~w:" ++ F ++ "~n", - [Timestamp, node(), self(), ?MODULE | A]). + test_server:format("*** ~s ~p ~p " ++ F ++ "~n", + [Timestamp, node(), self() | A]). tsf(Reason) -> test_server:fail(Reason). +tss(Time) -> + test_server:sleep(Time). + formated_timestamp() -> format_timestamp( os:timestamp() ). diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl index c578398c55..6a86b1b764 100644 --- a/lib/inets/test/inets_test_lib.hrl +++ b/lib/inets/test/inets_test_lib.hrl @@ -50,6 +50,11 @@ -define(OSCMD(Cmd), inets_test_lib:oscmd(Cmd)). +-define(PRINT_SYSTEM_INFO(P), inets_test_lib:print_system_info(P)). + +-define(RUN_ON_OS(OS, FUN), inets_test_lib:run_on_os(OS, FUN)). +-define(RUN_ON_WINDOWS(FUN), inets_test_lib:run_on_windows(FUN)). + %% - Test case macros - diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 77eb43a7ed..488947c3a1 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.8.1 +INETS_VSN = 5.9 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index fb9415d440..f5f972c112 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.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 @@ -1034,10 +1034,9 @@ sync_loop(From, S) -> -define(MAX_LOOK_AHEAD, 64*1024). %% Inlined. -log_loop(S, Pids, _Bins, _Sync, _Sz) when S#state.cache_error =/= ok -> +log_loop(#state{cache_error = CE}=S, Pids, _Bins, _Sync, _Sz) when CE =/= ok -> loop(cache_error(S, Pids)); -log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz) - when Sz > ?MAX_LOOK_AHEAD -> +log_loop(#state{}=S, Pids, Bins, Sync, Sz) when Sz > ?MAX_LOOK_AHEAD -> loop(log_end(S, Pids, Bins, Sync)); log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz) -> receive @@ -1046,8 +1045,7 @@ log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz) -> after 0 -> loop(log_end(S, Pids, Bins, Sync)) end; -log_loop(S, Pids, Bins, Sync, Sz) -> - [M | Ms] = S#state.messages, +log_loop(#state{messages = [M | Ms]}=S, Pids, Bins, Sync, Sz) -> S1 = S#state{messages = Ms}, log_loop(M, Pids, Bins, Sync, Sz, S1, get(log)). diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 4028dd4f0b..e2290f6a23 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -1416,7 +1416,11 @@ mode_list(_) -> %% Functions for communicating with the file server call(Command, Args) when is_list(Args) -> - gen_server:call(?FILE_SERVER, list_to_tuple([Command | Args]), infinity). + X = erlang:dt_spread_tag(true), + Y = gen_server:call(?FILE_SERVER, list_to_tuple([Command | Args]), + infinity), + erlang:dt_restore_tag(X), + Y. check_and_call(Command, Args) when is_list(Args) -> case check_args(Args) of diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl index 14da9c1a55..7311ad9fee 100644 --- a/lib/kernel/src/file_io_server.erl +++ b/lib/kernel/src/file_io_server.erl @@ -57,9 +57,11 @@ start_link(Owner, FileName, ModeList) do_start(Spawn, Owner, FileName, ModeList) -> Self = self(), Ref = make_ref(), + Utag = erlang:dt_spread_tag(true), Pid = erlang:Spawn( fun() -> + erlang:dt_restore_tag(Utag), %% process_flag(trap_exit, true), case parse_options(ModeList) of {ReadMode, UnicodeMode, Opts} -> @@ -84,6 +86,7 @@ do_start(Spawn, Owner, FileName, ModeList) -> exit(Reason1) end end), + erlang:dt_restore_tag(Utag), Mref = erlang:monitor(process, Pid), receive {Ref, {error, _Reason} = Error} -> diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 99b0cd2ffb..4dfa92a755 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1441,6 +1441,9 @@ filter_app("netconf",_) -> % Safe has the same kind of error in the .app file as ic filter_app("safe",_) -> false; +% Comte cannot be started in the "usual" way +filter_app("comte",_) -> + false; % OS_mon does not find it's port program when running cerl filter_app("os_mon",true) -> false; diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 0de9785fb9..fa867e12f6 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -278,7 +278,7 @@ collect_data(?IO_W, {N, Q}) -> end. mem_types() -> - [total, processes, system, atom, binary, code, ets]. + [total, processes, atom, binary, code, ets]. lmax([]) -> 0; lmax(List) -> diff --git a/lib/public_key/src/public_key.appup.src b/lib/public_key/src/public_key.appup.src index 2945fb1213..aacd3b866d 100644 --- a/lib/public_key/src/public_key.appup.src +++ b/lib/public_key/src/public_key.appup.src @@ -1,16 +1,8 @@ %% -*- erlang -*- {"%VSN%", [ - {"0.13", [{restart_application, public_key}]}, - {"0.11", [{restart_application, public_key}]}, - {"0.10", [{restart_application, public_key}]}, - {"0.9", [{restart_application, public_key}]}, - {"0.8", [{restart_application, public_key}]} + {<<"0\\.*">>, [{restart_application, public_key}]} ], [ - {"0.13", [{restart_application, public_key}]}, - {"0.11", [{restart_application, public_key}]}, - {"0.10", [{restart_application, public_key}]}, - {"0.9", [{restart_application, public_key}]}, - {"0.8", [{restart_application, public_key}]} + {<<"0\\.*">>, [{restart_application, public_key}]} ]}. diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk index d8f811bf25..ab4ee8b0ff 100644 --- a/lib/public_key/vsn.mk +++ b/lib/public_key/vsn.mk @@ -1 +1 @@ -PUBLIC_KEY_VSN = 0.14 +PUBLIC_KEY_VSN = 0.15 diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index 60e886e8f5..696ab52e63 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2009</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -433,7 +433,7 @@ sys() = {root_dir, root_dir()} | {excl_archive_filters, excl_archive_filters()} | {archive_opts, [archive_opt()]} app() = {vsn, app_vsn()} - | {mod, mod_name(), mod()} + | {mod, mod_name(), [mod()]} | {mod_cond, mod_cond()} | {incl_cond, incl_cond()} | {debug_info, debug_info()} @@ -445,8 +445,7 @@ app() = {vsn, app_vsn()} | {incl_archive_filters, incl_archive_filters()} | {excl_archive_filters, excl_archive_filters()} | {archive_opts, [archive_opt()]} -mod() = {vsn, app_vsn()} - | {incl_cond, incl_cond()} +mod() = {incl_cond, incl_cond()} | {debug_info, debug_info()} rel_app() = app_name() | {app_name(), app_type()} @@ -664,10 +663,10 @@ target_spec() = [target_spec()] </func> <func> - <name>install(Server, TargetDir) -> ok | {error, Reason}</name> + <name>install(RelName, TargetDir) -> ok | {error, Reason}</name> <fsummary>Install a target system</fsummary> <type> - <v>Server = server()</v> + <v>RelName = rel_name()</v> <v>TargetDir = target_dir()</v> <v>Reason = reason()</v> </type> diff --git a/lib/reltool/src/reltool.erl b/lib/reltool/src/reltool.erl index 54eb1ca9e1..2bdf222aa0 100644 --- a/lib/reltool/src/reltool.erl +++ b/lib/reltool/src/reltool.erl @@ -1,7 +1,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 @@ -59,7 +59,7 @@ start_link(Options) when is_list(Options) -> {ok, _WinPid} = OK -> OK; {error, Reason} -> - {error, lists:flatten(io_lib:format("~p", [Reason]))} + {error, Reason} end. %% Start server process with options @@ -69,7 +69,7 @@ start_server(Options) -> {ok, ServerPid, _Common, _Sys} -> {ok, ServerPid}; {error, Reason} -> - {error, lists:flatten(io_lib:format("~p", [Reason]))} + {error, Reason} end. %% Start server process with options diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 93f47f6381..4e5c5b2849 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -1,7 +1,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 @@ -45,6 +45,7 @@ -type profile() :: development | embedded | standalone. -type relocatable() :: boolean(). -type escript_file() :: file(). +-type escript_app_name() :: app_name(). -type mod_name() :: atom(). -type app_name() :: atom(). -type app_vsn() :: string(). % e.g. "4.7" @@ -62,7 +63,7 @@ -type mod() :: {incl_cond, incl_cond()} | {debug_info, debug_info()}. -type app() :: {vsn, app_vsn()} - | {mod, mod_name(), mod()} + | {mod, mod_name(), [mod()]} | {mod_cond, mod_cond()} | {incl_cond, incl_cond()} | {app_file, app_file()} @@ -126,10 +127,7 @@ { sys_debug :: term(), wx_debug :: term(), - trap_exit :: boolean(), - app_tab :: ets:tab(), - mod_tab :: ets:tab(), - mod_used_by_tab :: ets:tab() + trap_exit :: boolean() }). -record(mod, @@ -170,7 +168,7 @@ -record(app, { %% Static info name :: app_name(), - is_escript :: boolean(), + is_escript :: boolean() | {inlined, escript_app_name()}, use_selected_vsn :: boolean() | undefined, active_dir :: dir(), sorted_dirs :: [dir()], @@ -199,8 +197,8 @@ used_by_mods :: [mod_name()], uses_apps :: [app_name()], used_by_apps :: [app_name()], - is_pre_included :: boolean(), - is_included :: boolean(), + is_pre_included :: boolean() | undefined, + is_included :: boolean() | undefined, rels :: [rel_name()] }). @@ -208,7 +206,7 @@ { name :: app_name(), app_type :: app_type() | undefined, - incl_apps = [] :: [incl_app()] + incl_apps :: [incl_app()] | undefined }). -record(rel, diff --git a/lib/reltool/src/reltool_app_win.erl b/lib/reltool/src/reltool_app_win.erl index 70bd72b258..e0acfab7aa 100644 --- a/lib/reltool/src/reltool_app_win.erl +++ b/lib/reltool/src/reltool_app_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -271,8 +271,8 @@ create_apps_list_ctrl(Panel, Sizer, Text) -> ListItem = wxListItem:new(), wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT), wxListItem:setText(ListItem, Text), + wxListItem:setWidth(ListItem, reltool_utils:get_column_width(ListCtrl)), wxListCtrl:insertColumn(ListCtrl, ?APPS_APP_COL, ListItem), - %% wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, ?APPS_APP_COL_WIDTH), wxListItem:destroy(ListItem), wxSizer:add(Sizer, ListCtrl, @@ -292,7 +292,7 @@ create_deps_page(S, Derived) -> UsedByCtrl = create_mods_list_ctrl(Panel, Main, - "Modules used by others", + "Modules using this", " and their applications", undefined, undefined), diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl index e1c2fa5100..899423bb6d 100644 --- a/lib/reltool/src/reltool_mod_win.erl +++ b/lib/reltool/src/reltool_mod_win.erl @@ -1,7 +1,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 @@ -215,7 +215,7 @@ create_deps_page(S) -> UsedByCtrl = create_mods_list_ctrl(Panel, Main, - "Modules used by others", + "Modules using this", " and their applications"), wxSizer:add(Main, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), @@ -314,8 +314,8 @@ do_create_code_page(#state{xref_pid = Xref, mod = M} = S, PageName) -> {ok, App} = reltool_server:get_app(Xref, M#mod.app_name), ErlBin = case App#app.is_escript of - true -> find_escript_bin(App, M); - false -> find_regular_bin(App, M) + false -> find_regular_bin(App, M); + _ -> find_escript_bin(App, M) end, load_code(Editor, ErlBin), diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 692baea0a4..c99180a613 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -1,7 +1,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 @@ -51,7 +51,12 @@ sys, old_sys, status, - old_status}). + old_status, + app_tab, + old_app_tab, + mod_tab, + old_mod_tab, + mod_used_by_tab}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Client @@ -123,174 +128,165 @@ gen_spec(Pid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Server -init(Options) -> +init([{parent,Parent}|_] = Options) -> try do_init(Options) catch + throw:{error,Reason} -> + proc_lib:init_ack(Parent,{error,Reason}); error:Reason -> exit({Reason, erlang:get_stacktrace()}) end. do_init(Options) -> - {S, Status} = parse_options(Options), - #state{parent_pid = ParentPid, common = C, sys = Sys} = S, - - %% process_flag(trap_exit, (S#state.common)#common.trap_exit), - proc_lib:init_ack(ParentPid, - {ok, self(), C, Sys#sys{apps = undefined}}), - {S2, Status2} = refresh(S, true, Status), - {S3, Status3} = - analyse(S2#state{old_sys = S2#state.sys}, Status2), - case Status3 of - {ok, _Warnings} -> % BUGBUG: handle warnings - loop(S3#state{status = Status3, old_status = {ok, []}}); - {error, Reason} -> - exit(Reason) - end. - -parse_options(Opts) -> - AppTab = ets:new(reltool_apps, [public, ordered_set, {keypos, #app.name}]), - ModTab = ets:new(reltool_mods, [public, ordered_set, {keypos, #mod.name}]), + AppTab = ets:new(reltool_apps1, [public, ordered_set, {keypos,#app.name}]), + OldAppTab = ets:new(reltool_apps2, [public, ordered_set, {keypos,#app.name}]), + ModTab = ets:new(reltool_mods1, [public, ordered_set, {keypos,#mod.name}]), + OldModTab = ets:new(reltool_mods2, [public, ordered_set, {keypos,#mod.name}]), ModUsesTab = ets:new(reltool_mod_uses, [public, bag, {keypos, 1}]), - Sys = #sys{root_dir = reltool_utils:root_dir(), - lib_dirs = reltool_utils:erl_libs(), - escripts = [], - incl_cond = ?DEFAULT_INCL_COND, - mod_cond = ?DEFAULT_MOD_COND, - apps = ?DEFAULT_APPS, - boot_rel = ?DEFAULT_REL_NAME, - rels = reltool_utils:default_rels(), - emu_name = ?DEFAULT_EMU_NAME, - profile = ?DEFAULT_PROFILE, - incl_sys_filters = dec_re(incl_sys_filters, - ?DEFAULT_INCL_SYS_FILTERS, - []), - excl_sys_filters = dec_re(excl_sys_filters, - ?DEFAULT_EXCL_SYS_FILTERS, - []), - incl_app_filters = dec_re(incl_app_filters, - ?DEFAULT_INCL_APP_FILTERS, - []), - excl_app_filters = dec_re(excl_app_filters, - ?DEFAULT_EXCL_APP_FILTERS, - []), - relocatable = ?DEFAULT_RELOCATABLE, - rel_app_type = ?DEFAULT_REL_APP_TYPE, - embedded_app_type = ?DEFAULT_EMBEDDED_APP_TYPE, - app_file = ?DEFAULT_APP_FILE, - incl_archive_filters = dec_re(incl_archive_filters, - ?DEFAULT_INCL_ARCHIVE_FILTERS, - []), - excl_archive_filters = dec_re(excl_archive_filters, - ?DEFAULT_EXCL_ARCHIVE_FILTERS, - []), - archive_opts = ?DEFAULT_ARCHIVE_OPTS, - debug_info = ?DEFAULT_DEBUG_INFO}, - C2 = #common{sys_debug = [], - wx_debug = 0, - trap_exit = true, - app_tab = AppTab, - mod_tab = ModTab, - mod_used_by_tab = ModUsesTab}, - S = #state{options = Opts}, - parse_options(Opts, S, C2, Sys, {ok, []}). + S = #state{options = Options, + app_tab = AppTab, + old_app_tab = OldAppTab, + mod_tab = ModTab, + old_mod_tab = OldModTab, + mod_used_by_tab = ModUsesTab}, + + S2 = parse_options(S), + {S3, Apps, Status2} = refresh(S2), + Status3 = analyse(S3, Apps, Status2), + %% Set old_xxx equal to xxx to allow undo=nop + FakeBackup = {ets:tab2list(S3#state.app_tab),ets:tab2list(S3#state.mod_tab)}, + S4 = save_old(S3, S3, FakeBackup, Status3), + #state{parent_pid = Parent, sys=Sys, common=C} = S4, + proc_lib:init_ack(Parent, {ok, self(), C, Sys#sys{apps=undefined}}), + loop(S4). + +parse_options(S) -> + Sys = default_sys(), + C = #common{sys_debug = [], + wx_debug = 0, + trap_exit = true}, + parse_options(S#state.options, S, C, Sys). + +default_sys() -> + #sys{root_dir = reltool_utils:root_dir(), + lib_dirs = reltool_utils:erl_libs(), + escripts = [], + incl_cond = ?DEFAULT_INCL_COND, + mod_cond = ?DEFAULT_MOD_COND, + apps = ?DEFAULT_APPS, + boot_rel = ?DEFAULT_REL_NAME, + rels = reltool_utils:default_rels(), + emu_name = ?DEFAULT_EMU_NAME, + profile = ?DEFAULT_PROFILE, + incl_sys_filters = dec_re(incl_sys_filters, + ?DEFAULT_INCL_SYS_FILTERS, + []), + excl_sys_filters = dec_re(excl_sys_filters, + ?DEFAULT_EXCL_SYS_FILTERS, + []), + incl_app_filters = dec_re(incl_app_filters, + ?DEFAULT_INCL_APP_FILTERS, + []), + excl_app_filters = dec_re(excl_app_filters, + ?DEFAULT_EXCL_APP_FILTERS, + []), + relocatable = ?DEFAULT_RELOCATABLE, + rel_app_type = ?DEFAULT_REL_APP_TYPE, + embedded_app_type = ?DEFAULT_EMBEDDED_APP_TYPE, + app_file = ?DEFAULT_APP_FILE, + incl_archive_filters = dec_re(incl_archive_filters, + ?DEFAULT_INCL_ARCHIVE_FILTERS, + []), + excl_archive_filters = dec_re(excl_archive_filters, + ?DEFAULT_EXCL_ARCHIVE_FILTERS, + []), + archive_opts = ?DEFAULT_ARCHIVE_OPTS, + debug_info = ?DEFAULT_DEBUG_INFO}. dec_re(Key, Regexps, Old) -> reltool_utils:decode_regexps(Key, Regexps, Old). -parse_options([{Key, Val} | KeyVals], S, C, Sys, Status) -> +parse_options([{Key, Val} | KeyVals], S, C, Sys) -> case Key of parent -> - parse_options(KeyVals, S#state{parent_pid = Val}, C, Sys, Status); + parse_options(KeyVals, S#state{parent_pid = Val}, C, Sys); sys_debug -> - parse_options(KeyVals, S, C#common{sys_debug = Val}, Sys, Status); + parse_options(KeyVals, S, C#common{sys_debug = Val}, Sys); wx_debug -> - parse_options(KeyVals, S, C#common{wx_debug = Val}, Sys, Status); + parse_options(KeyVals, S, C#common{wx_debug = Val}, Sys); trap_exit -> - parse_options(KeyVals, S, C#common{trap_exit = Val}, Sys, Status); + parse_options(KeyVals, S, C#common{trap_exit = Val}, Sys); config -> - {Sys2, Status2} = read_config(Sys, Val, Status), - parse_options(KeyVals, S, C, Sys2, Status2); + Sys2 = read_config(Sys, Val), + parse_options(KeyVals, S, C, Sys2); sys -> - {Sys2, Status2} = read_config(Sys, {sys, Val}, Status), - parse_options(KeyVals, S, C, Sys2, Status2); + Sys2 = read_config(Sys, {sys, Val}), + parse_options(KeyVals, S, C, Sys2); _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - Status2 = - reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text), - parse_options(KeyVals, S, C, Sys, Status2) + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end; -parse_options([], S, C, Sys, Status) -> - {S#state{common = C, sys = Sys}, Status}; -parse_options(KeyVals, S, C, Sys, Status) -> - Text = lists:flatten(io_lib:format("~p", [KeyVals])), - Status2 = reltool_utils:return_first_error(Status, - "Illegal options: " ++ Text), - {S#state{common = C, sys = Sys}, Status2}. - -loop(#state{common = C, sys = Sys} = S) -> +parse_options([], S, C, Sys) -> + S#state{common = C, sys = Sys}; +parse_options(KeyVals, _S, _C, _Sys) -> + reltool_utils:throw_error("Illegal option: ~p", [KeyVals]). + +loop(#state{sys = Sys} = S) -> receive {system, From, Msg} -> sys:handle_system_msg(Msg, From, S#state.parent_pid, ?MODULE, - C#common.sys_debug, + (S#state.common)#common.sys_debug, S); {call, ReplyTo, Ref, {get_config, InclDef, InclDeriv}} -> Reply = do_get_config(S, InclDef, InclDeriv), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {load_config, SysConfig}} -> - {S2, Reply} = do_load_config(S, SysConfig), - reltool_utils:reply(ReplyTo, Ref, Reply), - ?MODULE:loop(S2); + Fun = fun() -> do_load_config(S, SysConfig) end, + {S3, Status2} = config_and_refresh(S, Fun), + reltool_utils:reply(ReplyTo, Ref, Status2), + ?MODULE:loop(S3); {call, ReplyTo, Ref, {save_config, Filename, InclDef, InclDeriv}} -> Reply = do_save_config(S, Filename, InclDef, InclDeriv), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, reset_config} -> - {S2, Status} = parse_options(S#state.options), - S3 = shrink_sys(S2), - {S4, Status2} = refresh(S3, true, Status), - {S5, Status3} = analyse(S4#state{old_sys = S4#state.sys}, Status2), - S6 = - case Status3 of - {ok, _Warnings} -> - S5#state{status = Status3, old_status = S#state.status}; - {error, _} -> - %% Keep old state - S - end, - reltool_utils:reply(ReplyTo, Ref, Status3), - ?MODULE:loop(S6); + Fun = fun() -> parse_options(S) end, + {S3, Status2} = config_and_refresh(S, Fun), + reltool_utils:reply(ReplyTo, Ref, Status2), + ?MODULE:loop(S3); {call, ReplyTo, Ref, undo_config} -> - reltool_utils:reply(ReplyTo, Ref, ok), S2 = S#state{sys = S#state.old_sys, - old_sys = S#state.sys, - status = S#state.old_status, - old_status = S#state.status}, + old_sys = Sys, + status = S#state.old_status, + old_status = S#state.status, + app_tab = S#state.old_app_tab, + old_app_tab = S#state.app_tab, + mod_tab = S#state.old_mod_tab, + old_mod_tab = S#state.mod_tab}, + reltool_utils:reply(ReplyTo, Ref, ok), ?MODULE:loop(S2); {call, ReplyTo, Ref, {get_rel, RelName}} -> - Sys = S#state.sys, Reply = case lists:keysearch(RelName, #rel.name, Sys#sys.rels) of {value, Rel} -> - reltool_target:gen_rel(Rel, Sys); + reltool_target:gen_rel(Rel, sys_all_apps(S)); false -> {error, "No such release: " ++ RelName} end, reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {get_script, RelName}} -> - Sys = S#state.sys, Reply = case lists:keysearch(RelName, #rel.name, Sys#sys.rels) of {value, Rel} -> PathFlag = true, Vars = [], - reltool_target:gen_script(Rel, Sys, PathFlag, Vars); + reltool_target:gen_script(Rel, sys_all_apps(S), + PathFlag, Vars); false -> {error, "No such release: " ++ RelName} end, @@ -298,7 +294,7 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, {get_mod, ModName}} -> Reply = - case ets:lookup(C#common.mod_tab, ModName) of + case ets:lookup(S#state.mod_tab, ModName) of [M] -> {ok, M}; [] -> @@ -308,92 +304,83 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, {get_app, AppName}} when is_atom(AppName) -> Reply = - case lists:keysearch(AppName, #app.name, Sys#sys.apps) of - {value, App} -> + case ets:lookup(S#state.app_tab,AppName) of + [App] -> {ok, App}; - false -> + [] -> {error, "No such application: " ++ atom_to_list(AppName)} end, reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_app, App}} -> - {S2, Status} = do_set_app(S, App, {ok, []}), - {S3, Status2} = analyse(S2, Status), - case Status2 of - {ok, Warnings} -> - App2 = ?KEYSEARCH(App#app.name, - #app.name, - (S3#state.sys)#sys.apps), - reltool_utils:reply(ReplyTo, Ref, {ok, App2, Warnings}), - ?MODULE:loop(S3); - {error, Reason} -> - %% Keep old state - reltool_utils:reply(ReplyTo, Ref, {error, Reason}), - ?MODULE:loop(S) - end; + Fun = fun() -> do_set_apps(S, [App]) end, + {S3, Status2} = config_and_refresh(S, Fun), + Reply = + case Status2 of + {ok, Warnings} -> + [App2] = ets:lookup(S3#state.app_tab,App#app.name), + {ok, App2, Warnings}; + {error, _} -> + Status2 + end, + reltool_utils:reply(ReplyTo, Ref, Reply), + ?MODULE:loop(S3); {call, ReplyTo, Ref, {get_apps, Kind}} -> AppNames = case Kind of whitelist -> - [A || - A <- Sys#sys.apps, - A#app.is_pre_included =:= true]; - blacklist -> - [A || - A <- Sys#sys.apps, - A#app.is_pre_included =:= false]; - source -> - [A || - A <- Sys#sys.apps, - A#app.is_included =/= true, - A#app.is_pre_included =/= false]; + %% Pre-included + ets:select(S#state.app_tab, + [{#app{is_pre_included=true,_='_'}, + [], + ['$_']}]); + blacklist -> + %% Pre-excluded + ets:select(S#state.app_tab, + [{#app{is_pre_included=false,_='_'}, + [], + ['$_']}]); + source -> + %% Not included and not pre-excluded + ets:select(S#state.app_tab, + [{#app{is_included='$1', + is_pre_included='$2', + _='_'}, + [{'=/=','$1',true}, + {'=/=','$2',false}], + ['$_']}]); derived -> - [A || - A <- Sys#sys.apps, - A#app.is_included =:= true, - A#app.is_pre_included =/= true] + %% Included, but not pre-included + ets:select(S#state.app_tab, + [{#app{is_included='$1', + is_pre_included='$2', + _='_'}, + [{'=:=','$1',true}, + {'=/=','$2',true}], + ['$_']}]) end, reltool_utils:reply(ReplyTo, Ref, {ok, AppNames}), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_apps, Apps}} -> - {S2, Status} = - lists:foldl(fun(A, {X, Y}) -> do_set_app(X, A, Y) end, - {S, {ok, []}}, - Apps), - {S3, Status2} = analyse(S2, Status), + Fun = fun() -> do_set_apps(S, Apps) end, + {S3, Status2} = config_and_refresh(S, Fun), reltool_utils:reply(ReplyTo, Ref, Status2), - ?MODULE:loop(S3); + ?MODULE:loop(S3); {call, ReplyTo, Ref, get_sys} -> reltool_utils:reply(ReplyTo, Ref, {ok, Sys#sys{apps = undefined}}), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_sys, Sys2}} -> - S2 = S#state{sys = Sys2#sys{apps = Sys#sys.apps}}, - Force = - (Sys2#sys.root_dir =/= Sys#sys.root_dir) orelse - (Sys2#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse - (Sys2#sys.escripts =/= Sys#sys.escripts), - {S3, Status} = refresh(S2, Force, {ok, []}), - {S4, Status2} = - analyse(S3#state{old_sys = S#state.sys}, Status), - {S5, Status3} = - case Status2 of - {ok, _Warnings} -> % BUGBUG: handle warnings - {S4#state{status = Status2, - old_status = S#state.status}, - Status2}; - {error, _} -> - %% Keep old state - {S, Status2} - end, - reltool_utils:reply(ReplyTo, Ref, Status3), - ?MODULE:loop(S5); + Fun = fun() -> S#state{sys = Sys2#sys{apps = Sys#sys.apps}} end, + {S3, Status} = config_and_refresh(S, Fun), + reltool_utils:reply(ReplyTo, Ref, Status), + ?MODULE:loop(S3); {call, ReplyTo, Ref, get_status} -> reltool_utils:reply(ReplyTo, Ref, S#state.status), ?MODULE:loop(S); {call, ReplyTo, Ref, {gen_rel_files, Dir}} -> Status = - case reltool_target:gen_rel_files(S#state.sys, Dir) of + case reltool_target:gen_rel_files(sys_all_apps(S), Dir) of ok -> {ok, []}; {error, Reason} -> @@ -402,11 +389,11 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Status), ?MODULE:loop(S); {call, ReplyTo, Ref, {gen_target, Dir}} -> - Reply = reltool_target:gen_target(S#state.sys, Dir), + Reply = reltool_target:gen_target(sys_all_apps(S), Dir), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, gen_spec} -> - Reply = reltool_target:gen_spec(S#state.sys), + Reply = reltool_target:gen_spec(sys_all_apps(S)), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {'EXIT', Pid, Reason} when Pid =:= S#state.parent_pid -> @@ -422,101 +409,211 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S) end. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -do_set_app(#state{sys = Sys} = S, App, Status) -> - AppName = App#app.name, - {App2, Status2} = refresh_app(App, false, Status), - Apps = Sys#sys.apps, - Apps2 = lists:keystore(AppName, #app.name, Apps, App2), - Escripts = [A#app.active_dir || A <- Apps2, A#app.is_escript], - Sys2 = Sys#sys{apps = Apps2, escripts = Escripts}, - {S#state{sys = Sys2}, Status2}. - -analyse(#state{common = C, - sys = #sys{apps = Apps0, rels = Rels} = Sys} = S, - Status) -> - Apps = lists:keydelete(?MISSING_APP_NAME, #app.name, Apps0), - ets:delete_all_objects(C#common.app_tab), - ets:delete_all_objects(C#common.mod_tab), - ets:delete_all_objects(C#common.mod_used_by_tab), - MissingApp = default_app(?MISSING_APP_NAME, "missing"), - ets:insert(C#common.app_tab, MissingApp), - - {RevRelApps, Status2} = apps_in_rels(Rels, Apps, Status), - RelApps2 = lists:reverse(RevRelApps), - {Apps2, Status3} = - lists:mapfoldl(fun(App, Acc) -> - app_init_is_included(C, Sys, App, RelApps2, Acc) - end, - Status2, - Apps), - Apps3 = - case app_propagate_is_included(C, Sys, Apps2, []) of - [] -> - Apps2; - MissingMods -> - %% io:format("Missing mods: ~p\n", [MissingMods]), - MissingApp2 = MissingApp#app{label = ?MISSING_APP_TEXT, - info = missing_app_info(""), - mods = MissingMods, - status = missing, - uses_mods = []}, - [MissingApp2 | Apps2] - end, - app_propagate_is_used_by(C, Apps3), - {Apps4,Status4} = app_recap_dependencies(C, Sys, Apps3, [], Status3), - %% io:format("Missing app: ~p\n", - %% [lists:keysearch(?MISSING_APP_NAME, #app.name, Apps4)]), - Sys2 = Sys#sys{apps = Apps4}, - - case verify_config(RelApps2, Sys2, Status4) of - {ok, _Warnings} = Status5 -> - {S#state{sys = Sys2}, Status5}; - {error, _} = Status5 -> - {S, Status5} + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +do_set_apps(#state{sys = Sys} = S, ChangedApps) -> + %% Create new list of configured applications + SysApps = app_update_config(ChangedApps, Sys#sys.apps), + S#state{sys = Sys#sys{apps = SysApps}}. + +%% Re-create the #sys.apps list by +%% 1) taking configurable fields from the changed #app records and +%% create new default records +%% 2) removing #app records if no configurable fields are set +%% 3) keeping #app records that are not changed +app_update_config([#app{name=Name,is_escript={inlined,Escript}}|_],_SysApps) -> + reltool_utils:throw_error("Application ~p is inlined in ~p. Can not change " + "configuration for an inlined application.", + [Name,Escript]); +app_update_config([Config|Configs],SysApps) -> + NewSysApps = + case app_set_config_only(Config) of + {delete,Name} -> + lists:keydelete(Name,#app.name,SysApps); + New -> + lists:ukeymerge(#app.name,[New],SysApps) + end, + app_update_config(Configs,NewSysApps); +app_update_config([],SysApps) -> + SysApps. + +app_set_config_only(#app{mods=ConfigMods} = Config) -> + app_set_config_only(mod_set_config_only(ConfigMods),Config). + +app_set_config_only([],#app{name = Name, + incl_cond = undefined, + mod_cond = undefined, + use_selected_vsn = undefined, + debug_info = undefined, + app_file = undefined, + app_type = undefined, + incl_app_filters = undefined, + excl_app_filters = undefined, + incl_archive_filters = undefined, + excl_archive_filters = undefined, + archive_opts = undefined, + is_escript = false})-> + {delete,Name}; +app_set_config_only(Mods,#app{name = Name, + incl_cond = InclCond, + mod_cond = ModCond, + use_selected_vsn = UseSelectedVsn, + debug_info = DebugInfo, + app_file = AppFile, + app_type = AppType, + incl_app_filters = InclAppFilters, + excl_app_filters = ExclAppFilters, + incl_archive_filters = InclArchiveFilters, + excl_archive_filters = ExclArchiveFilters, + archive_opts = ArchiveOpts, + vsn = Vsn, + is_escript = IsEscript, + label = Label, + info = Info, + active_dir = ActiveDir, + sorted_dirs = SortedDirs}) -> + App = (default_app(Name))#app{incl_cond = InclCond, + mod_cond = ModCond, + use_selected_vsn = UseSelectedVsn, + debug_info = DebugInfo, + app_file = AppFile, + app_type = AppType, + incl_app_filters = InclAppFilters, + excl_app_filters = ExclAppFilters, + incl_archive_filters = InclArchiveFilters, + excl_archive_filters = ExclArchiveFilters, + archive_opts = ArchiveOpts, + vsn = Vsn, + mods = Mods}, + + %% Some fields shall only be set if it is an escript, e.g. label + %% must never be set for any other applications since that will + %% prevent refreshing. + if IsEscript -> + App#app{is_escript = IsEscript, + active_dir = ActiveDir, + sorted_dirs = SortedDirs, + label = Label, + info = Info}; + true -> + App end. -apps_in_rels(Rels, Apps, Status) -> - lists:foldl(fun(Rel, {RelApps, S}) -> - {MoreRelApps, S2} = apps_in_rel(Rel, Apps, S), - {MoreRelApps ++ RelApps, S2} - end, - {[], Status}, - Rels). +mod_set_config_only(ConfigMods) -> + [#mod{name = Name, + incl_cond = InclCond, + debug_info = DebugInfo} || + #mod{name = Name, + incl_cond = InclCond, + debug_info = DebugInfo} <- ConfigMods, + (InclCond =/= undefined) orelse (DebugInfo =/= undefined)]. -apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps, Status) -> - Mandatory = [{RelName, kernel}, {RelName, stdlib}], - Other = [{RelName, AppName} || - RA <- RelApps, - AppName <- [RA#rel_app.name | RA#rel_app.incl_apps], - not lists:keymember(AppName, 2, Mandatory)], - more_apps_in_rels(Mandatory ++ Other, Apps, [], Status). -more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc, Status) -> + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +analyse(#state{sys=Sys} = S, Apps, Status) -> + %% Create a list of {RelName,AppName}, one element for each + %% AppName that needs to be included for the given release. + RelApps = apps_in_rels(Sys#sys.rels, Apps), + + %% Initiate is_pre_included and is_included for all applications + %% based on #sys.incl_cond, #app.incl_cond and if the application + %% is included in a release (rel spec - see apps_in_rels above). + %% Then initiate the same for each module, and check that there + %% are no duplicated module names (in different applications) + %% where we can not decide which one to use. + %% Write all #app to app_tab and all #mod to mod_tab. + Status2 = apps_init_is_included(S, Apps, RelApps, Status), + + %% For each module that has #mod.is_included==true, propagate + %% is_included to the modules it uses. + propagate_is_included(S), + + %% Insert reverse dependencies - i.e. for each + %% #mod{name=Mod, uses_mods=[UsedMod]}, + %% insert an entry {UsedMod,Mod} in mod_used_by_tab. + propagate_is_used_by(S), + + %% Set the above reverse dependencies in #mod records + %% (used_by_mods) and accumulate in #app records. + %% Make sure #app.is_included is always true if some + %% #mod.is_included==true for at least one module in the app. + %% Set status=missing|ok for #app and #mod - indicates if module + %% (.beam file) is missing in file system. + app_recap_dependencies(S), + + %% Check that the boot_rel exists. + %% Check that all applications that are listed in a 'rel' spec are + %% also really included in the target release. + %% Check that all mandatory applications are included in all rels. + verify_config(S, RelApps, Status2). + +apps_in_rels(Rels, Apps) -> + AllRelApps = + lists:foldl(fun(Rel, RelApps) -> + MoreRelApps = apps_in_rel(Rel, Apps), + MoreRelApps ++ RelApps + end, + [], + Rels), + lists:reverse(AllRelApps). + +apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps) -> + Mandatory = [{RelName, kernel}, {RelName, stdlib}], + Other = + [{RelName, AppName} || + RA <- RelApps, + AppName <- [RA#rel_app.name | + %% Included applications in rel shall overwrite included + %% applications in .app. I.e. included applications in + %% .app shall only be used if it is not defined in rel. + case RA#rel_app.incl_apps of + undefined -> + case lists:keyfind(RA#rel_app.name, + #app.name, + Apps) of + #app{info = #app_info{incl_apps = IA}} -> + IA; + false -> + reltool_utils:throw_error( + "Release ~p uses non existing " + "application ~p", + [RelName,RA#rel_app.name]) + end; + IA -> + IA + end], + not lists:keymember(AppName, 2, Mandatory)], + more_apps_in_rels(Mandatory ++ Other, Apps, []). + +more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) -> case lists:member(RA, Acc) of true -> - more_apps_in_rels(RelApps, Apps, Acc, Status); + more_apps_in_rels(RelApps, Apps, Acc); false -> case lists:keyfind(AppName, #app.name, Apps) of #app{info = #app_info{applications = InfoApps}} -> Extra = [{RelName, N} || N <- InfoApps], - {Acc2, Status2} = - more_apps_in_rels(Extra, Apps, [RA | Acc], Status), - more_apps_in_rels(RelApps, Apps, Acc2, Status2); + Acc2 = more_apps_in_rels(Extra, Apps, [RA | Acc]), + more_apps_in_rels(RelApps, Apps, Acc2); false -> - Text = lists:concat(["Release ", RelName, - " uses non existing application ", - AppName]), - Status2 = reltool_utils:return_first_error(Status, Text), - more_apps_in_rels(RelApps, Apps, Acc, Status2) + reltool_utils:throw_error( + "Release ~p uses non existing application ~p", + [RelName,AppName]) end end; -more_apps_in_rels([], _Apps, Acc, Status) -> - {Acc, Status}. +more_apps_in_rels([], _Apps, Acc) -> + Acc. + + +apps_init_is_included(S, Apps, RelApps, Status) -> + lists:foldl(fun(App, AccStatus) -> + app_init_is_included(S, App, RelApps, AccStatus) + end, + Status, + Apps). -app_init_is_included(C, - Sys, +app_init_is_included(#state{app_tab = AppTab, mod_tab = ModTab, sys=Sys}, #app{name = AppName, mods = Mods} = A, RelApps, Status) -> @@ -538,18 +635,16 @@ app_init_is_included(C, {exclude, []} -> {undefined, false, false, Status}; {exclude, [RelName | _]} -> % App is included in at least one rel - Text = lists:concat(["Application ", AppName, " is used " - "in release ", RelName, " and cannot " - "be excluded"]), - TmpStatus = reltool_utils:return_first_error(Status, Text), - {undefined, false, false, TmpStatus}; + reltool_utils:throw_error( + "Application ~p is used in release ~p and cannot be excluded", + [AppName,RelName]); {derived, []} -> {undefined, undefined, undefined, Status}; {derived, [_ | _]} -> % App is included in at least one rel {true, undefined, true, Status} end, {Mods2,Status3} = lists:mapfoldl(fun(Mod,Acc) -> - mod_init_is_included(C, + mod_init_is_included(ModTab, Mod, ModCond, AppCond, @@ -562,10 +657,10 @@ app_init_is_included(C, is_pre_included = IsPreIncl, is_included = IsIncl, rels = Rels}, - ets:insert(C#common.app_tab, A2), - {A2, Status3}. + ets:insert(AppTab, A2), + Status3. -mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> +mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) -> %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]), IsIncl = case AppCond of @@ -602,43 +697,33 @@ mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> M2 = M#mod{is_pre_included = IsIncl, is_included = IsIncl}, Status2 = - case ets:lookup(C#common.mod_tab,M#mod.name) of + case ets:lookup(ModTab,M#mod.name) of [Existing] -> case {Existing#mod.is_included,IsIncl} of {false,_} -> - Warning = - lists:concat( - ["Module ",M#mod.name, - " exists in applications ", Existing#mod.app_name, - " and ", M#mod.app_name, - ". Using module from application ", - M#mod.app_name, "."]), - ets:insert(C#common.mod_tab, M2), - reltool_utils:add_warning(Status,Warning); + ets:insert(ModTab, M2), + reltool_utils:add_warning( + "Module ~p exists in applications ~p and ~p. " + "Using module from application ~p.", + [M#mod.name, Existing#mod.app_name, + M#mod.app_name, M#mod.app_name], + Status); {_,false} -> - Warning = - lists:concat( - ["Module ",M#mod.name, - " exists in applications ", Existing#mod.app_name, - " and ", M#mod.app_name, - ". Using module from application ", - Existing#mod.app_name, "."]), - - %% Don't insert in mod_tab - using Existing - reltool_utils:add_warning(Status,Warning); + %% Don't insert in ModTab - using Existing + reltool_utils:add_warning( + "Module ~p exists in applications ~p and ~p. " + "Using module from application ~p.", + [M#mod.name, Existing#mod.app_name, + M#mod.app_name,Existing#mod.app_name], + Status); {_,_} -> - Error = - lists:concat( - ["Module ",M#mod.name, - " potentially included by ", - "two different applications: ", - Existing#mod.app_name, " and ", - M#mod.app_name, "."]), - %% Don't insert in mod_tab - using Existing - reltool_utils:return_first_error(Status,Error) + reltool_utils:throw_error( + "Module ~p potentially included by two different " + "applications: ~p and ~p.", + [M#mod.name,Existing#mod.app_name,M#mod.app_name]) end; [] -> - ets:insert(C#common.mod_tab, M2), + ets:insert(ModTab, M2), Status end, @@ -651,55 +736,43 @@ false_to_undefined(Bool) -> _ -> Bool end. -app_propagate_is_included(C, Sys, [#app{mods = Mods} = A | Apps], Acc) -> - Acc2 = mod_propagate_is_included(C, Sys, A, Mods, Acc), - app_propagate_is_included(C, Sys, Apps, Acc2); -app_propagate_is_included(_C, _Sys, [], Acc) -> - Acc. - -mod_propagate_is_included(C, Sys, A, [#mod{name = ModName} | Mods], Acc) -> - Acc2 = - case ets:lookup(C#common.mod_tab, ModName) of - [M2] when M2#mod.app_name=:=A#app.name -> - %% print(ModName, file, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), - %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), - case M2#mod.is_included of - true -> - %% Propagate include mark - mod_mark_is_included(C,Sys,ModName,M2#mod.uses_mods,Acc); - false -> - Acc; - undefined -> - Acc - end; - [_] -> - %% This module is currently used from a different application - %% Ignore - Acc - end, - mod_propagate_is_included(C, Sys, A, Mods, Acc2); -mod_propagate_is_included(_C, _Sys, _A, [], Acc) -> - Acc. +%% Return the list for {ModName, UsesModNames} for all modules where +%% #mod.is_included==true. +get_all_mods_and_dependencies(S) -> + ets:select(S#state.mod_tab, [{#mod{name='$1', + uses_mods='$2', + is_included=true, + _='_'}, + [], + [{{'$1','$2'}}]}]). + +propagate_is_included(S) -> + case lists:flatmap( + fun({ModName,UsesModNames}) -> + mod_mark_is_included(S,ModName,UsesModNames,[]) + end, + get_all_mods_and_dependencies(S)) of + [] -> + ok; + MissingMods -> + MissingApp = default_app(?MISSING_APP_NAME, "missing"), + MissingApp2 = MissingApp#app{label = ?MISSING_APP_TEXT, + info = missing_app_info(""), + mods = MissingMods, + status = missing, + uses_mods = []}, + ets:insert(S#state.app_tab, MissingApp2), + ok + end. -mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> +mod_mark_is_included(#state{app_tab=AppTab, mod_tab=ModTab, sys=Sys} = S, + UsedByName, [ModName | ModNames], Acc) -> Acc3 = - case ets:lookup(C#common.mod_tab, ModName) of + case ets:lookup(ModTab, ModName) of [M] -> - %% print(UsedByName, file, "Maybe Mark ~p -> ~p\n", - %% [M, M#mod.is_included]), - %% print(UsedByName, filename, "Maybe Mark ~p -> ~p\n", - %% [M, M#mod.is_included]), case M#mod.is_included of - true -> - %% Already marked - Acc; - false -> - %% Already marked - Acc; undefined -> - %% Mark and propagate + %% Not yet marked => mark and propagate M2 = case M#mod.incl_cond of include -> @@ -711,16 +784,10 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> undefined -> M#mod{is_included = true} end, - ets:insert(C#common.mod_tab, M2), - %% io:format("Propagate mod: ~p -> ~p (~p)\n", - %% [UsedByName, ModName, M#mod.incl_cond]), - [A] = ets:lookup(C#common.app_tab, M2#mod.app_name), + ets:insert(ModTab, M2), + [A] = ets:lookup(AppTab, M2#mod.app_name), Acc2 = case A#app.is_included of - true -> - Acc; - false -> - Acc; undefined -> ModCond = case A#app.mod_cond of @@ -738,63 +805,50 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> end end, Mods = lists:filter(Filter, A#app.mods), - %% io:format("Propagate app: ~p ~p -> ~p\n", - %% [UsedByName, A#app.name, - %% [M3#mod.name || M3 <- Mods]]), A2 = A#app{is_included = true}, - ets:insert(C#common.app_tab, A2), - mod_mark_is_included(C, - Sys, + ets:insert(AppTab, A2), + mod_mark_is_included(S, ModName, [M3#mod.name || M3 <- Mods], - Acc) + Acc); + _ -> + %% Already marked true or false + Acc end, - mod_mark_is_included(C, - Sys, - ModName, - M2#mod.uses_mods, - Acc2) + mod_mark_is_included(S, ModName, M2#mod.uses_mods, Acc2); + _ -> + %% Already marked true or false + Acc end; [] -> M = missing_mod(ModName, ?MISSING_APP_NAME), M2 = M#mod{is_included = true}, - ets:insert(C#common.mod_tab, M2), - ets:insert(C#common.mod_used_by_tab, {UsedByName, ModName}), + ets:insert(ModTab, M2), [M2 | Acc] end, - mod_mark_is_included(C, Sys, UsedByName, ModNames, Acc3); -mod_mark_is_included(_C, _Sys, _UsedByName, [], Acc) -> + mod_mark_is_included(S, UsedByName, ModNames, Acc3); +mod_mark_is_included(_S, _UsedByName, [], Acc) -> Acc. -app_propagate_is_used_by(C, [#app{mods = Mods, name = Name} | Apps]) -> - case Name =:= ?MISSING_APP_NAME of - true -> ok; - false -> ok - end, - mod_propagate_is_used_by(C, Mods), - app_propagate_is_used_by(C, Apps); -app_propagate_is_used_by(_C, []) -> - ok. +propagate_is_used_by(S) -> + lists:foreach( + fun({Mod,UsesMods}) -> + lists:foreach( + fun(UsedMod) -> + ets:insert(S#state.mod_used_by_tab,{UsedMod,Mod}) + end, + UsesMods) + end, + get_all_mods_and_dependencies(S)). -mod_propagate_is_used_by(C, [#mod{name = ModName} | Mods]) -> - [M] = ets:lookup(C#common.mod_tab, ModName), - case M#mod.is_included of - true -> - [ets:insert(C#common.mod_used_by_tab, {UsedModName, ModName}) || - UsedModName <- M#mod.uses_mods]; - false -> - ignore; - undefined -> - ignore - end, - mod_propagate_is_used_by(C, Mods); -mod_propagate_is_used_by(_C, []) -> - ok. -app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc, Status) -> - {Mods2, IsIncl2, Status2} = - mod_recap_dependencies(C, Sys, A, Mods, [], IsIncl, Status), +app_recap_dependencies(S) -> + ets:foldl(fun(App,_) -> app_recap_dependencies(S,App) end, + ok, S#state.app_tab). + +app_recap_dependencies(S, #app{mods = Mods, is_included = IsIncl} = A) -> + {Mods2, IsIncl2} = mod_recap_dependencies(S, A, Mods, [], IsIncl), AppStatus = case lists:keymember(missing, #mod.status, Mods2) of true -> missing; @@ -803,12 +857,12 @@ app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Ap UsesMods = [M#mod.uses_mods || M <- Mods2, M#mod.is_included =:= true], UsesMods2 = lists:usort(lists:flatten(UsesMods)), UsesApps = [M#mod.app_name || ModName <- UsesMods2, - M <- ets:lookup(C#common.mod_tab, ModName)], + M <- ets:lookup(S#state.mod_tab, ModName)], UsesApps2 = lists:usort(UsesApps), UsedByMods = [M#mod.used_by_mods || M <- Mods2, M#mod.is_included =:= true], UsedByMods2 = lists:usort(lists:flatten(UsedByMods)), UsedByApps = [M#mod.app_name || ModName <- UsedByMods2, - M <- ets:lookup(C#common.mod_tab, ModName)], + M <- ets:lookup(S#state.mod_tab, ModName)], UsedByApps2 = lists:usort(UsedByApps), A2 = A#app{mods = Mods2, @@ -818,13 +872,11 @@ app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Ap uses_apps = UsesApps2, used_by_apps = UsedByApps2, is_included = IsIncl2}, - ets:insert(C#common.app_tab,A2), - app_recap_dependencies(C, Sys, Apps, [A2 | Acc], Status2); -app_recap_dependencies(_C, _Sys, [], Acc, Status) -> - {lists:reverse(Acc), Status}. + ets:insert(S#state.app_tab,A2), + ok. -mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, Status) -> - case ets:lookup(C#common.mod_tab, ModName) of +mod_recap_dependencies(S, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl) -> + case ets:lookup(S#state.mod_tab, ModName) of [M2] when M2#mod.app_name=:=A#app.name -> ModStatus = do_get_status(M2), %% print(M2#mod.name, hipe, "status -> ~p\n", [ModStatus]), @@ -832,32 +884,28 @@ mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, case M2#mod.is_included of true -> UsedByMods = - [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, + [N || {_, N} <- ets:lookup(S#state.mod_used_by_tab, ModName)], {true, M2#mod{status = ModStatus, used_by_mods = UsedByMods}}; _ -> {IsIncl, M2#mod{status = ModStatus, used_by_mods = []}} end, - ets:insert(C#common.mod_tab, M3), - mod_recap_dependencies(C, Sys, A, Mods, [M3 | Acc], IsIncl2, Status); + ets:insert(S#state.mod_tab, M3), + mod_recap_dependencies(S, A, Mods, [M3 | Acc], IsIncl2); [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> %% App is explicitely excluded so it is ok that the module %% record does not exist for this module in this %% application. - mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status); + mod_recap_dependencies(S, A, Mods, [M1 | Acc], IsIncl); [M2] -> %% A module is potensially included by multiple %% applications. This is not allowed! - Error = - lists:concat( - ["Module ",ModName, - " potentially included by two different applications: ", - A#app.name, " and ", M2#mod.app_name, "."]), - Status2 = reltool_utils:return_first_error(Status,Error), - mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status2) + reltool_utils:throw_error( + "Module ~p potentially included by two different applications: " + "~p and ~p", [ModName,A#app.name, " and ", M2#mod.app_name, "."]) end; -mod_recap_dependencies(_C, _Sys, _A, [], Acc, IsIncl, Status) -> - {lists:reverse(Acc), IsIncl, Status}. +mod_recap_dependencies(_S, _A, [], Acc, IsIncl) -> + {lists:reverse(Acc), IsIncl}. do_get_status(M) -> if @@ -867,48 +915,49 @@ do_get_status(M) -> ok end. -shrink_sys(#state{sys = #sys{apps = Apps} = Sys} = S) -> - Apps2 = lists:zf(fun filter_app/1, Apps), - S#state{sys = Sys#sys{apps = Apps2}}. - -filter_app(A) -> - Mods = [M#mod{is_app_mod = undefined, - is_ebin_mod = undefined, - uses_mods = undefined, - exists = false} || - M <- A#app.mods, - M#mod.incl_cond =/= undefined], - if - A#app.is_escript -> - {true, A#app{vsn = undefined, - label = undefined, - info = undefined, - mods = [], - uses_mods = undefined}}; - Mods =:= [], - A#app.mod_cond =:= undefined, - A#app.incl_cond =:= undefined, - A#app.use_selected_vsn =:= undefined -> - false; +verify_config(#state{app_tab=AppTab, sys=#sys{boot_rel = BootRel, rels = Rels}}, + RelApps, Status) -> + case lists:keymember(BootRel, #rel.name, Rels) of true -> - {Dir, Dirs, OptVsn} = - case A#app.use_selected_vsn of - undefined -> - {shrinked, [], undefined}; - false -> - {shrinked, [], undefined}; - true -> - {A#app.active_dir, [A#app.active_dir], A#app.vsn} - end, - {true, A#app{active_dir = Dir, - sorted_dirs = Dirs, - vsn = OptVsn, - label = undefined, - info = undefined, - mods = Mods, - uses_mods = undefined}} + Status2 = lists:foldl(fun(RA, Acc) -> + check_app(AppTab, RA, Acc) end, + Status, + RelApps), + lists:foldl(fun(#rel{name = RelName}, Acc)-> + check_rel(RelName, RelApps, Acc) + end, + Status2, + Rels); + false -> + reltool_utils:throw_error( + "Release ~p is mandatory (used as boot_rel)",[BootRel]) + end. + +check_app(AppTab, {RelName, AppName}, Status) -> + case ets:lookup(AppTab, AppName) of + [#app{is_pre_included=IsPreIncl, is_included=IsIncl}] + when IsPreIncl; IsIncl -> + Status; + _ -> + reltool_utils:throw_error( + "Release ~p uses non included application ~p",[RelName,AppName]) end. +check_rel(RelName, RelApps, Status) -> + EnsureApp = + fun(AppName, Acc) -> + case lists:member({RelName, AppName}, RelApps) of + true -> + Acc; + false -> + reltool_utils:throw_error( + "Mandatory application ~p is not included in " + "release ~p", [AppName,RelName]) + end + end, + Mandatory = [kernel, stdlib], + lists:foldl(EnsureApp, Status, Mandatory). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% refresh_app(#app{name = AppName, @@ -937,30 +986,57 @@ refresh_app(#app{name = AppName, AppName, DefaultVsn, Status), + + %% And read all modules from ebin and create + %% #mod record with dependencies (uses_mods). {AI, read_ebin_mods(Ebin, AppName), Status2}; - true -> + _ -> {App#app.info, Mods, Status} end, - %% Add non-existing modules - AppInfoMods = AppInfo#app_info.modules, - AppModNames = - case AppInfo#app_info.mod of - {StartModName, _} -> - case lists:member(StartModName, AppInfoMods) of - true -> AppInfoMods; - false -> [StartModName | AppInfoMods] - end; - undefined -> - AppInfoMods - end, - MissingMods = add_missing_mods(AppName, EbinMods, AppModNames), + %% Add non-existing modules - i.e. create default #mod + %% records for all modules that are listed in .app file + %% but do not exist in ebin. + AppInfoMods = lists:usort(AppInfo#app_info.modules), + Status4 = + case AppInfo#app_info.modules -- AppInfoMods of + [] -> + Status3; + DuplicatedMods -> + lists:foldl( + fun(M,S) -> + reltool_utils:add_warning( + "Module ~p duplicated in app file for " + "application ~p.", [M, AppName], S) + end, + Status3, + DuplicatedMods) + end, + AppModNames = + case AppInfo#app_info.mod of + {StartModName, _} -> + case lists:member(StartModName, AppInfoMods) of + true -> AppInfoMods; + false -> [StartModName | AppInfoMods] + end; + undefined -> + AppInfoMods + end, + MissingMods = add_missing_mods(AppName, EbinMods, AppModNames), - %% Add optional user config for each module + %% Add optional user config for each module. + %% The #mod records that are already in the #app record at + %% this point do only contain user defined configuration + %% (set by parse_options/4). So here we merge with the + %% default records from above. Mods2 = add_mod_config(MissingMods ++ EbinMods, Mods), - %% Set app flag for each module in app file + %% Set app flag for each module in app file, i.e. the flag + %% which indicates if the module is listed in the .app + %% file or not. The start module also get the flag set to true. Mods3 = set_mod_flags(Mods2, AppModNames), + + %% Finally, set label and update the #app record AppVsn = AppInfo#app_info.vsn, AppLabel = case AppVsn of @@ -971,7 +1047,7 @@ refresh_app(#app{name = AppName, label = AppLabel, info = AppInfo, mods = lists:keysort(#mod.name, Mods3)}, - {App2, Status3}; + {App2, Status4}; true -> {App, Status} end. @@ -988,22 +1064,21 @@ read_app_info(AppFileOrBin, AppFile, AppName, DefaultVsn, Status) -> AI = #app_info{vsn = DefaultVsn}, parse_app_info(AppFile, Info, AI, Status); {ok, _BadApp} -> - Text = lists:concat([AppName, - ": Illegal contents in app file ", AppFile, - ", application tuple with arity 3 expected."]), {missing_app_info(DefaultVsn), - reltool_utils:add_warning(Status, Text)}; + reltool_utils:add_warning("~p: Illegal contents in app file ~p, " + "application tuple with arity 3 expected.", + [AppName,AppFile], + Status)}; {error, Text} when Text =:= EnoentText -> - Text2 = lists:concat([AppName, - ": Missing app file ", AppFile, "."]), {missing_app_info(DefaultVsn), - reltool_utils:add_warning(Status, Text2)}; + reltool_utils:add_warning("~p: Missing app file ~p.", + [AppName,AppFile], + Status)}; {error, Text} -> - Text2 = lists:concat([AppName, - ": Cannot parse app file ", - AppFile, " (", Text, ")."]), {missing_app_info(DefaultVsn), - reltool_utils:add_warning(Status, Text2)} + reltool_utils:add_warning("~p: Cannot parse app file ~p (~p).", + [AppName,AppFile,Text], + Status)} end. parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) -> @@ -1037,10 +1112,11 @@ parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) -> parse_app_info(File, KeyVals, AI#app_info{start_phases = Val}, Status); _ -> - String = lists:concat(["Unexpected item ", - Key, "in app file ", File]), - parse_app_info(File, KeyVals, AI, - reltool_utils:add_warning(Status, String)) + Status2 = + reltool_utils:add_warning("Unexpected item ~p in app file ~p.", + [Key,File], + Status), + parse_app_info(File, KeyVals, AI, Status2) end; parse_app_info(_, [], AI, Status) -> {AI, Status}. @@ -1171,12 +1247,54 @@ set_mod_flags(Mods, AppModNames) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_get_config(S, InclDef, InclDeriv) -> - S2 = + AppTab = S#state.app_tab, + Sys = case InclDeriv of - false -> shrink_sys(S); - true -> S + false -> + %% Only the apps that exist in #sys.apps shall be + %% included,and they shall be minimized + Apps = [shrink_app(App) || + #app{name=Name} <- (S#state.sys)#sys.apps, + App <- ets:lookup(AppTab,Name)], + (S#state.sys)#sys{apps=Apps}; + true -> + sys_all_apps(S) end, - reltool_target:gen_config(S2#state.sys, InclDef). + reltool_target:gen_config(Sys, InclDef). + +shrink_app(A) -> + Mods = [M#mod{is_app_mod = undefined, + is_ebin_mod = undefined, + uses_mods = undefined, + exists = false} || + M <- A#app.mods, + M#mod.incl_cond =/= undefined], + if + A#app.is_escript -> + A#app{vsn = undefined, + label = undefined, + info = undefined, + mods = [], + uses_mods = undefined}; + true -> + {Dir, Dirs, OptVsn} = + case A#app.use_selected_vsn of + undefined -> + {shrinked, [], undefined}; + false -> + {shrinked, [], undefined}; + true -> + {A#app.active_dir, [A#app.active_dir], A#app.vsn} + end, + A#app{active_dir = Dir, + sorted_dirs = Dirs, + vsn = OptVsn, + label = undefined, + info = undefined, + mods = Mods, + uses_mods = undefined} + end. + do_save_config(S, Filename, InclDef, InclDeriv) -> {ok, Config} = do_get_config(S, InclDef, InclDeriv), @@ -1187,132 +1305,83 @@ do_save_config(S, Filename, InclDef, InclDeriv) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_load_config(S, SysConfig) -> - OldSys = S#state.sys, - S2 = shrink_sys(S), - ShrinkedSys = S2#state.sys, - {NewSys, Status} = - read_config(ShrinkedSys#sys{apps = []}, SysConfig, {ok, []}), - case Status of - {ok, _Warnings} -> - Force = false, - {MergedSys, Status2} = merge_config(OldSys, NewSys, Force, Status), - {S3, Status3} = - analyse(S2#state{sys = MergedSys, old_sys = OldSys}, Status2), - S4 = - case Status3 of - {ok, _Warnings2} -> - S3#state{status = Status3, old_status = S#state.status}; - {error, _} -> - %% Keep old state - S - end, - {S4, Status3}; - {error, _} -> - %% Keep old state - {S, Status} - end. + S#state{sys = read_config(default_sys(), SysConfig)}. -read_config(OldSys, Filename, Status) when is_list(Filename) -> +read_config(OldSys, Filename) when is_list(Filename) -> case file:consult(Filename) of {ok, [SysConfig | _]} -> - read_config(OldSys, SysConfig, Status); + read_config(OldSys, SysConfig); {ok, Content} -> - Text = lists:flatten(io_lib:format("~p", [Content])), - {OldSys, - reltool_utils:return_first_error(Status, - "Illegal file content: " ++ - Text)}; + reltool_utils:throw_error("Illegal file content: ~p",[Content]); {error, Reason} -> - Text = file:format_error(Reason), - {OldSys, - reltool_utils:return_first_error(Status, - "Illegal config file " ++ - Filename ++ ": " ++ Text)} + reltool_utils:throw_error("Illegal config file ~p: ~s", + [Filename,file:format_error(Reason)]) end; -read_config(OldSys, {sys, KeyVals}, Status) -> - {NewSys, Status2} = - decode(OldSys#sys{apps = [], rels = []}, KeyVals, Status), - case Status2 of - {ok, _Warnings} -> % BUGBUG: handle warnings - Apps = [A#app{mods = lists:sort(A#app.mods)} || - A <- NewSys#sys.apps], - case NewSys#sys.rels of - [] -> Rels = reltool_utils:default_rels(); - Rels -> ok - end, - NewSys2 = NewSys#sys{apps = lists:sort(Apps), - rels = lists:sort(Rels)}, - case lists:keymember(NewSys2#sys.boot_rel, - #rel.name, - NewSys2#sys.rels) of - true -> - {NewSys2, Status2}; - false -> - Text2 = lists:concat(["Release " ++ NewSys2#sys.boot_rel, - " is mandatory (used as boot_rel)"]), - {OldSys, reltool_utils:return_first_error(Status2, Text2)} - end; - {error, _} -> - %% Keep old state - {OldSys, Status2} +read_config(OldSys, {sys, KeyVals}) -> + NewSys = decode(OldSys#sys{apps = [], rels = []}, KeyVals), + Apps = [A#app{mods = lists:sort(A#app.mods)} || A <- NewSys#sys.apps], + Rels = + case NewSys#sys.rels of + [] -> reltool_utils:default_rels(); + Rs -> Rs + end, + NewSys2 = NewSys#sys{apps = lists:sort(Apps), + rels = lists:sort(Rels)}, + case lists:keymember(NewSys2#sys.boot_rel, #rel.name, NewSys2#sys.rels) of + true -> + NewSys2; + false -> + reltool_utils:throw_error( + "Release ~p is mandatory (used as boot_rel)", + [NewSys2#sys.boot_rel]) end; -read_config(OldSys, BadConfig, Status) -> - Text = lists:flatten(io_lib:format("~p", [BadConfig])), - {OldSys, - reltool_utils:return_first_error(Status, "Illegal content: " ++ Text)}. +read_config(_OldSys, BadConfig) -> + reltool_utils:throw_error("Illegal content: ~p", [BadConfig]). -decode(#sys{apps = Apps} = Sys, [{erts = Name, AppKeyVals} | SysKeyVals], - Status) +decode(#sys{apps = Apps} = Sys, [{erts = Name, AppKeyVals} | SysKeyVals]) when is_atom(Name), is_list(AppKeyVals) -> App = default_app(Name), - {App2, Status2} = decode(App, AppKeyVals, Status), - decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals, Status2); -decode(#sys{apps = Apps} = Sys, [{app, Name, AppKeyVals} | SysKeyVals], Status) + App2= decode(App, AppKeyVals), + decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals); +decode(#sys{apps = Apps} = Sys, [{app, Name, AppKeyVals} | SysKeyVals]) when is_atom(Name), is_list(AppKeyVals) -> App = default_app(Name), - {App2, Status2} = decode(App, AppKeyVals, Status), - decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals, Status2); + App2 = decode(App, AppKeyVals), + decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals); decode(#sys{apps = Apps, escripts = Escripts} = Sys, - [{escript, File, AppKeyVals} | SysKeyVals], Status) - when is_list(File), is_list(AppKeyVals) -> - {Name, Label} = split_escript_name(File), - App = default_app(Name, File), - App2 = App#app{is_escript = true, - label = Label, - info = missing_app_info(""), - active_dir = File, - sorted_dirs = [File]}, - {App3, Status2} = decode(App2, AppKeyVals, Status), - decode(Sys#sys{apps = [App3 | Apps], escripts = [File | Escripts]}, - SysKeyVals, - Status2); -decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps} | SysKeyVals], - Status) + [{escript, File0, AppKeyVals} | SysKeyVals]) + when is_list(File0), is_list(AppKeyVals) -> + File = filename:absname(File0), + App = default_escript_app(File), + App2 = decode(App, AppKeyVals), + decode(Sys#sys{apps = [App2 | Apps], escripts = [File | Escripts]}, + SysKeyVals); +decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps} | SysKeyVals]) when is_list(Name), is_list(Vsn), is_list(RelApps) -> Rel = #rel{name = Name, vsn = Vsn, rel_apps = []}, - {Rel2, Status2} = decode(Rel, RelApps, Status), - decode(Sys#sys{rels = [Rel2 | Rels]}, SysKeyVals, Status2); -decode(#sys{} = Sys, [{Key, Val} | KeyVals], Status) -> - {Sys3, Status3} = + Rel2 = decode(Rel, RelApps), + decode(Sys#sys{rels = [Rel2 | Rels]}, SysKeyVals); +decode(#sys{} = Sys, [{Key, Val} | KeyVals]) -> + Sys3 = case Key of root_dir when is_list(Val) -> - {Sys#sys{root_dir = Val}, Status}; + Sys#sys{root_dir = Val}; lib_dirs when is_list(Val) -> - {Sys#sys{lib_dirs = Val}, Status}; + Sys#sys{lib_dirs = Val}; mod_cond when Val =:= all; Val =:= app; Val =:= ebin; Val =:= derived; Val =:= none -> - {Sys#sys{mod_cond = Val}, Status}; + Sys#sys{mod_cond = Val}; incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> - {Sys#sys{incl_cond = Val}, Status}; + Sys#sys{incl_cond = Val}; boot_rel when is_list(Val) -> - {Sys#sys{boot_rel = Val}, Status}; + Sys#sys{boot_rel = Val}; emu_name when is_list(Val) -> - {Sys#sys{emu_name = Val}, Status}; + Sys#sys{emu_name = Val}; profile when Val =:= development; Val =:= embedded; Val =:= standalone -> @@ -1321,198 +1390,153 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals], Status) -> InclApp = reltool_utils:choose_default(incl_app_filters, Val, false), ExclApp = reltool_utils:choose_default(excl_app_filters, Val, false), AppType = reltool_utils:choose_default(embedded_app_type, Val, false), - {Sys#sys{profile = Val, - incl_sys_filters = dec_re(incl_sys_filters, - InclSys, - Sys#sys.incl_sys_filters), - excl_sys_filters = dec_re(excl_sys_filters, - ExclSys, - Sys#sys.excl_sys_filters), - incl_app_filters = dec_re(incl_app_filters, - InclApp, - Sys#sys.incl_app_filters), - excl_app_filters = dec_re(excl_app_filters, - ExclApp, - Sys#sys.excl_app_filters), - embedded_app_type = AppType}, - Status}; + Sys#sys{profile = Val, + incl_sys_filters = dec_re(incl_sys_filters, + InclSys, + Sys#sys.incl_sys_filters), + excl_sys_filters = dec_re(excl_sys_filters, + ExclSys, + Sys#sys.excl_sys_filters), + incl_app_filters = dec_re(incl_app_filters, + InclApp, + Sys#sys.incl_app_filters), + excl_app_filters = dec_re(excl_app_filters, + ExclApp, + Sys#sys.excl_app_filters), + embedded_app_type = AppType}; incl_sys_filters -> - {Sys#sys{incl_sys_filters = - dec_re(Key, - Val, - Sys#sys.incl_sys_filters)}, - Status}; + Sys#sys{incl_sys_filters = + dec_re(Key, Val, Sys#sys.incl_sys_filters)}; excl_sys_filters -> - {Sys#sys{excl_sys_filters = - dec_re(Key, - Val, - Sys#sys.excl_sys_filters)}, - Status}; + Sys#sys{excl_sys_filters = + dec_re(Key, Val, Sys#sys.excl_sys_filters)}; incl_app_filters -> - {Sys#sys{incl_app_filters = - dec_re(Key, - Val, - Sys#sys.incl_app_filters)}, - Status}; + Sys#sys{incl_app_filters = + dec_re(Key, Val, Sys#sys.incl_app_filters)}; excl_app_filters -> - {Sys#sys{excl_app_filters = - dec_re(Key, - Val, - Sys#sys.excl_app_filters)}, - Status}; + Sys#sys{excl_app_filters = + dec_re(Key, Val, Sys#sys.excl_app_filters)}; incl_archive_filters -> - {Sys#sys{incl_archive_filters = - dec_re(Key, - Val, - Sys#sys.incl_archive_filters)}, - Status}; + Sys#sys{incl_archive_filters = + dec_re(Key, Val, Sys#sys.incl_archive_filters)}; excl_archive_filters -> - {Sys#sys{excl_archive_filters = - dec_re(Key, - Val, - Sys#sys.excl_archive_filters)}, - Status}; + Sys#sys{excl_archive_filters = + dec_re(Key, Val, Sys#sys.excl_archive_filters)}; archive_opts when is_list(Val) -> - {Sys#sys{archive_opts = Val}, Status}; + Sys#sys{archive_opts = Val}; relocatable when Val =:= true; Val =:= false -> - {Sys#sys{relocatable = Val}, Status}; + Sys#sys{relocatable = Val}; rel_app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; Val =:= load; Val =:= none -> - {Sys#sys{rel_app_type = Val}, Status}; + Sys#sys{rel_app_type = Val}; embedded_app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; Val =:= load; Val =:= none; Val =:= undefined -> - {Sys#sys{embedded_app_type = Val}, Status}; + Sys#sys{embedded_app_type = Val}; app_file when Val =:= keep; Val =:= strip; Val =:= all -> - {Sys#sys{app_file = Val}, Status}; + Sys#sys{app_file = Val}; debug_info when Val =:= keep; Val =:= strip -> - {Sys#sys{debug_info = Val}, Status}; + Sys#sys{debug_info = Val}; _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {Sys, reltool_utils:return_first_error(Status, - "Illegal option: " ++ - Text)} + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end, - decode(Sys3, KeyVals, Status3); -decode(#app{} = App, [{Key, Val} | KeyVals], Status) -> - {App2, Status2} = + decode(Sys3, KeyVals); +decode(#app{} = App, [{Key, Val} | KeyVals]) -> + App2 = case Key of mod_cond when Val =:= all; Val =:= app; Val =:= ebin; Val =:= derived; Val =:= none -> - {App#app{mod_cond = Val}, Status}; + App#app{mod_cond = Val}; incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> - {App#app{incl_cond = Val}, Status}; + App#app{incl_cond = Val}; debug_info when Val =:= keep; Val =:= strip -> - {App#app{debug_info = Val}, Status}; + App#app{debug_info = Val}; app_file when Val =:= keep; Val =:= strip; Val =:= all -> - {App#app{app_file = Val}, Status}; + App#app{app_file = Val}; app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; Val =:= load; Val =:= none; Val =:= undefined -> - {App#app{app_type = Val}, Status}; + App#app{app_type = Val}; incl_app_filters -> - {App#app{incl_app_filters = - dec_re(Key, - Val, - App#app.incl_app_filters)}, - Status}; + App#app{incl_app_filters = + dec_re(Key, Val, App#app.incl_app_filters)}; excl_app_filters -> - {App#app{excl_app_filters = - dec_re(Key, - Val, - App#app.excl_app_filters)}, - Status}; + App#app{excl_app_filters = + dec_re(Key, Val, App#app.excl_app_filters)}; incl_archive_filters -> - {App#app{incl_archive_filters = - dec_re(Key, - Val, - App#app.incl_archive_filters)}, - Status}; + App#app{incl_archive_filters = + dec_re(Key, Val, App#app.incl_archive_filters)}; excl_archive_filters -> - {App#app{excl_archive_filters = - dec_re(Key, - Val, - App#app.excl_archive_filters)}, - Status}; + App#app{excl_archive_filters = + dec_re(Key, Val, App#app.excl_archive_filters)}; archive_opts when is_list(Val) -> - {App#app{archive_opts = Val}, Status}; + App#app{archive_opts = Val}; vsn when is_list(Val) -> - {App#app{use_selected_vsn = true, vsn = Val}, Status}; + App#app{use_selected_vsn = true, vsn = Val}; _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {App, reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text)} + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end, - decode(App2, KeyVals, Status2); -decode(#app{mods = Mods} = App, [{mod, Name, ModKeyVals} | AppKeyVals], - Status) -> - {Mod, Status2} = decode(#mod{name = Name}, ModKeyVals, Status), - decode(App#app{mods = [Mod | Mods]}, AppKeyVals, Status2); -decode(#mod{} = Mod, [{Key, Val} | KeyVals], Status) -> - {Mod2, Status2} = + decode(App2, KeyVals); +decode(#app{mods = Mods} = App, [{mod, Name, ModKeyVals} | AppKeyVals]) -> + Mod = decode(#mod{name = Name}, ModKeyVals), + decode(App#app{mods = [Mod | Mods]}, AppKeyVals); +decode(#mod{} = Mod, [{Key, Val} | KeyVals]) -> + Mod2 = case Key of incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> - {Mod#mod{incl_cond = Val}, Status}; + Mod#mod{incl_cond = Val}; debug_info when Val =:= keep; Val =:= strip -> - {Mod#mod{debug_info = Val}, Status}; + Mod#mod{debug_info = Val}; _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {Mod, - reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text)} + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end, - decode(Mod2, KeyVals, Status2); -decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals], Status) -> + decode(Mod2, KeyVals); +decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals]) -> {ValidTypesAssigned, RA} = case RelApp of Name when is_atom(Name) -> {true, #rel_app{name = Name}}; - {Name, Type} when is_atom(Name) -> - {is_type(Type), #rel_app{name = Name, app_type = Type}}; {Name, InclApps} when is_atom(Name), is_list(InclApps) -> VI = lists:all(fun erlang:is_atom/1, InclApps), {VI, #rel_app{name = Name, incl_apps = InclApps}}; + {Name, Type} when is_atom(Name) -> + {is_type(Type), #rel_app{name = Name, app_type = Type}}; {Name, Type, InclApps} when is_atom(Name), is_list(InclApps) -> VT = is_type(Type), VI = lists:all(fun erlang:is_atom/1, InclApps), {VT andalso VI, #rel_app{name = Name, app_type = Type, incl_apps = InclApps}}; _ -> - {false, #rel_app{incl_apps = []}} + {false, #rel_app{}} end, case ValidTypesAssigned of true -> - decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals, Status); + decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals); false -> - Text = lists:flatten(io_lib:format("~p", [RelApp])), - Status2 = - reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text), - decode(Rel, KeyVals, Status2) + reltool_utils:throw_error("Illegal option: ~p", [RelApp]) end; -decode(Acc, [], Status) -> - {Acc, Status}; -decode(Acc, KeyVal, Status) -> - Text = lists:flatten(io_lib:format("~p", [KeyVal])), - {Acc, reltool_utils:return_first_error(Status, "Illegal option: " ++ Text)}. +decode(Acc, []) -> + Acc; +decode(_Acc, KeyVal) -> + reltool_utils:throw_error("Illegal option: ~p", [KeyVal]). is_type(Type) -> case Type of @@ -1529,79 +1553,50 @@ split_escript_name(File) when is_list(File) -> Label = filename:basename(File, ".escript"), {list_to_atom("*escript* " ++ Label), Label}. +default_escript_app(File) -> + {Name, Label} = split_escript_name(File), + App = default_app(Name, File), + App#app{is_escript = true, + label = Label, + info = missing_app_info("")}. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -refresh(#state{sys = Sys} = S, Force, Status) -> - {Sys2, Status2} = merge_config(Sys, Sys#sys{apps = []}, Force, Status), - {S#state{sys = Sys2}, Status2}. - -merge_config(OldSys, NewSys, Force, Status) -> - RootDir = filename:absname(NewSys#sys.root_dir), - LibDirs = [filename:absname(D) || D <- NewSys#sys.lib_dirs], - Escripts = [filename:absname(E) || E <- NewSys#sys.escripts], - {SourceDirs, Status2} = - libs_to_dirs(RootDir, LibDirs, Status), - MergedApps = merge_app_dirs(SourceDirs, NewSys#sys.apps, OldSys#sys.apps), - {AllApps, Status3} = - escripts_to_apps(Escripts, MergedApps, OldSys#sys.apps, Status2), - {RefreshedApps, Status4} = - refresh_apps(OldSys#sys.apps, AllApps, [], Force, Status3), - {PatchedApps, Status5} = - patch_erts_version(RootDir, RefreshedApps, Status4), - Escripts2 = [A#app.active_dir || A <- PatchedApps, A#app.is_escript], - NewSys2 = NewSys#sys{root_dir = RootDir, - lib_dirs = LibDirs, - escripts = Escripts2, - apps = PatchedApps}, - {NewSys2, Status5}. +%% Apps is a list of #app records - sorted on #app.name - containing +%% only the apps that have specific configuration (e.g. in the config +%% file) +refresh(#state{sys=Sys} = S) -> + RootDir = filename:absname(Sys#sys.root_dir), + LibDirs = [filename:absname(D) || D <- Sys#sys.lib_dirs], + Escripts = [filename:absname(E) || E <- Sys#sys.escripts], -verify_config(RelApps, #sys{boot_rel = BootRel, rels = Rels, apps = Apps}, Status) -> - case lists:keymember(BootRel, #rel.name, Rels) of - true -> - Status2 = lists:foldl(fun(RA, Acc) -> - check_app(RA, Apps, Acc) end, - Status, - RelApps), - lists:foldl(fun(#rel{name = RelName}, Acc)-> - check_rel(RelName, RelApps, Acc) - end, - Status2, - Rels); - false -> - Text = lists:concat(["Release ", BootRel, - " is mandatory (used as boot_rel)"]), - reltool_utils:return_first_error(Status, Text) - end. + %% Read all lib dirs and return sorted [{AppName,Dir}] + SourceDirs = libs_to_dirs(RootDir, LibDirs), -check_app({RelName, AppName}, Apps, Status) -> - case lists:keysearch(AppName, #app.name, Apps) of - {value, App} when App#app.is_pre_included -> - Status; - {value, App} when App#app.is_included -> - Status; - _ -> - Text = lists:concat(["Release ", RelName, - " uses non included application ", - AppName]), - reltool_utils:return_first_error(Status, Text) - end. + %% Create #app records for all apps in SourceDirs, and merge with + %% list of apps from config. + MergedApps = merge_app_dirs(SourceDirs, Sys#sys.apps), -check_rel(RelName, RelApps, Status) -> - EnsureApp = - fun(AppName, Acc) -> - case lists:member({RelName, AppName}, RelApps) of - true -> - Acc; - false -> - Text = lists:concat(["Mandatory application ", - AppName, - " is not included in release ", - RelName]), - reltool_utils:return_first_error(Acc, Text) - end - end, - Mandatory = [kernel, stdlib], - lists:foldl(EnsureApp, Status, Mandatory). + %% For each escript, find all related files and convert to #app + %% and #mod records + {AllApps, Status2} = escripts_to_apps(Escripts, MergedApps, {ok,[]}), + + %% Make sure correct version of each application is used according + %% to the user configuration. + %% Then find all modules and their dependencies and set user + %% configuration per module if it exists. + {RefreshedApps, Status3} = refresh_apps(Sys#sys.apps, AllApps, [], + true, Status2), + + %% Make sure erts exists in app list and has a version (or warn) + {PatchedApps, Status4} = patch_erts_version(RootDir, RefreshedApps, Status3), + + %% Update #sys and return + Escripts2 = [A#app.active_dir || A <- PatchedApps, A#app.is_escript], + Sys2 = Sys#sys{root_dir = RootDir, + lib_dirs = LibDirs, + escripts = Escripts2}, + {S#state{sys=Sys2}, PatchedApps, Status4}. patch_erts_version(RootDir, Apps, Status) -> AppName = erts, @@ -1615,18 +1610,17 @@ patch_erts_version(RootDir, Apps, Status) -> Apps2 = lists:keystore(AppName, #app.name, Apps, Erts2), {Apps2, Status}; Vsn =:= "" -> - {Apps, reltool_utils:add_warning(Status, - "erts has no version")}; + {Apps, reltool_utils:add_warning("erts has no version",[], + Status)}; true -> {Apps, Status} end; false -> - Text = "erts cannot be found in the root directory " ++ RootDir, - Status2 = reltool_utils:return_first_error(Status, Text), - {Apps, Status2} + reltool_utils:throw_error( + "erts cannot be found in the root directory ~p", [RootDir]) end. -libs_to_dirs(RootDir, LibDirs, Status) -> +libs_to_dirs(RootDir, LibDirs) -> case file:list_dir(RootDir) of {ok, RootFiles} -> RootLibDir = filename:join([RootDir, "lib"]), @@ -1648,21 +1642,16 @@ libs_to_dirs(RootDir, LibDirs, Status) -> end, ErtsFiles = [{erts, Fun(F)} || F <- RootFiles, lists:prefix("erts", F)], - app_dirs2(AllLibDirs, [ErtsFiles], Status); + app_dirs2(AllLibDirs, [ErtsFiles]); [Duplicate | _] -> - {[], - reltool_utils:return_first_error(Status, - "Duplicate library: " ++ - Duplicate)} + reltool_utils:throw_error("Duplicate library: ~p",[Duplicate]) end; {error, Reason} -> - Text = file:format_error(Reason), - {[], reltool_utils:return_first_error(Status, - "Missing root library " ++ - RootDir ++ ": " ++ Text)} + reltool_utils:throw_error("Missing root library ~p: ~s", + [RootDir,file:format_error(Reason)]) end. -app_dirs2([Lib | Libs], Acc, Status) -> +app_dirs2([Lib | Libs], Acc) -> case file:list_dir(Lib) of {ok, Files} -> Filter = @@ -1682,17 +1671,15 @@ app_dirs2([Lib | Libs], Acc, Status) -> end end, Files2 = lists:zf(Filter, Files), - app_dirs2(Libs, [Files2 | Acc], Status); + app_dirs2(Libs, [Files2 | Acc]); {error, Reason} -> - Text = file:format_error(Reason), - {[], reltool_utils:return_first_error(Status, - "Illegal library " ++ - Lib ++ ": " ++ Text)} + reltool_utils:throw_error("Illegal library ~p: ~s", + [Lib, file:format_error(Reason)]) end; -app_dirs2([], Acc, Status) -> - {lists:sort(lists:append(Acc)), Status}. +app_dirs2([], Acc) -> + lists:sort(lists:append(Acc)). -escripts_to_apps([Escript | Escripts], Apps, OldApps, Status) -> +escripts_to_apps([Escript | Escripts], Apps, Status) -> {EscriptAppName, _Label} = split_escript_name(Escript), Ext = code:objfile_extension(), Fun = fun(FullName, _GetInfo, GetBin, {FileAcc, StatusAcc}) -> @@ -1755,156 +1742,111 @@ escripts_to_apps([Escript | Escripts], Apps, OldApps, Status) -> end, case reltool_utils:escript_foldl(Fun, {[], Status}, Escript) of {ok, {Files, Status2}} -> + EscriptApp = + case lists:keyfind(EscriptAppName,#app.name,Apps) of + false -> default_escript_app(Escript); + EA -> EA + end, {Apps2, Status3} = - files_to_apps(Escript, - lists:sort(Files), - Apps, - Apps, - OldApps, - Status2), - escripts_to_apps(Escripts, Apps2, OldApps, Status3); + escript_files_to_apps(EscriptAppName, + lists:sort(Files), + [EscriptApp], + Apps, + Status2), + escripts_to_apps(Escripts, Apps2, Status3); {error, Reason} -> - Text = lists:flatten(io_lib:format("~p", [Reason])), - {[], reltool_utils:return_first_error(Status, - "Illegal escript " ++ - Escript ++ ": " ++ Text)} + reltool_utils:throw_error("Illegal escript ~p: ~p", [Escript,Reason]) end; -escripts_to_apps([], Apps, _OldApps, Status) -> +escripts_to_apps([], Apps, Status) -> {Apps, Status}. %% Assume that all files for an app are in consecutive order %% Assume the app info is before the mods -files_to_apps(Escript, - [{AppName, Type, Dir, ModOrInfo} | Files] = AllFiles, - Acc, - Apps, - OldApps, - Status) -> - case Type of - mod -> - case Acc of - [] -> - Info = missing_app_info(""), - {NewApp, Status2} = - merge_escript_app(AppName, - Dir, - Info, - [ModOrInfo], - Apps, - OldApps, - Status), - files_to_apps(Escript, - AllFiles, - [NewApp | Acc], - Apps, - OldApps, Status2); - [App | Acc2] when App#app.name =:= ModOrInfo#mod.app_name -> - App2 = App#app{mods = [ModOrInfo | App#app.mods]}, - files_to_apps(Escript, - Files, - [App2 | Acc2], - Apps, - OldApps, - Status); - [App | Acc2] -> - PrevApp = App#app{mods = lists:keysort(#mod.name, - App#app.mods)}, - Info = missing_app_info(""), - {NewApp, Status2} = - merge_escript_app(AppName, - Dir, - Info, - [ModOrInfo], - Apps, - OldApps, - Status), - files_to_apps(Escript, - Files, - [NewApp, PrevApp | Acc2], - Apps, - OldApps, - Status2) - end; - app -> - {App, Status2} = - merge_escript_app(AppName, Dir, ModOrInfo, [], Apps, OldApps, - Status), - files_to_apps(Escript, Files, [App | Acc], Apps, OldApps, Status2) - end; -files_to_apps(_Escript, [], Acc, _Apps, _OldApps, Status) -> - {lists:keysort(#app.name, Acc), Status}. - -merge_escript_app(AppName, Dir, Info, Mods, Apps, OldApps, Status) -> - App1 = case lists:keyfind(AppName, #app.name, OldApps) of - #app{} = App -> - App; - false -> - default_app(AppName, Dir) - end, - App2 = App1#app{is_escript = true, +escript_files_to_apps(EscriptAppName, + [{AppName, Type, Dir, ModOrInfo} | Files], + Acc, + Apps, + Status) -> + {NewAcc,Status3} = + case Type of + mod -> + case Acc of + [App | Acc2] when App#app.name =:= ModOrInfo#mod.app_name -> + Mods = lists:ukeymerge(#mod.name, + [ModOrInfo], + App#app.mods), + {[App#app{mods = Mods} | Acc2], Status}; + Acc -> + {NewApp, Status2} = init_escript_app(AppName, + EscriptAppName, + Dir, + missing_app_info(""), + [ModOrInfo], + Apps, + Status), + {[NewApp | Acc], Status2} + end; + app -> + {App, Status2} = init_escript_app(AppName, + EscriptAppName, + Dir, + ModOrInfo, + [], + Apps, + Status), + {[App | Acc], Status2} + end, + escript_files_to_apps(EscriptAppName, Files, NewAcc, Apps, Status3); +escript_files_to_apps(_EscriptAppName, [], Acc, Apps, Status) -> + {lists:ukeymerge(#app.name, lists:reverse(Acc), Apps), Status}. + +init_escript_app(AppName, EscriptAppName, Dir, Info, Mods, Apps, Status) -> + App1 = default_app(AppName, Dir), + IsEscript = + if AppName=:=EscriptAppName -> true; + true -> {inlined, EscriptAppName} + end, + InclCond = (lists:keyfind(EscriptAppName,#app.name,Apps))#app.incl_cond, + App2 = App1#app{is_escript = IsEscript, label = filename:basename(Dir, ".escript"), info = Info, mods = Mods, active_dir = Dir, - sorted_dirs = [Dir]}, + sorted_dirs = [Dir], + incl_cond = InclCond},% inlined apps inherit incl from escript case lists:keymember(AppName, #app.name, Apps) of true -> - Error = lists:concat([AppName, ": Application name clash. ", - "Escript ", Dir," contains application ", - AppName, "."]), - {App2, reltool_utils:return_first_error(Status, Error)}; + reltool_utils:throw_error( + "~p: Application name clash. Escript ~p contains application ~p.", + [AppName,Dir,AppName]); false -> {App2, Status} end. -merge_app_dirs([{Name, Dir} | Rest], [App | Apps], OldApps) - when App#app.name =:= Name -> - %% Add new dir to app - App2 = App#app{sorted_dirs = [Dir | App#app.sorted_dirs]}, - merge_app_dirs(Rest, [App2 | Apps], OldApps); -merge_app_dirs([{Name, Dir} | Rest], Apps, OldApps) -> - %% Initate app - Apps2 = sort_app_dirs(Apps), - Apps4 = +merge_app_dirs([{Name, Dir} | Rest], Apps) -> + App = case lists:keyfind(Name, #app.name, Apps) of false -> - case lists:keyfind(Name, #app.name, OldApps) of - false -> - App = default_app(Name, Dir), - [App | Apps2]; - #app{active_dir = Dir} = OldApp -> - [OldApp | Apps2]; - OldApp -> - App = - case filter_app(OldApp) of - {true, NewApp} -> - NewApp#app{active_dir = Dir, - sorted_dirs = [Dir]}; - false -> - default_app(Name, Dir) - end, - [App | Apps2] - end; + default_app(Name, Dir); OldApp -> - Apps3 = lists:keydelete(Name, #app.name, Apps2), - App = OldApp#app{sorted_dirs = [Dir | OldApp#app.sorted_dirs]}, - [App | Apps3] + SortedDirs = lists:umerge(fun reltool_utils:app_dir_test/2, + [Dir], OldApp#app.sorted_dirs), + OldApp#app{sorted_dirs = SortedDirs} end, - merge_app_dirs(Rest, Apps4, OldApps); -merge_app_dirs([], Apps, _OldApps) -> - Apps2 = sort_app_dirs(Apps), - lists:reverse(Apps2). - -sort_app_dirs([#app{sorted_dirs = Dirs} = App | Acc]) -> - SortedDirs = lists:sort(fun reltool_utils:app_dir_test/2, Dirs), - case SortedDirs of - [ActiveDir | _] -> ok; - [] -> ActiveDir = undefined - end, - [App#app{active_dir = ActiveDir, sorted_dirs = SortedDirs} | Acc]; -sort_app_dirs([]) -> + Apps2 = lists:ukeymerge(#app.name, [App], Apps), + merge_app_dirs(Rest, Apps2); +merge_app_dirs([], Apps) -> + set_active_dirs(Apps). + +%% First dir, i.e. the one with highest version, is set to active dir +set_active_dirs([#app{sorted_dirs = [ActiveDir|_]} = App | Apps]) -> + [App#app{active_dir = ActiveDir} | set_active_dirs(Apps)]; +set_active_dirs([#app{sorted_dirs = []} = App | Apps]) -> + [App#app{active_dir = undefined} | set_active_dirs(Apps)]; +set_active_dirs([]) -> []. + default_app(Name, Dir) -> App = default_app(Name), App#app{active_dir = Dir, @@ -1913,89 +1855,51 @@ default_app(Name, Dir) -> default_app(Name) -> #app{name = Name, is_escript = false, - use_selected_vsn = undefined, - active_dir = undefined, sorted_dirs = [], - vsn = undefined, - label = undefined, - info = undefined, mods = [], - - mod_cond = undefined, - incl_cond = undefined, - - status = missing, - uses_mods = undefined, - is_pre_included = undefined, - is_included = undefined, - rels = undefined}. - -%% Assume that the application are sorted -refresh_apps([Old | OldApps], [New | NewApps], Acc, Force, Status) - when New#app.name =:= Old#app.name -> - {Info, ActiveDir, Status2} = ensure_app_info(New, Status), - OptLabel = - case Info#app_info.vsn =:= New#app.vsn of - true -> New#app.label; - false -> undefined % Cause refresh - end, - {Refreshed, Status3} = - refresh_app(New#app{label = OptLabel, - active_dir = ActiveDir, - vsn = Info#app_info.vsn, - info = Info}, - Force, - Status2), - refresh_apps(OldApps, NewApps, [Refreshed | Acc], Force, Status3); -refresh_apps([Old | OldApps], [New | NewApps], Acc, Force, Status) - when New#app.name < Old#app.name -> - %% No old app version exists. Use new as is. - %% BUGBUG: Issue warning if the active_dir is not defined - {New2, Status2} = refresh_app(New, Force, Status), - refresh_apps([Old | OldApps], NewApps, [New2 | Acc], Force, Status2); -refresh_apps([Old | OldApps], [New | NewApps], Acc, Force, Status) - when New#app.name > Old#app.name -> - %% No new version. Remove the old. - Status2 = - case Old#app.name =:= ?MISSING_APP_NAME of - true -> - Status; - false -> - Warning = - lists:concat([Old#app.name, - ": The source dirs does not ", - "contain the application anymore."]), - reltool_utils:add_warning(Status, Warning) - end, - refresh_apps(OldApps, [New | NewApps], Acc, Force, Status2); -refresh_apps([], [New | NewApps], Acc, Force, Status) -> - %% No old app version exists. Use new as is. - {New2, Status2} = refresh_app(New, Force, Status), - refresh_apps([], NewApps, [New2 | Acc], Force, Status2); -refresh_apps([Old | OldApps], [], Acc, Force, Status) -> - %% No new version. Remove the old. - Status2 = - case Old#app.name =:= ?MISSING_APP_NAME of - true -> - Status; - false -> - Warning = - lists:concat([Old#app.name, - ": The source dirs does not " - "contain the application anymore."]), - reltool_utils:add_warning(Status, Warning) - end, - refresh_apps(OldApps, [], Acc, Force, Status2); -refresh_apps([], [], Acc, _Force, Status) -> + status = missing}. + + + +refresh_apps(ConfigApps, [New | NewApps], Acc, Force, Status) -> + {New2, Status3} = + case lists:keymember(New#app.name,#app.name,ConfigApps) of + true -> + %% There is user defined config for this application, make + %% sure that the application exists and that correct + %% version is used. Set active directory. + {Info, ActiveDir, Status2} = ensure_app_info(New, Status), + OptLabel = + case Info#app_info.vsn =:= New#app.vsn of + true -> New#app.label; + false -> undefined % Cause refresh + end, + refresh_app(New#app{label = OptLabel, + active_dir = ActiveDir, + vsn = Info#app_info.vsn, + info = Info}, + Force, + Status2); + false -> + %% There is no user defined config for this + %% application. This means that the app is found in the + %% lib dirs, and that the highest version shall be + %% used. I.e. the active_dir and vsn are already correct + %% from merge_app_dirs. + refresh_app(New, Force, Status) + end, + refresh_apps(ConfigApps, NewApps, [New2 | Acc], Force, Status3); +refresh_apps(_ConfigApps, [], Acc, _Force, Status) -> {lists:reverse(Acc), Status}. -ensure_app_info(#app{is_escript = true, active_dir = Dir, info = Info}, - Status) -> + +ensure_app_info(#app{is_escript = IsEscript, active_dir = Dir, info = Info}, + Status) + when IsEscript=/=false -> + %% Escript or application which is inlined in an escript {Info, Dir, Status}; -ensure_app_info(#app{name = Name, sorted_dirs = []}, Status) -> - Error = lists:concat([Name, ": Missing application directory."]), - Status2 = reltool_utils:return_first_error(Status, Error), - {missing_app_info(""), undefined, Status2}; +ensure_app_info(#app{name = Name, sorted_dirs = []}, _Status) -> + reltool_utils:throw_error("~p: : Missing application directory.",[Name]); ensure_app_info(#app{name = Name, vsn = Vsn, sorted_dirs = Dirs, @@ -2017,11 +1921,10 @@ ensure_app_info(#app{name = Name, %% No redundant info Status2; [BadVsn | _] -> - Error2 = - lists:concat([Name, ": Application version clash. ", - "Multiple directories contains version \"", - BadVsn, "\"."]), - reltool_utils:return_first_error(Status2, Error2) + reltool_utils:throw_error( + "~p: Application version clash. " + "Multiple directories contains version ~p.", + [Name,BadVsn]) end, FirstInfo = hd(AllInfo), FirstDir = hd(Dirs), @@ -2035,13 +1938,9 @@ ensure_app_info(#app{name = Name, {Info, VsnDir} -> {Info, VsnDir, Status3}; false -> - Error3 = - lists:concat([Name, - ": No application directory contains ", - "selected version \"", - Vsn, "\"."]), - Status4 = reltool_utils:return_first_error(Status3, Error3), - {FirstInfo, FirstDir, Status4} + reltool_utils:throw_error( + "~p: No application directory contains selected version ~p", + [Name,Vsn]) end end; ensure_app_info(#app{active_dir = Dir, info = Info}, Status) -> @@ -2067,6 +1966,54 @@ get_base(Name, Dir) -> filename:basename(Dir) end. +sys_all_apps(#state{app_tab=AppTab, sys=Sys}) -> + Sys#sys{apps = ets:match_object(AppTab,'_')}. + +config_and_refresh(OldS, Fun) -> + try + S = Fun(), + {S2, Apps, Status2} = refresh(S), + %% Analyse will write to app_tab and mod_tab, so we first + %% backup these tables and clear them + Backup = backup(OldS), + try + Status3 = analyse(S2, Apps, Status2), + S3 = save_old(OldS, S2, Backup, Status3), + {S3, Status3} + catch throw:{error,_} = Error1 -> + restore(Backup,OldS), + throw(Error1) + end + catch throw:{error,_} = Error2 -> + {OldS, Error2} + end. + + +backup(S) -> + Apps = ets:tab2list(S#state.app_tab), + Mods = ets:tab2list(S#state.mod_tab), + ets:delete_all_objects(S#state.app_tab), + ets:delete_all_objects(S#state.mod_tab), + ets:delete_all_objects(S#state.mod_used_by_tab), %tmp tab, no backup needed + {Apps,Mods}. + +restore({Apps,Mods}, S) -> + insert_all(S#state.app_tab,Apps), + insert_all(S#state.mod_tab,Mods). + +save_old(#state{status=OldStatus,sys=OldSys},NewS,{OldApps,OldMods},NewStatus) -> + ets:delete_all_objects(NewS#state.old_app_tab), + ets:delete_all_objects(NewS#state.old_mod_tab), + insert_all(NewS#state.old_app_tab,OldApps), + insert_all(NewS#state.old_mod_tab,OldMods), + NewS#state{old_sys=OldSys, + old_status=OldStatus, + status=NewStatus}. + +insert_all(Tab,Items) -> + lists:foreach(fun(Item) -> ets:insert(Tab,Item) end, Items). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sys callbacks diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index 8b0f64eb45..0c0b295db1 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -1,7 +1,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 @@ -56,7 +56,9 @@ derived, fgraph_wins, app_box, - mod_box + mod_box, + warning_list, + warning_wins }). -define(WIN_WIDTH, 800). @@ -88,6 +90,10 @@ -define(blacklist, "Excluded"). -define(derived, "Derived"). +-define(WARNING_COL, 0). +-define(DEFAULT_WARNING_TIP, "Warnings are listed in this window"). +-define(WARNING_POPUP_SIZE, {400,150}). + -define(safe_config,{sys,[{incl_cond,exclude}, {app,kernel,[{incl_cond,include}]}, {app,stdlib,[{incl_cond,include}]}, @@ -141,48 +147,44 @@ do_init([{safe_config, Safe}, {parent, Parent} | Options]) -> wx:debug(C#common.wx_debug), %% wx_misc:beginBusyCursor(), - case reltool_server:get_status(ServerPid) of - {ok, Warnings} -> - exit_dialog(Warnings), - {ok, Sys} = reltool_server:get_sys(ServerPid), - S = #state{parent_pid = Parent, - server_pid = ServerPid, - common = C, - config_file = filename:absname("config.reltool"), - target_dir = filename:absname("reltool_target_dir"), - app_wins = [], - sys = Sys, - fgraph_wins = []}, - S2 = create_window(S), - S5 = wx:batch(fun() -> - Title = atom_to_list(?APPLICATION), - wxFrame:setTitle(S2#state.frame, - Title), - %% wxFrame:setMinSize(Frame, - %% {?WIN_WIDTH, ?WIN_HEIGHT}), - wxStatusBar:setStatusText( - S2#state.status_bar, - "Done."), - S3 = redraw_apps(S2), - S4 = redraw_libs(S3), - redraw_config_page(S4) - end), - %% wx_misc:endBusyCursor(), - %% wxFrame:destroy(Frame), - proc_lib:init_ack(S#state.parent_pid, {ok, self()}), - loop(S5); - {error, Reason} -> - restart_server_safe_config(Safe,Parent,Reason) - end; + {ok, Warnings} = reltool_server:get_status(ServerPid), + exit_dialog(Warnings), + S = #state{parent_pid = Parent, + server_pid = ServerPid, + common = C, + config_file = filename:absname("config.reltool"), + target_dir = filename:absname("reltool_target_dir"), + app_wins = [], + sys = Sys, + fgraph_wins = [], + warning_wins = []}, + S2 = create_window(S), + S5 = wx:batch(fun() -> + Title = atom_to_list(?APPLICATION), + wxFrame:setTitle(S2#state.frame, + Title), + %% wxFrame:setMinSize(Frame, + %% {?WIN_WIDTH, ?WIN_HEIGHT}), + wxStatusBar:setStatusText( + S2#state.status_bar, + "Done."), + S3 = redraw_apps(S2), + S4 = redraw_libs(S3), + redraw_config_page(S4) + end), + %% wx_misc:endBusyCursor(), + %% wxFrame:destroy(Frame), + proc_lib:init_ack(S#state.parent_pid, {ok, self()}), + loop(S5); {error, Reason} -> - io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), - exit(Reason) + restart_server_safe_config(Safe,Parent,Reason) end. -restart_server_safe_config(true,_Parent,Reason) -> +restart_server_safe_config(true,Parent,Reason) -> io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), - exit(Reason); + proc_lib:init_ack(Parent, {error,Reason}); restart_server_safe_config(false,Parent,Reason) -> + wx:new(), Strings = [{?wxBLACK,"Could not start reltool server:\n\n"}, {?wxRED,Reason++"\n\n"}, @@ -197,7 +199,7 @@ restart_server_safe_config(false,Parent,Reason) -> do_init([{safe_config,true},{parent,Parent},?safe_config]); ?wxID_CANCEL -> io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), - exit(Reason) + proc_lib:init_ack(Parent,{error,Reason}) end. exit_dialog([]) -> @@ -240,10 +242,18 @@ loop(S) -> lists:keydelete(ObjRef, #fgraph_win.frame, FWs), ?MODULE:loop(S#state{fgraph_wins = FWs2}); false -> - error_logger:format("~p~p got unexpected " - "message:\n\t~p\n", - [?MODULE, self(), Msg]), - ?MODULE:loop(S) + WWs = S#state.warning_wins, + case lists:member(ObjRef, WWs) of + true -> + wxFrame:destroy(ObjRef), + WWs2 = lists:delete(ObjRef, WWs), + ?MODULE:loop(S#state{warning_wins = WWs2}); + false -> + error_logger:format("~p~p got unexpected " + "message:\n\t~p\n", + [?MODULE, self(), Msg]), + ?MODULE:loop(S) + end end end; #wx{id = ?CLOSE_ITEM, @@ -297,8 +307,8 @@ handle_child_exit({'EXIT', Pid, _Reason} = Exit, FWs, AWs) -> msg_warning(Exit, application_window), {FWs, lists:keydelete(Pid, #app_win.pid, AWs)}; false -> - msg_warning(Exit, unknown), - {FWs, AWs} + msg_warning(Exit, unknown), + {FWs, AWs} end end. @@ -340,8 +350,12 @@ create_window(S) -> fun create_main_release_page/1, fun create_config_page/1 ]), + + S4 = create_warning_list(S3), + Sizer = wxBoxSizer:new(?wxVERTICAL), wxSizer:add(Sizer, Book, [{flag, ?wxEXPAND}, {proportion, 1}]), + wxSizer:add(Sizer, S4#state.warning_list, [{flag, ?wxEXPAND}]), wxPanel:setSizer(Panel, Sizer), wxSizer:fit(Sizer, Frame), @@ -349,7 +363,7 @@ create_window(S) -> wxFrame:connect(Frame, close_window), wxFrame:show(Frame), - S3. + S4. create_menubar(Frame) -> MenuBar = wxMenuBar:new(), @@ -444,6 +458,7 @@ create_app_list_ctrl(Panel, OuterSz, Title, Tick, Cross) -> ListItem = wxListItem:new(), wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT), wxListItem:setText(ListItem, Title), + wxListItem:setWidth(ListItem, reltool_utils:get_column_width(ListCtrl)), wxListCtrl:insertColumn(ListCtrl, ?APPS_APP_COL, ListItem), wxListItem:destroy(ListItem), @@ -642,6 +657,49 @@ redraw_config_page(#state{sys = Sys, app_box = AppBox, mod_box = ModBox} = S) -> wxRadioBox:setSelection(ModBox, ModChoice), S. +create_warning_list(#state{panel = Panel} = S) -> + ListCtrl = wxListCtrl:new(Panel, + [{style, + ?wxLC_REPORT bor + ?wxLC_HRULES bor + ?wxVSCROLL}, + {size, {?WIN_WIDTH,80}}]), + reltool_utils:assign_image_list(ListCtrl), + wxListCtrl:insertColumn(ListCtrl, ?WARNING_COL, "Warnings", + [{format,?wxLIST_FORMAT_LEFT}, + {width,reltool_utils:get_column_width(ListCtrl)}]), + wxListCtrl:setToolTip(ListCtrl, ?DEFAULT_WARNING_TIP), + wxEvtHandler:connect(ListCtrl, size, + [{skip, true}, {userData, warnings}]), + wxEvtHandler:connect(ListCtrl, command_list_item_activated, + [{userData, warnings}]), + wxEvtHandler:connect(ListCtrl, motion, [{userData, warnings}]), + wxEvtHandler:connect(ListCtrl, enter_window), + S#state{warning_list=ListCtrl}. + +redraw_warnings(S) -> + {ok,Warnings} = reltool_server:get_status(S#state.server_pid), + redraw_warnings(S#state.warning_list,Warnings), + length(Warnings). + +redraw_warnings(ListCtrl, []) -> + wxListCtrl:deleteAllItems(ListCtrl), + ok; +redraw_warnings(ListCtrl, Warnings) -> + wxListCtrl:deleteAllItems(ListCtrl), + Show = fun(Warning, Row) -> + wxListCtrl:insertItem(ListCtrl, Row, ""), + wxListCtrl:setItem(ListCtrl, + Row, + ?WARNING_COL, + Warning, + [{imageId, ?WARN_IMAGE}]), + Row + 1 + end, + wx:foldl(Show, 0, Warnings), + ok. + + create_main_release_page(#state{book = Book} = S) -> Panel = wxPanel:new(Book, []), RelBook = wxNotebook:new(Panel, ?wxID_ANY, []), @@ -783,6 +841,9 @@ escript_popup(S, File, Tree, Item) -> handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = _Wx) -> %% io:format("wx: ~p\n", [Wx]), case Event of + _ when UserData =:= warnings; + is_tuple(UserData), element(1,UserData)=:=warning -> + handle_warning_event(S, ObjRef, UserData, Event); #wxSize{type = size, size = {W, _H}} when UserData =:= app_list_ctrl -> wxListCtrl:setColumnWidth(ObjRef, ?APPS_APP_COL, W), S; @@ -848,7 +909,9 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = when S#state.popup_menu =/= undefined -> handle_popup_event(S, Type, Id, ObjRef, UserData, Str); #wxMouse{type = enter_window} -> - wxWindow:setFocus(ObjRef), + %% The following is commented out because it raises the + %% main system window on top of popup windows. + %% wxWindow:setFocus(ObjRef), S; _ -> case wxNotebook:getPageText(S#state.book, @@ -860,6 +923,110 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = end end. +handle_warning_event(S, ObjRef, _, #wxSize{type = size}) -> + ColumnWidth = reltool_utils:get_column_width(ObjRef), + wxListCtrl:setColumnWidth(ObjRef, ?WARNING_COL, ColumnWidth), + S; +handle_warning_event(S, ObjRef, _, #wxMouse{type = motion, x=X, y=Y}) -> + Pos = reltool_utils:wait_for_stop_motion(ObjRef, {X,Y}), + warning_list_set_tool_tip(os:type(),ObjRef,Pos), + S; +handle_warning_event(S, ObjRef, _, #wxList{type = command_list_item_activated, + itemIndex = Pos}) -> + Text = wxListCtrl:getItemText(ObjRef, Pos), + S#state{warning_wins = [display_warning(S,Text) | S#state.warning_wins]}; +handle_warning_event(S, _ObjRef, {warning,Frame}, + #wxCommand{type = command_button_clicked}) -> + wxFrame:destroy(Frame), + S#state{warning_wins = lists:delete(Frame,S#state.warning_wins)}. + +warning_list_set_tool_tip({win32,_},ListCtrl,{_X,Y}) -> + case win_find_item(ListCtrl,Y,0) of + -1 -> + wxListCtrl:setToolTip(ListCtrl,?DEFAULT_WARNING_TIP); + _Index -> + %% The following is commented out because there seems to + %% be an utomatic tooltip under Windows that shows the + %% expanded list item in case it is truncated because it + %% is too long for column width. + %% Tip = + %% case wxListCtrl:getItemText(ListCtrl,Index) of + %% "" -> + %% ?DEFAULT_WARNING_TIP; + %% Text -> + %% "WARNING:\n" ++ Text + %% end, + %% wxListCtrl:setToolTip(ListCtrl,Tip), + ok + end; +warning_list_set_tool_tip(_,ListCtrl,Pos) -> + case wxListCtrl:findItem(ListCtrl,-1,Pos,0) of + Index when Index >= 0 -> + Tip = + case wxListCtrl:getItemText(ListCtrl,Index) of + "" -> + ?DEFAULT_WARNING_TIP; + Text -> + "WARNING:\n" ++ Text + end, + wxListCtrl:setToolTip(ListCtrl, Tip); + _ -> + ok + end. + +win_find_item(ListCtrl,YPos,Index) -> + case wxListCtrl:getItemRect(ListCtrl,Index) of + {true,{_,Y,_,H}} when YPos>=Y, YPos=<Y+H -> + Index; + {true,_} -> + win_find_item(ListCtrl,YPos,Index+1); + {false,_} -> + -1 + end. + +display_warning(S,Warning) -> + Pos = warning_popup_position(S,?WARNING_POPUP_SIZE), + Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Warning",[{pos,Pos}]), + Panel = wxPanel:new(Frame, []), + TextStyle = ?wxTE_READONLY bor ?wxTE_WORDWRAP bor ?wxTE_MULTILINE, + Text = wxTextCtrl:new(Panel, ?wxID_ANY, [{value, Warning}, + {style, TextStyle}, + {size, ?WARNING_POPUP_SIZE}]), + Attr = wxTextAttr:new(), + wxTextAttr:setLeftIndent(Attr,10), + wxTextAttr:setRightIndent(Attr,10), + true = wxTextCtrl:setDefaultStyle(Text, Attr), + wxTextAttr:destroy(Attr), + Sizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(Sizer, Text, [{border, 2}, + {flag, ?wxEXPAND bor ?wxALL}, + {proportion, 1}]), + + Close = wxButton:new(Panel, ?wxID_ANY, [{label, "Close"}]), + wxButton:setToolTip(Close, "Close window."), + wxButton:connect(Close, command_button_clicked, [{userData,{warning,Frame}}]), + wxSizer:add(Sizer, Close, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), + + wxPanel:setSizer(Panel, Sizer), + wxSizer:fit(Sizer, Frame), + wxSizer:setSizeHints(Sizer, Frame), + wxFrame:connect(Frame, close_window), + + wxFrame:show(Frame), + Frame. + +warning_popup_position(#state{frame=MF,warning_list=WL},{WFW,WFH}) -> + {MFX,MFY} = wxWindow:getPosition(MF), + {MFW,MFH} = wxWindow:getSize(MF), + {_WLW,WLH} = wxWindow:getSize(WL), + + %% Position the popup in the middle of the main frame, above the + %% warning list, and with a small offset from the exact middle... + Offset = 50, + X = MFX + (MFW-WFW) div 2 - Offset, + Y = MFY + (MFH-WLH-WFH) div 2 - Offset, + {X,Y}. + handle_popup_event(S, _Type, 0, _ObjRef, _UserData, _Str) -> S#state{popup_menu = undefined}; handle_popup_event(#state{popup_menu = #root_popup{dir = OldDir, @@ -1079,16 +1246,16 @@ handle_app_event(S, Event, ObjRef, UserData) -> [?MODULE, self(), ObjRef, UserData, Event]), S. -handle_app_button(#state{server_pid = ServerPid, app_wins = AppWins} = S, +handle_app_button(#state{server_pid = ServerPid, + status_bar = Bar, + app_wins = AppWins} = S, Items, Action) -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), NewApps = [move_app(S, Item, Action) || Item <- Items], case reltool_server:set_apps(ServerPid, NewApps) of - {ok, []} -> + {ok, _Warnings} -> ok; - {ok, Warnings} -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING); {error, Reason} -> display_message(Reason, ?wxICON_ERROR) end, @@ -1125,23 +1292,22 @@ move_app(S, {_ItemNo, AppBase}, Action) -> end, OldApp#app{incl_cond = AppCond}. -do_set_app(#state{server_pid = ServerPid, app_wins = AppWins} = S, NewApp) -> +do_set_app(#state{server_pid = ServerPid, + status_bar = Bar, + app_wins = AppWins} = S, NewApp) -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), Result = reltool_server:set_app(ServerPid, NewApp), - [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- AppWins], - S2 = redraw_apps(S), ReturnApp = case Result of - {ok, AnalysedApp, []} -> - AnalysedApp; - {ok, AnalysedApp, Warnings} -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING), + {ok, AnalysedApp, _Warnings} -> AnalysedApp; {error, Reason} -> display_message(Reason, ?wxICON_ERROR), {ok,OldApp} = reltool_server:get_app(ServerPid, NewApp#app.name), OldApp end, + [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- AppWins], + S2 = redraw_apps(S), {ok, ReturnApp, S2}. redraw_apps(#state{server_pid = ServerPid, @@ -1164,8 +1330,14 @@ redraw_apps(#state{server_pid = ServerPid, WhiteN = redraw_apps(WhiteApps, WhiteCtrl, ?TICK_IMAGE, ?ERR_IMAGE), redraw_apps(BlackApps2, BlackCtrl, ?CROSS_IMAGE, ?WARN_IMAGE), DerivedN = redraw_apps(DerivedApps, DerivedCtrl, ?TICK_IMAGE, ?ERR_IMAGE), + + WarningsN = redraw_warnings(S), + WarningText = if WarningsN==1 -> "warning"; + true -> "warnings" + end, Status = lists:concat([WhiteN, " whitelisted modules and ", - DerivedN, " derived modules."]), + DerivedN, " derived modules, ", + WarningsN, " ", WarningText, "."]), wxStatusBar:setStatusText(S#state.status_bar, Status), S. @@ -1323,26 +1495,28 @@ save_config(#state{config_file = OldFile} = S, InclDefaults, InclDerivates) -> S end. -gen_rel_files(#state{target_dir = OldDir} = S) -> +gen_rel_files(#state{status_bar = Bar, target_dir = OldDir} = S) -> Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT, case select_dir(S#state.frame, "Select a directory to generate rel, script and boot files to", OldDir, Style) of {ok, NewDir} -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), Status = reltool_server:gen_rel_files(S#state.server_pid, NewDir), check_and_refresh(S, Status); cancel -> S end. -gen_target(#state{target_dir = OldDir} = S) -> +gen_target(#state{status_bar = Bar, target_dir = OldDir} = S) -> Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT, case select_dir(S#state.frame, "Select a directory to generate a target system to", OldDir, Style) of {ok, NewDir} -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), Status = reltool_server:gen_target(S#state.server_pid, NewDir), check_and_refresh(S#state{target_dir = NewDir}, Status); cancel -> @@ -1380,8 +1554,8 @@ check_and_refresh(S, Status) -> case Status of ok -> true; - {ok, Warnings} -> - undo_dialog(S, Warnings); + {ok, _Warnings} -> + true; {error, Reason} when is_list(Reason) -> display_message(Reason, ?wxICON_ERROR), false; @@ -1435,19 +1609,6 @@ question_dialog(Question, Details) -> wxDialog:destroy(Dialog), Answer. -undo_dialog(_S, []) -> - true; -undo_dialog(S, Warnings) -> - Question = "Do you want to perform the update despite these warnings?", - Details = lists:flatten([[W, $\n] || W <- Warnings]), - case question_dialog(Question, Details) of - ?wxID_OK -> - true; - ?wxID_CANCEL -> - reltool_server:undo_config(S#state.server_pid), - false - end. - display_message(Message, Icon) -> Dialog = wxMessageDialog:new(wx:null(), Message, @@ -1491,8 +1652,6 @@ add_text(_,_,[]) -> ok. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sys callbacks diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl index 0fcf89a360..3d83a77d99 100644 --- a/lib/reltool/src/reltool_target.erl +++ b/lib/reltool/src/reltool_target.erl @@ -1,7 +1,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 @@ -101,7 +101,7 @@ do_gen_config(#sys{root_dir = RootDir, || A <- Apps, A#app.name =/= ?MISSING_APP_NAME, A#app.name =/= erts, - not A#app.is_escript], + A#app.is_escript =/= true], EscriptItems = [{escript, A#app.active_dir, emit(incl_cond, A#app.incl_cond, undefined, InclDefs)} @@ -208,10 +208,10 @@ do_gen_config(#rel_app{name = Name, incl_apps = InclApps}, _InclDefs) -> case {Type, InclApps} of - {undefined, []} -> Name; - {undefined, _} -> {Name, InclApps}; - {_, []} -> {Name, Type}; - {_, _} -> {Name, Type, InclApps} + {undefined, undefined} -> Name; + {undefined, _} -> {Name, InclApps}; + {_, undefined} -> {Name, Type}; + {_, _} -> {Name, Type, InclApps} end; do_gen_config({Tag, Val}, InclDefs) -> emit(Tag, Val, undefined, InclDefs); @@ -279,7 +279,7 @@ gen_rel(Rel, Sys) -> {error, Text} end. -do_gen_rel(#rel{name = RelName, vsn = RelVsn}, +do_gen_rel(#rel{name = RelName, vsn = RelVsn, rel_apps = RelApps}, #sys{apps = Apps}, MergedApps) -> ErtsName = erts, @@ -288,7 +288,7 @@ do_gen_rel(#rel{name = RelName, vsn = RelVsn}, {release, {RelName, RelVsn}, {ErtsName, Erts#app.vsn}, - [strip_rel_info(App) || App <- MergedApps]}; + [strip_rel_info(App, RelApps) || App <- MergedApps]}; false -> reltool_utils:throw_error("Mandatory application ~p is " "not included", @@ -298,13 +298,17 @@ do_gen_rel(#rel{name = RelName, vsn = RelVsn}, strip_rel_info(#app{name = Name, vsn = Vsn, app_type = Type, - info = #app_info{incl_apps = InclApps}}) - when Type =/= undefined -> - case {Type, InclApps} of - {permanent, []} -> {Name, Vsn}; - {permanent, _} -> {Name, Vsn, InclApps}; - {_, []} -> {Name, Vsn, Type}; - {_, _} -> {Name, Vsn, Type, InclApps} + info = #app_info{incl_apps = AppInclApps}}, + RelApps) when Type =/= undefined -> + RelInclApps = case lists:keyfind(Name,#rel_app.name,RelApps) of + #rel_app{incl_apps = RIA} when RIA =/= undefined -> RIA; + _ -> undefined + end, + case {Type, RelInclApps} of + {permanent, undefined} -> {Name, Vsn}; + {permanent, _} -> {Name, Vsn, AppInclApps}; + {_, undefined} -> {Name, Vsn, Type}; + {_, _} -> {Name, Vsn, Type, AppInclApps} end. merge_apps(#rel{name = RelName, @@ -323,7 +327,7 @@ merge_apps(#rel{name = RelName, A#app.name =/= ?MISSING_APP_NAME, not lists:keymember(A#app.name, #app.name, MergedApps2)], MergedApps3 = do_merge_apps(RelName, Embedded, Apps, EmbAppType, MergedApps2), - sort_apps(MergedApps3). + sort_apps(lists:reverse(MergedApps3)). do_merge_apps(RelName, [#rel_app{name = Name} = RA | RelApps], Apps, RelAppType, Acc) -> case is_already_merged(Name, RelApps, Acc) of @@ -341,25 +345,18 @@ do_merge_apps(RelName, [Name | RelApps], Apps, RelAppType, Acc) -> true -> do_merge_apps(RelName, RelApps, Apps, RelAppType, Acc); false -> - RelApp = init_rel_app(Name, Apps), + RelApp = #rel_app{name = Name}, do_merge_apps(RelName, [RelApp | RelApps], Apps, RelAppType, Acc) end; do_merge_apps(_RelName, [], _Apps, _RelAppType, Acc) -> - lists:reverse(Acc). - -init_rel_app(Name, Apps) -> - {value, App} = lists:keysearch(Name, #app.name, Apps), - Info = App#app.info, - #rel_app{name = Name, - app_type = undefined, - incl_apps = Info#app_info.incl_apps}. + Acc. merge_app(RelName, - #rel_app{name = Name, - app_type = Type, - incl_apps = InclApps}, - RelAppType, - App) -> + #rel_app{name = Name, + app_type = Type, + incl_apps = InclApps0}, + RelAppType, + App) -> Type2 = case {Type, App#app.app_type} of {undefined, undefined} -> RelAppType; @@ -367,6 +364,11 @@ merge_app(RelName, {_, _} -> Type end, Info = App#app.info, + InclApps = + case InclApps0 of + undefined -> Info#app_info.incl_apps; + _ -> InclApps0 + end, case InclApps -- Info#app_info.incl_apps of [] -> App#app{app_type = Type2, info = Info#app_info{incl_apps = InclApps}}; @@ -421,7 +423,10 @@ do_gen_script(#rel{name = RelName, vsn = RelVsn}, Mandatory = mandatory_modules(), Early = Mandatory ++ Preloaded, {value, KernelApp} = lists:keysearch(kernel, #app.name, MergedApps), - InclApps = [I || #app{info = #app_info{incl_apps = I}} <- MergedApps], + InclApps = lists:flatmap(fun(#app{info = #app_info{incl_apps = I}}) -> + I + end, + MergedApps), %% Create the script DeepList = @@ -471,7 +476,7 @@ load_app_mods(#app{mods = Mods} = App, Mand, PathFlag, Variables) -> Path = cr_path(App, PathFlag, Variables), PartNames = lists:sort([{packages:split(M),M} || - #mod{name = M} <- Mods, + #mod{name = M, is_included=true} <- Mods, not lists:member(M, Mand)]), SplitMods = lists:foldl( @@ -513,7 +518,12 @@ sort_apps([#app{name = Name, info = Info} = App | Apps], Circular, Visited) -> {Uses, Apps1, NotFnd1} = - find_all(Name, Info#app_info.applications, Apps, Visited, [], []), + find_all(Name, + lists:reverse(Info#app_info.applications), + Apps, + Visited, + [], + []), {Incs, Apps2, NotFnd2} = find_all(Name, lists:reverse(Info#app_info.incl_apps), @@ -895,7 +905,7 @@ spec_escripts(#sys{apps = Apps}, ErtsBin, BinFiles) -> if Name =:= ?MISSING_APP_NAME -> false; - not IsEscript -> + IsEscript =/= true -> false; IsIncl; IsPre -> {true, do_spec_escript(File, ErtsBin, BinFiles)}; @@ -957,7 +967,7 @@ spec_lib_files(#sys{apps = Apps} = Sys) -> if Name =:= ?MISSING_APP_NAME -> false; - IsEscript -> + IsEscript =/= false -> false; IsIncl; IsPre -> true; diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index 39d057d994..b0def45213 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,14 +23,15 @@ split_app_name/1, prim_consult/1, default_rels/0, choose_default/3, - assign_image_list/1, get_latest_resize/1, + assign_image_list/1, get_latest_resize/1, wait_for_stop_motion/2, mod_conds/0, list_to_mod_cond/1, mod_cond_to_index/1, incl_conds/0, list_to_incl_cond/1, incl_cond_to_index/1, elem_to_index/2, app_dir_test/2, split_app_dir/1, get_item/1, get_items/1, get_selected_items/3, select_items/3, select_item/2, + get_column_width/1, - safe_keysearch/5, print/4, return_first_error/2, add_warning/2, + safe_keysearch/5, print/4, add_warning/3, create_dir/1, list_dir/1, read_file_info/1, write_file_info/2, read_file/1, write_file/2, @@ -126,18 +127,14 @@ prim_parse(Tokens, Acc) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% default_rels() -> - %%Kernel = #rel_app{name = kernel, incl_apps = []}, - %%Stdlib = #rel_app{name = stdlib, incl_apps = []}, - Sasl = #rel_app{name = sasl, incl_apps = []}, + %% kernel and stdlib are added automatically in every release [ #rel{name = ?DEFAULT_REL_NAME, vsn = "1.0", rel_apps = []}, - %%rel_apps = [Kernel, Stdlib]}, #rel{name = "start_sasl", vsn = "1.0", - rel_apps = [Sasl]} - %%rel_apps = [Kernel, Sasl, Stdlib]} + rel_apps = [#rel_app{name = sasl}]} ]. choose_default(Tag, Profile, InclDefs) @@ -191,6 +188,16 @@ get_latest_resize(#wx{obj = ObjRef, event = #wxSize{}} = Wx) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +wait_for_stop_motion(ObjRef, {_,_}=Pos) -> + receive + #wx{obj = ObjRef, event = #wxMouse{type = motion, x=X, y=Y}} -> + wait_for_stop_motion(ObjRef, {X,Y}) + after 100 -> + Pos + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + mod_conds() -> ["all (ebin + app file)", "ebin + derived", "app file + derived", "derived", "none"]. @@ -377,6 +384,26 @@ select_item(ListCtrl, [{ItemNo, Text} | Items]) -> select_item(_ListCtrl, []) -> ok. +get_column_width(ListCtrl) -> + wx:batch(fun() -> + {Total, _} = wxWindow:getClientSize(ListCtrl), + Total - scroll_size(ListCtrl) + end). + +scroll_size(ObjRef) -> + case os:type() of + {win32, nt} -> 0; + {unix, darwin} -> + %% I can't figure out is there is a visible scrollbar + %% Always make room for it + wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); + _ -> + case wxWindow:hasScrollbar(ObjRef, ?wxVERTICAL) of + true -> wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); + false -> 0 + end + end. + safe_keysearch(Key, Pos, List, Mod, Line) -> case lists:keysearch(Key, Pos, List) of false -> @@ -392,31 +419,13 @@ print(X, X, Format, Args) -> print(_, _, _, _) -> ok. -%% -define(SAFE(M,F,A), safe(M, F, A, ?MODULE, ?LINE)). -%% -%% safe(M, F, A, Mod, Line) -> -%% case catch apply(M, F, A) of -%% {'EXIT', Reason} -> -%% io:format("~p(~p): ~p:~p~p -> ~p\n", [Mod, Line, M, F, A, Reason]), -%% timer:sleep(infinity); -%% Res -> -%% Res -%% end. - -return_first_error(Status, NewError) when is_list(NewError) -> - case Status of - {ok, _Warnings} -> - {error, NewError}; - {error, OldError} -> - {error, OldError} - end. - -add_warning(Status, Warning) -> - case Status of - {ok, Warnings} -> - {ok, [Warning | Warnings]}; - {error, Error} -> - {error, Error} +add_warning(Format, Args, {ok,Warnings}) -> + Warning = lists:flatten(io_lib:format(Format,Args)), + case lists:member(Warning,Warnings) of + true -> + {ok,Warnings}; + false -> + {ok,[Warning|Warnings]} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index 767454b66a..d8a7adb837 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/Makefile @@ -1,7 +1,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 @@ -28,6 +28,7 @@ MODULES= \ reltool_app_SUITE \ reltool_wx_SUITE \ reltool_server_SUITE \ + reltool_manual_gui_SUITE \ reltool_test_lib diff --git a/lib/reltool/test/reltool.spec b/lib/reltool/test/reltool.spec index 2995720105..2501a7a203 100644 --- a/lib/reltool/test/reltool.spec +++ b/lib/reltool/test/reltool.spec @@ -1 +1,2 @@ {suites,"../reltool_test",all}. +{skip_suites,"../reltool_test",[reltool_manual_gui_SUITE],"Manual only"}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE.erl b/lib/reltool/test/reltool_manual_gui_SUITE.erl new file mode 100644 index 0000000000..0dcc5cbf15 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE.erl @@ -0,0 +1,266 @@ +%% +%% %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(reltool_manual_gui_SUITE). + +-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, + init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include("reltool_test_lib.hrl"). + +%% Initialization functions. +init_per_suite(Config) -> + reltool_test_lib:wx_init_per_suite(Config). + +end_per_suite(Config) -> + reltool_test_lib:wx_end_per_suite(Config). + +init_per_testcase(Func,Config) -> + reltool_test_lib:init_per_testcase(Func,Config). +end_per_testcase(Func,Config) -> + reltool_test_lib:end_per_testcase(Func,Config). + +%% SUITE specification +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [config, depgraphs]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%% The test cases + +%% Semi-automatic walkthrough of the GUI +config(Config) -> + DataDir = ?config(data_dir,Config), + PrivDir = ?config(priv_dir,Config), + {ok, SysPid} = ?msym({ok, _}, reltool:start([])), + link(SysPid), + + SimpleConfigFile = create_simple_config(PrivDir), + WarningConfigFile = create_warning_config(PrivDir,DataDir), + + break("there are no modules in the 'Included' and 'Excluded' columns, " + "and now warnings are displayed", + {"load configuration ~p",[SimpleConfigFile]}), + break("kernel, stdlib and sasl are included and all other are excluded", + "undo"), + break("we are back to default - no included and no excluded applications", + "undo again"), + break("kernel, stdlib and sasl are included and all other are excluded", + {"load configuration ~p", + [WarningConfigFile]}), + break("a warning is displayed in the warning list", + "undo"), + break("no warning is displayed in the warning list", + "load same configuration again"), + break("application a is added in the 'Included' column and " + "one warning is displayed", + "reset configuration"), + break("we are back to default - no included and no excluded applications, " + "and no warnings", + "undo"), + break("a, kernel, stdlib and sasl are included and all other are excluded. " + "One warning should now exist through the rest of this test case", + "double click the warning"), + break("a popup window occurs displaying the warning text", + "close it with the 'Close' button"), + break("it disappears", + "open it again"), + break("the warning text can be marked, copied and pasted", + "close the popup with the close box on the top frame"), + break("it disappears", + "select application a from 'Included' column and click 'cross'-button " + "with to exclude it"), + break("application a is moved to 'Excluded' column", + "select application tools from 'Excluded' column and click " + "'tick'-button to include it"), + break("application tools is moved to 'Included' column", + "select application runtime_tools from 'Excluded' column and click " + "'tick'-button to include it"), + break("application runtime_tools is moved to 'Included' column", + "undo"), + + ExplicitConfig = filename:join(PrivDir,"explicit.config"), + break("application runtime_tools is moved back to 'Excluded' column", + {"save configuration as 'explicit' to ~p",[ExplicitConfig]}), + ExpectedExplicitConfig = + {sys,[{lib_dirs,[filename:join(DataDir,"faulty_app_file")]}, + {incl_cond,exclude}, + {app,a,[{incl_cond,exclude}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + check_config(ExpectedExplicitConfig,ExplicitConfig), + + break("The saved configuration file is checked and is ok.\n" + "Now go to the 'Libraries' tab and change the root directory to " + "some invalid directory."), + break("an error dialog occurs saying that there is no lib dir", + {"add library directory ~p", + [filename:join(DataDir,"dependencies")]}), + break("applications x, y and z are added to the 'Excluded' column in " + "'Applications' tab", + "go to the 'System settings' tab and set application inclusion policy " + "to 'derived'"), + break("a is excluded, kernel, stdlib, sasl and tools are included and " + "x, y and z are available in the 'Applications' tab", + "undo"), + break("all non included application are moved to the 'Excluded' column " + "and that 'Application inclusion policy' under 'System settings' " + "tab is set back to 'exclude'", + "undo again"), + break("applications x, y and z are in the 'Available' column", + "open application x, go to the 'Application settings' tab and set " + "'Application inclusion policy' to 'Use application specific config' " + "and 'include'. Close the window"), + break("application x is moved to the 'Included' column and z and y are moved " + "to the 'Derived' column in the system window", + "open application y"), + break("modules y1, y2 and y3 are in the 'Derived' column", + "go to 'Application dependencies' tab"), + break("application y is used by x, requires kernel and stdlib, and uses z", + "go to 'Module dependencies' tab"), + break("y is used by x2 and x3, and uses z1", + "got to 'Application settings' tab and select " + "'Source selection policy' 'Use selected version'"), + break("'Directories' frame becomes enabled (clickable)", + "select 'Module inclusion policy' 'Use application specific config' " + "and select 'app file + derived'"), + break("module y3 is moved to the 'Available' column in the 'Modules' tab", + "open module y1"), + break("module y1 is used by x2 and uses z1", + "go to the 'Code' tab and double click the line with call to z1:f()"), + break("new tab is opened with module z1", + "find something"), + break("it is found", + "goto some line"), + break("the cursor is moved to that line", + "click the 'Back' button"), + break("the cursor is moved back to the line of your previous 'find'", + "terminate the application window (but keep the module window)."), + + {ok,ServerPid} = reltool:get_server(SysPid), + unlink(SysPid), + break("the system window is still alive", + "terminate reltool by hitting 'Ctrl-q' (linux) or clicking the " + "close box on the top fram when system window is active"), + false = erlang:is_process_alive(SysPid), + false = erlang:is_process_alive(ServerPid), + + break("Check that both module window and system window are terminated"), + + ok. + + +depgraphs(Config) -> + PrivDir = ?config(priv_dir,Config), + SimpleConfigFile = create_simple_config(PrivDir), + {ok, SysPid} = ?msym({ok, _}, reltool:start([{config,SimpleConfigFile}])), + link(SysPid), + + break("Open the application dependency graph and \n\n" + "*move the complete graph by left clicking somewhere over it and drag\n" + "*move one node left clicking the node and drag\n" + "*lock node to position by holding down shift while releasing\n" + "*select several nodes with ctrl and left mouse button\n" + "*lock/unlock selected nodes with suitable buttons\n" + "*freeze\n" + "*reset\n" + "*left slider: push nodes apart\n" + "*right slider: pull nodes together\n" + "*middle slider: adjust length of links\n" + "*select node and delete\n"), + break("Open the module dependency graph and meditate over it... "), + + unlink(SysPid), + break("Terminate reltool from the file menu in the system window"), + false = erlang:is_process_alive(SysPid), + + break("Check that system window and graphs are terminated"), + + ok. + + + + +%%%----------------------------------------------------------------- +%%% Internal functions +break(CheckStr,DoStr) when is_list(CheckStr), is_list(DoStr) -> + Str = io_lib:format("Check that ~s.~n~nThen ~s.",[CheckStr,DoStr]), + break(Str); +break(Check,Do) -> + CheckStr = + case Check of + {CheckFormat,CheckArgs} -> io_lib:format(CheckFormat,CheckArgs); + _ -> Check + end, + DoStr = + case Do of + {DoFormat,DoArgs} -> io_lib:format(DoFormat,DoArgs); + _ -> Do + end, + break(CheckStr,DoStr). + +break(Str) -> + Count = + case get(count) of + undefined -> 1; + C -> C + end, + put(count,Count+1), + test_server:break("Step " ++ integer_to_list(Count) ++ ":\n" ++ Str). + +check_config(Expected,File) -> + {ok,[Config]} = file:consult(File), + ?m(Expected,Config). + +create_simple_config(PrivDir) -> + SimpleConfigFile = filename:join(PrivDir,"simple.config"), + SimpleConfig = {sys,[{incl_cond,exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}]}, + ok=file:write_file(SimpleConfigFile,io_lib:format("~p.~n",[SimpleConfig])), + SimpleConfigFile. + +create_warning_config(PrivDir,DataDir) -> + WarningConfigFile = filename:join(PrivDir,"warning.config"), + FaultyAppFileDir = filename:join(DataDir,"faulty_app_file"), + WarningConfig = {sys,[{lib_dirs,[FaultyAppFileDir]}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,a,[{incl_cond,include}]} + ]}, + ok=file:write_file(WarningConfigFile,io_lib:format("~p.~n",[WarningConfig])), + WarningConfigFile. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/Makefile.src b/lib/reltool/test/reltool_manual_gui_SUITE_data/Makefile.src new file mode 100644 index 0000000000..9a340274ad --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/Makefile.src @@ -0,0 +1,28 @@ +EFLAGS=+debug_info + +DEPENDENCIES= \ + dependencies/x-1.0/ebin/x1.@EMULATOR@ \ + dependencies/x-1.0/ebin/x2.@EMULATOR@ \ + dependencies/x-1.0/ebin/x3.@EMULATOR@ \ + dependencies/y-1.0/ebin/y1.@EMULATOR@ \ + dependencies/y-1.0/ebin/y2.@EMULATOR@ \ + dependencies/y-1.0/ebin/y3.@EMULATOR@ \ + dependencies/z-1.0/ebin/z1.@EMULATOR@ + + +all: $(DEPENDENCIES) + +dependencies/x-1.0/ebin/x1.@EMULATOR@: dependencies/x-1.0/src/x1.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x1.erl +dependencies/x-1.0/ebin/x2.@EMULATOR@: dependencies/x-1.0/src/x2.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x2.erl +dependencies/x-1.0/ebin/x3.@EMULATOR@: dependencies/x-1.0/src/x3.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x3.erl +dependencies/y-1.0/ebin/y1.@EMULATOR@: dependencies/y-1.0/src/y1.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y1.erl +dependencies/y-1.0/ebin/y2.@EMULATOR@: dependencies/y-1.0/src/y2.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y2.erl +dependencies/y-1.0/ebin/y3.@EMULATOR@: dependencies/y-1.0/src/y3.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y3.erl +dependencies/z-1.0/ebin/z1.@EMULATOR@: dependencies/z-1.0/src/z1.erl + erlc $(EFLAGS) -odependencies/z-1.0/ebin dependencies/z-1.0/src/z1.erl diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/ebin/x.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/ebin/x.app new file mode 100644 index 0000000000..ccaab8a8c7 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, x, + [{description, "Main application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [x1,x2,x3]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x1.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x1.erl new file mode 100644 index 0000000000..bf1e7f9279 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x1.erl @@ -0,0 +1,5 @@ +-module(x1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x2.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x2.erl new file mode 100644 index 0000000000..82191ba278 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x2.erl @@ -0,0 +1,5 @@ +-module(x2). +-compile(export_all). + +f() -> + y1:f(). diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x3.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x3.erl new file mode 100644 index 0000000000..618c75c9a7 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x3.erl @@ -0,0 +1,5 @@ +-module(x3). +-compile(export_all). + +f() -> + y2:f(). diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/ebin/y.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/ebin/y.app new file mode 100644 index 0000000000..4f9b610b69 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, y, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [y1,y2]}, % y3 is skipped on purpose - to test module inclusion policy + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y1.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y1.erl new file mode 100644 index 0000000000..dd21b33292 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y1.erl @@ -0,0 +1,5 @@ +-module(y1). +-compile(export_all). + +f() -> + z1:f(). diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y2.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y2.erl new file mode 100644 index 0000000000..bf8ddf6080 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y2.erl @@ -0,0 +1,5 @@ +-module(y2). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y3.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y3.erl new file mode 100644 index 0000000000..915d64d5b9 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y3.erl @@ -0,0 +1,5 @@ +-module(y3). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/ebin/z.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/ebin/z.app new file mode 100644 index 0000000000..437a0968e9 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/ebin/z.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, z, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [z1]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/src/z1.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/src/z1.erl new file mode 100644 index 0000000000..97ef90b87f --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/src/z1.erl @@ -0,0 +1,5 @@ +-module(z1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/ebin/a.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/ebin/a.app new file mode 100644 index 0000000000..ea77103598 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/ebin/a.app @@ -0,0 +1 @@ +faulty app file diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/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_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/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/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 9ed79e8c95..4e24a2fb55 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -1,7 +1,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 @@ -24,6 +24,7 @@ -compile(export_all). +-include_lib("reltool/src/reltool.hrl"). -include("reltool_test_lib.hrl"). -include_lib("common_test/include/ct.hrl"). @@ -51,10 +52,43 @@ end_per_testcase(Func,Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [start_server, set_config, create_release, - create_script, create_target, create_embedded, - create_standalone, create_old_target, - otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod]. + [start_server, + set_config, + get_config, + create_release, + create_release_sort, + create_script, + create_script_sort, + create_target, + create_embedded, + create_standalone, + create_standalone_beam, + create_standalone_app, + create_standalone_app_clash, + create_multiple_standalone, + create_old_target, + eval_target_spec, + otp_9135, + otp_9229_dupl_mod_exclude_app, + otp_9229_dupl_mod_exclude_mod, + dupl_mod_in_app_file, + get_apps, + get_mod, + get_sys, + set_app_and_undo, + set_apps_and_undo, + set_apps_inlined, + set_sys_and_undo, + load_config_and_undo, + load_config_fail, + load_config_escript_path, + load_config_same_escript_source, + load_config_same_escript_beam, + load_config_add_escript, + reset_config_and_undo, + gen_rel_files, + save_config, + dependencies]. groups() -> []. @@ -71,8 +105,6 @@ end_per_group(_GroupName, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Start a server process and check that it does not crash -start_server(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); start_server(_Config) -> {ok, Pid} = ?msym({ok, _}, reltool:start_server([])), Libs = lists:sort(erl_libs()), @@ -88,8 +120,6 @@ start_server(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Start a server process and check that it does not crash -set_config(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); set_config(_Config) -> Libs = lists:sort(erl_libs()), Default = @@ -112,10 +142,90 @@ set_config(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Check that get_config returns the expected derivates and defaults +%% as specified +get_config(_Config) -> + + KVsn = latest(kernel), + StdVsn = latest(stdlib), + SaslVsn = latest(sasl), + + Sys = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}]}, + {app,stdlib,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + ?m({ok, Sys}, reltool:get_config(Pid,false,false)), + + %% Include derived info + ?msym({ok,{sys,[{incl_cond, exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}}, + reltool:get_config(Pid,false,true)), + + %% Include defaults + ?msym({ok,{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include},{vsn,undefined}]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}]}, + {app,stdlib,[{incl_cond,include},{vsn,undefined}]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}}, + reltool:get_config(Pid,true,false)), + + %% Include both defaults and derived info + ?msym({ok,{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{vsn,KVsn},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{vsn,StdVsn},{mod,_,[]}|_]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}}, + reltool:get_config(Pid,true,true)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% OTP-9135, test that app_file option can be set to all | keep | strip -otp_9135(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); otp_9135(_Config) -> Libs = lists:sort(erl_libs()), StrippedDefaultSys = @@ -145,8 +255,6 @@ otp_9135(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate releases -create_release(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_release(_Config) -> %% Configure the server RelName = "Just testing...", @@ -170,10 +278,135 @@ create_release(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate releases and make sure order of applications specified in +%% 'rel' parameter is preserved and that included applications are +%% started before the including application. +%% Circular dependencies shall also be detected and cause error. + +create_release_sort(_Config) -> {skip, "Two bugs related to sorting"}; +create_release_sort(Config) -> + DataDir = ?config(data_dir,Config), + %% Configure the server + RelName1 = "MnesiaFirst", + RelName2 = "SaslFirst", + RelName3 = "Include-both", + RelName4 = "Include-only-app", + RelName5 = "Include-only-rel", + RelName6 = "Include-missing-app", + RelName7 = "Circular", + RelName8 = "Include-both-missing-app", + RelName9 = "Include-overwrite", + RelName10= "Uses-order-as-rel", + RelVsn = "1.0", + %% Application z (.app file): + %% includes [tools, mnesia] + %% uses [kernel, stdlib, sasl, inets] + Sys = + {sys, + [ + {lib_dirs, [filename:join(DataDir,"sort_apps")]}, + {boot_rel, RelName1}, + {rel, RelName1, RelVsn, [stdlib, kernel, mnesia, sasl]}, + {rel, RelName2, RelVsn, [stdlib, kernel, sasl, mnesia]}, + {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]}, + {rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]}, + {rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]}, + {rel, RelName6, RelVsn, [stdlib, kernel, z]}, + {rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]}, + {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]}, + {rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]}, + {rel, RelName10, RelVsn, [stdlib, kernel, {z,[]}, inets, sasl]}, + {incl_cond,exclude}, + {mod_cond,app}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,mnesia,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,inets,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,include}]}, + {app,z,[{incl_cond,include}]}, + {app,tools,[{mod_cond,app},{incl_cond,include}]} + ]}, + %% Generate release + + ?msym({ok, {release, {RelName1, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {mnesia, _}, + {sasl, _}]}}, + reltool:get_rel([{config, Sys}], RelName1)), + + ?msym({ok, {release, {RelName2, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {sasl, _}, + {mnesia, _}]}}, + reltool:get_rel([{config, Sys}], RelName2)), + + ?msym({ok, {release, {RelName3, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {sasl, _}, + {inets, _}, + {tools, _}, + {z, _, [tools]}, + {mnesia, _}]}}, + reltool:get_rel([{config, Sys}], RelName3)), + + %%! BUG: same as OTP-4121, but for reltool???? Or revert tools and mnesia + ?msym({ok, {release, {RelName4, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {sasl, _}, + {inets, _}, + {mnesia, _}, + {tools, _}, + {z, _}]}}, + reltool:get_rel([{config, Sys}], RelName4)), + + ?m({error,"sasl: These applications are used by release " + "Include-only-rel but are missing as included_applications " + "in the app file: [tools]"}, + reltool:get_rel([{config, Sys}], RelName5)), + + ?m({error, "Undefined applications: [tools,mnesia]"}, + reltool:get_rel([{config, Sys}], RelName6)), + + ?m({error,"Circular dependencies: [x,y]"}, + reltool:get_rel([{config, Sys}], RelName7)), + + ?m({error,"Undefined applications: [tools]"}, + reltool:get_rel([{config, Sys}], RelName8)), + + ?msym({ok,{release,{RelName9,RelVsn}, + {erts,_}, + [{kernel,_}, + {stdlib,_}, + {sasl, _}, + {inets, _}, + {z,_,[]}]}}, + reltool:get_rel([{config, Sys}], RelName9)), + + %%! BUG: same as OTP-9984, but for reltool???? Or revert inets and sasl? + ?msym({ok,{release,{RelName10,RelVsn}, + {erts,_}, + [{kernel,_}, + {stdlib,_}, + {inets, _}, + {sasl, _}, + {z,_,[]}]}}, + reltool:get_rel([{config, Sys}], RelName10)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate boot scripts -create_script(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_script(_Config) -> %% Configure the server RelName = "Just testing", @@ -218,10 +451,197 @@ create_script(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test creation of .script with different sorting of applications and +%% included applications. +%% Test that result is equal to what systools produces +create_script_sort(Config) -> + DataDir = ?config(data_dir,Config), + %% Configure the server + RelName1 = "MnesiaFirst", + RelName2 = "SaslFirst", + RelName3 = "Include-both", + RelName4 = "Include-only-app", + RelName5 = "Include-only-rel", + RelName6 = "Include-missing-app", + RelName7 = "Circular", + RelName8 = "Include-both-missing-app", + RelName9 = "Include-overwrite", + RelVsn = "1.0", + LibDir = filename:join(DataDir,"sort_apps"), + Sys = + {sys, + [ + {lib_dirs, [LibDir]}, + {boot_rel, RelName1}, + {rel, RelName1, RelVsn, [stdlib, kernel, mnesia, sasl]}, + {rel, RelName2, RelVsn, [stdlib, kernel, sasl, mnesia]}, + {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]}, + {rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]}, + {rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]}, + {rel, RelName6, RelVsn, [stdlib, kernel, z]}, + {rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]}, + {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]}, + {rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]}, + {incl_cond,exclude}, + {mod_cond,app}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,mnesia,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,inets,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,include}]}, + {app,z,[{incl_cond,include}]}, + {app,tools,[{mod_cond,app},{incl_cond,include}]} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + %% Generate release files + application:load(sasl), + application:load(inets), + application:load(mnesia), + application:load(tools), + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + {ok,InetsVsn} = application:get_key(inets,vsn), + {ok,MnesiaVsn} = application:get_key(mnesia,vsn), + {ok,ToolsVsn} = application:get_key(tools,vsn), + ErtsVsn = erlang:system_info(version), + + Rel1 = {release, {RelName1,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {mnesia,MnesiaVsn}, + {sasl,SaslVsn}]}, + FullName1 = filename:join(?WORK_DIR,RelName1), + ?m(ok, file:write_file(FullName1 ++ ".rel", io_lib:format("~p.\n", [Rel1]))), + Rel2 = {release, {RelName2,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {sasl,SaslVsn}, + {mnesia,MnesiaVsn}]}, + FullName2 = filename:join(?WORK_DIR,RelName2), + ?m(ok, file:write_file(FullName2 ++ ".rel", io_lib:format("~p.\n", [Rel2]))), + Rel3 = {release, {RelName3,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0",[tools]}, + {tools,ToolsVsn}, + {mnesia,MnesiaVsn}, + {sasl,SaslVsn}, + {inets,InetsVsn}]}, + FullName3 = filename:join(?WORK_DIR,RelName3), + ?m(ok, file:write_file(FullName3 ++ ".rel", io_lib:format("~p.\n", [Rel3]))), + Rel4 = {release, {RelName4,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0"}, + {tools,ToolsVsn}, + {mnesia,MnesiaVsn}, + {sasl,SaslVsn}, + {inets,InetsVsn}]}, + FullName4 = filename:join(?WORK_DIR,RelName4), + ?m(ok, file:write_file(FullName4 ++ ".rel", io_lib:format("~p.\n", [Rel4]))), + Rel5 = {release, {RelName5,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {sasl,SaslVsn,[tools]}, + {tools,ToolsVsn}]}, + FullName5 = filename:join(?WORK_DIR,RelName5), + ?m(ok, file:write_file(FullName5 ++ ".rel", io_lib:format("~p.\n", [Rel5]))), + Rel6 = {release, {RelName6,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0"}]}, + FullName6 = filename:join(?WORK_DIR,RelName6), + ?m(ok, file:write_file(FullName6 ++ ".rel", io_lib:format("~p.\n", [Rel6]))), + Rel7 = {release, {RelName7,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {mnesia,MnesiaVsn}, + {y,"1.0"}, + {sasl,SaslVsn}, + {x,"1.0"}]}, + FullName7 = filename:join(?WORK_DIR,RelName7), + ?m(ok, file:write_file(FullName7 ++ ".rel", io_lib:format("~p.\n", [Rel7]))), + Rel8 = {release, {RelName8,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0",[tools]}]}, + FullName8 = filename:join(?WORK_DIR,RelName8), + ?m(ok, file:write_file(FullName8 ++ ".rel", io_lib:format("~p.\n", [Rel8]))), + Rel9 = {release, {RelName9,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0",[]}, + {sasl,SaslVsn}, + {inets,InetsVsn}]}, + FullName9 = filename:join(?WORK_DIR,RelName9), + ?m(ok, file:write_file(FullName9 ++ ".rel", io_lib:format("~p.\n", [Rel9]))), + + %% Generate script files with systools and reltool and compare + ZPath = filename:join([LibDir,"*",ebin]), + + ?msym({ok,_,_}, systools_make_script(FullName1,ZPath)), + {ok, [SystoolsScript1]} = ?msym({ok,[_]}, file:consult(FullName1++".script")), + {ok, Script1} = ?msym({ok, _}, reltool:get_script(Pid, RelName1)), + ?m(equal, diff_script(SystoolsScript1, Script1)), + + ?msym({ok,_,_}, systools_make_script(FullName2,ZPath)), + {ok, [SystoolsScript2]} = ?msym({ok,[_]}, file:consult(FullName2++".script")), + {ok, Script2} = ?msym({ok, _}, reltool:get_script(Pid, RelName2)), + ?m(equal, diff_script(SystoolsScript2, Script2)), + + ?msym({ok,_,_}, systools_make_script(FullName3,ZPath)), + {ok, [SystoolsScript3]} = ?msym({ok,[_]}, file:consult(FullName3++".script")), + {ok, Script3} = ?msym({ok, _}, reltool:get_script(Pid, RelName3)), + ?m(equal, diff_script(SystoolsScript3, Script3)), + + ?msym({ok,_,_}, systools_make_script(FullName4,ZPath)), + {ok, [SystoolsScript4]} = ?msym({ok,[_]}, file:consult(FullName4++".script")), + {ok, Script4} = ?msym({ok, _}, reltool:get_script(Pid, RelName4)), + ?m(equal, diff_script(SystoolsScript4, Script4)), + + ?msym({error,_,[{error_reading,{sasl,{override_include,_}}}]}, + systools_make_script(FullName5,ZPath)), + ?m({error,"sasl: These applications are used by release " + "Include-only-rel but are missing as included_applications " + "in the app file: [tools]"}, + reltool:get_script(Pid, RelName5)), + + ?msym({error,_,{undefined_applications,_}}, + systools_make_script(FullName6,ZPath)), + ?m({error, "Undefined applications: [tools,mnesia]"}, + reltool:get_script(Pid, RelName6)), + + ?msym({error,_,{circular_dependencies,_}}, + systools_make_script(FullName7,ZPath)), + ?m({error,"Circular dependencies: [x,y]"}, + reltool:get_script(Pid, RelName7)), + + ?msym({error,_,{undefined_applications,_}}, + systools_make_script(FullName8,ZPath)), + ?m({error, "Undefined applications: [tools]"}, + reltool:get_script(Pid, RelName8)), + + ?msym({ok,_,_}, systools_make_script(FullName9,ZPath)), + {ok, [SystoolsScript9]} = ?msym({ok,[_]}, file:consult(FullName9++".script")), + {ok, Script9} = ?msym({ok, _}, reltool:get_script(Pid, RelName9)), + ?m(equal, diff_script(SystoolsScript9, Script9)), + + %% Stop server + ?m(ok, reltool:stop(Pid)), + ok. + + +systools_make_script(Name,Path) -> + systools:make_script(Name,[{path,[Path]},{outdir,?WORK_DIR},silent]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate target system -create_target(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_target(_Config) -> %% Configure the server RelName1 = "Just testing", @@ -243,7 +663,7 @@ create_target(_Config) -> ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Config}])]), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -254,8 +674,6 @@ create_target(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate embedded target system -create_embedded(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_embedded(_Config) -> %% Configure the server RelName1 = "Just testing", @@ -276,7 +694,7 @@ create_embedded(_Config) -> TargetDir = filename:join([?WORK_DIR, "target_embedded"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -287,8 +705,6 @@ create_embedded(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate standalone system -create_standalone(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_standalone(_Config) -> %% Configure the server ExDir = code:lib_dir(reltool, examples), @@ -306,30 +722,223 @@ create_standalone(_Config) -> TargetDir = filename:join([?WORK_DIR, "target_standalone"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + %% Start the target system and fetch root dir BinDir = filename:join([TargetDir, "bin"]), Erl = filename:join([BinDir, "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), ?msym(ok, stop_node(Node)), + %% Execute escript Expected = iolist_to_binary(["Root dir: ", RootDir, "\n" "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", "Smp: false\n", "ExitCode:0"]), io:format("Expected: ~s\n", [Expected]), - ?m(Expected, run(BinDir, EscriptName ++ " -arg1 arg2 arg3")), + ?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with inlined beam file + +create_standalone_beam(Config) -> + %% Read beam file + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + + %% Create the escript + EscriptName = "mymod.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_standalone_beam"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)), + + %% Start the target system and fetch root dir + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), + ?msym(ok, stop_node(Node)), + + %% Execute escript + Expected = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "ExitCode:0"]), + io:format("Expected: ~s\n", [Expected]), + ?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with inlined archived application + +create_standalone_app(Config) -> + %% Create archive + DataDir = ?config(data_dir,Config), + EscriptDir = filename:join(DataDir,escript), + {ok,{_Archive,Bin}} = zip:create("someapp-1.0.ez",["someapp-1.0"], + [memory, + {cwd,EscriptDir}, + {compress,all}, + {uncompress,[".beam",".app"]}]), + + %% Create the escript + EscriptName = "someapp.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang, + {emu_args,"-escript main mymod"}, + {archive,Bin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_standalone_app"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)), + + %% Start the target system and fetch root dir + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), + ?msym(ok, stop_node(Node)), + + %% Execute escript + Expected = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "ExitCode:0"]), + io:format("Expected: ~s\n", [Expected]), + ?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with inlined archived application +%% Check that the inlined app can not be explicitly configured + +create_standalone_app_clash(Config) -> + %% Create archive + DataDir = ?config(data_dir,Config), + EscriptDir = filename:join(DataDir,escript), + {ok,{_Archive,Bin}} = zip:create("someapp-1.0.ez",["someapp-1.0"], + [memory, + {cwd,EscriptDir}, + {compress,all}, + {uncompress,[".beam",".app"]}]), + + %% Create the escript + EscriptName = "someapp.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang, + {emu_args,"-escript main mymod"}, + {archive,Bin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone}, + {app, someapp, [{incl_cond,include}]} + ]}, + + ?msym({error,"someapp: Application name clash. Escript "++_}, + reltool:start_server([{config,Sys}])), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with multiple escripts + +create_multiple_standalone(Config) -> + %% First escript + ExDir = code:lib_dir(reltool, examples), + EscriptName1 = "display_args", + Escript1 = filename:join([ExDir, EscriptName1]), + + %% Second escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName2 = "mymod.escript", + Escript2 = filename:join(?WORK_DIR,EscriptName2), + ok = escript:create(Escript2,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript2,8#00744), + + %% Configure server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript1, [{incl_cond, include}]}, + {escript, Escript2, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + %% Generate target system + TargetDir = filename:join([?WORK_DIR, "target_multiple_standalone"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ok = ?m(ok, reltool:create_target([{config,Sys}], TargetDir)), + + %% Start the target system and fetch root dir + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), + ?msym(ok, stop_node(Node)), + + %% Execute escript1 + Expected1 = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "Smp: false\n", + "ExitCode:0"]), + io:format("Expected1: ~s\n", [Expected1]), + ?m(Expected1, run(BinDir, EscriptName1, "-arg1 arg2 arg3")), + + + %% Execute escript2 + Expected2 = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "ExitCode:0"]), + io:format("Expected2: ~s\n", [Expected2]), + ?m(Expected2, run(BinDir, EscriptName2, "-arg1 arg2 arg3")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate old type of target system -create_old_target(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); +create_old_target(_Config) -> {skip, "Old style of target"}; create_old_target(_Config) -> - ?skip("Old style of target", []), %% Configure the server RelName1 = "Just testing", @@ -350,10 +959,10 @@ create_old_target(_Config) -> TargetDir = filename:join([?WORK_DIR, "target_old_style"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), %% io:format("Will fail on Windows (should patch erl.ini)\n", []), - ?m(ok, reltool:install(RelName2, TargetDir)), + ok = ?m(ok, reltool:install(RelName2, TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -362,11 +971,43 @@ create_old_target(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate target system with eval_target_spec/3 + +eval_target_spec(_Config) -> + %% Configure the server + RelName1 = "Just testing", + RelName2 = "Just testing with SASL", + RelVsn = "1.0", + Config = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, []}, + {boot_rel, RelName2}, + {rel, RelName1, RelVsn, [stdlib, kernel]}, + {rel, RelName2, RelVsn, [sasl, stdlib, kernel]}, + {app, sasl, [{incl_cond, include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "eval_target_spec"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + {ok, Spec} = ?msym({ok,_}, reltool:get_target_spec([{config, Config}])), + ok = ?m(ok, reltool:eval_target_spec(Spec, code:root_dir(), TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + ?msym(ok, stop_node(Node)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% OTP-9229 - handle duplicated module names, i.e. same module name %% exists in two applications. %% Include on app, exclude the other -otp_9229_exclude_app(Config) -> +otp_9229_dupl_mod_exclude_app(Config) -> DataDir = ?config(data_dir,Config), LibDir = filename:join(DataDir,"otp_9229"), @@ -389,8 +1030,8 @@ otp_9229_exclude_app(Config) -> ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclApp}])]), - {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclApp}]), - ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), + ?m({ok,["Module mylib exists in applications x and y. Using module from application x."]}, reltool:get_status([{config, ExclApp}])), + ok = ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -413,7 +1054,7 @@ otp_9229_exclude_app(Config) -> ok. %% Include both apps, but exclude common module from one app -otp_9229_exclude_mod(Config) -> +otp_9229_dupl_mod_exclude_mod(Config) -> DataDir = ?config(data_dir,Config), LibDir = filename:join(DataDir,"otp_9229"), @@ -436,8 +1077,8 @@ otp_9229_exclude_mod(Config) -> ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclMod}])]), - {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclMod}]), - ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), + ?m({ok,["Module mylib exists in applications x and y. Using module from application x."]}, reltool:get_status([{config, ExclMod}])), + ok = ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -466,7 +1107,892 @@ otp_9229_exclude_mod(Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that if a module is duplicated in a .app file, then a warning +%% is produced, but target can still be created. +dupl_mod_in_app_file(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"dupl_mod"), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_in_app_file"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]), + ?m({ok,["Module a duplicated in app file for application a."]}, + reltool:get_status([{config, Sys}])), + + %%! test that only one module installed (in spec) + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test the interface used by the GUI: +%% get_app +%% get_apps +%% set_app +%% set_apps +%% load_config +%% reset_config +%% +%% Also, for each operation which manipulates the config, test +%% get_status and undo_config. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_apps(_Config) -> + Sys = {sys,[{app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}, + {app,runtime_tools,[{incl_cond,exclude}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + {ok,Sasl} = ?msym({ok,#app{name=sasl}}, reltool_server:get_app(Pid,sasl)), + {ok,[#app{name=kernel}, + #app{name=sasl}=Sasl, + #app{name=stdlib}] = White} = + ?msym({ok,_}, reltool_server:get_apps(Pid,whitelist)), + {ok,[#app{name=runtime_tools}] = Black} = + ?msym({ok,_}, reltool_server:get_apps(Pid,blacklist)), + + {ok,Derived} = ?msym({ok,_}, reltool_server:get_apps(Pid,derived)), + true = lists:keymember(tools,#app.name,Derived), + + {ok,Source} = ?msym({ok,_}, reltool_server:get_apps(Pid,source)), + true = lists:keymember(common_test,#app.name,Source), + + %% Check that the four lists are disjoint + Number = length(White) + length(Black) + length(Derived) + length(Source), + WN = lists:usort([N || #app{name=N} <- White]), + BN = lists:usort([N || #app{name=N} <- Black]), + DN = lists:usort([N || #app{name=N} <- Derived]), + SN = lists:usort([N || #app{name=N} <- Source]), + AllN = lists:umerge([WN,BN,DN,SN]), + ?m(Number,length(AllN)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_mod(_Config) -> + Sys = {sys,[{app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}, + {app,runtime_tools,[{incl_cond,exclude}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + %% Read app and get a module from the #app record + {ok,Tools} = ?msym({ok,#app{name=tools}}, reltool_server:get_app(Pid,tools)), + Cover = lists:keyfind(cover,#mod.name,Tools#app.mods), + + %% get_mod - and check that it is equal to the one in #app.mods + ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_sys(_Config) -> + Sys = {sys,[{app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}, + {app,runtime_tools,[{incl_cond,exclude}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + RootDir = code:root_dir(), + ?msym({ok,#sys{root_dir=RootDir,apps=undefined}},reltool_server:get_sys(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +set_app_and_undo(Config) -> + Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {incl_cond, exclude}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + {ok,Cover} = ?msym({ok,#mod{name=cover, is_included=true}}, + reltool_server:get_mod(Pid,cover)), + + %% Exclude one module with set_app + ExclCover = Cover#mod{incl_cond=exclude}, + Mods = Tools#app.mods, + Tools1 = Tools#app{mods = lists:keyreplace(cover,#mod.name,Mods,ExclCover)}, + {ok,ToolsNoCover,["a: Cannot parse app file"++_|_]} = + ?msym({ok,_,["a: Cannot parse app file"++_|_]}, + reltool_server:set_app(Pid,Tools1)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), + + %% Check that the module is no longer included + ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), + {ok,NoIncludeCover} = ?msym({ok,#mod{name=cover, is_included=false}}, + reltool_server:get_mod(Pid,cover)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?msym(ok, reltool_server:undo_config(Pid)), + ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), + ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +set_apps_and_undo(Config) -> + Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(true, Tools#app.is_pre_included), + ?m(true, Tools#app.is_included), + {ok,Cover} = ?msym({ok,#mod{name=cover, is_included=true}}, + reltool_server:get_mod(Pid,cover)), + + %% Exclude one application with set_apps + ExclTools = Tools#app{incl_cond=exclude}, + ?msym({ok,["a: Cannot parse app file"++_]}, + reltool_server:set_apps(Pid,[ExclTools])), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), + + %% Check that the app and its modules (one of them) are no longer included + {ok,NoTools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(false, NoTools#app.is_pre_included), + ?m(false, NoTools#app.is_included), + {ok,NoIncludeCover} = ?msym({ok,#mod{name=cover, is_included=false}}, + reltool_server:get_mod(Pid,cover)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,NoTools}, reltool_server:get_app(Pid,tools)), + ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that escript can be configured, but not its inlined applications +set_apps_inlined(Config) -> + %% Create archive + DataDir = ?config(data_dir,Config), + EscriptDir = filename:join(DataDir,escript), + {ok,{_Archive,Bin}} = zip:create("someapp-1.0.ez",["someapp-1.0"], + [memory, + {cwd,EscriptDir}, + {compress,all}, + {uncompress,[".beam",".app"]}]), + + %% Create the escript + EscriptName = "someapp.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang, + {emu_args,"-escript main mymod"}, + {archive,Bin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = {sys,[{incl_cond, exclude}, + {escript,Escript,[]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?msym({ok,[]},reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,EApp} = ?msym({ok,_}, reltool_server:get_app(Pid,'*escript* someapp')), + {ok,Someapp} = ?msym({ok,_}, reltool_server:get_app(Pid,someapp)), + ?m(undefined, EApp#app.incl_cond), + ?m(undefined, Someapp#app.incl_cond), + ?m(false, Someapp#app.is_included), + ?m(false, Someapp#app.is_pre_included), + + %% Include escript + EApp1 = EApp#app{incl_cond=include}, + ?m({ok,[]}, reltool_server:set_apps(Pid,[EApp1])), + ExpectedEApp = EApp1#app{is_included=true,is_pre_included=true}, + ?m({ok,ExpectedEApp}, reltool_server:get_app(Pid,'*escript* someapp')), + {ok,Someapp1} = ?msym({ok,_}, reltool_server:get_app(Pid,someapp)), + ?m(include, Someapp1#app.incl_cond), + ?m(true, Someapp1#app.is_included), + ?m(true, Someapp1#app.is_pre_included), + + %% Check that inlined app can not be configured + Someapp2 = Someapp1#app{incl_cond=exclude}, + ?msym({error, + "Application someapp is inlined in '*escript* someapp'. " + "Can not change configuration for an inlined application."}, + reltool_server:set_apps(Pid,[Someapp2])), + ?m({ok,Someapp1}, reltool_server:get_app(Pid,someapp)), + + %% Exclude escript + {ok,EApp2} = ?msym({ok,_}, reltool_server:get_app(Pid,'*escript* someapp')), + EApp3 = EApp2#app{incl_cond=exclude}, + ?m({ok,[]}, reltool_server:set_apps(Pid,[EApp3])), + ExpectedEApp3 = EApp3#app{is_included=false,is_pre_included=false}, + ?m({ok,ExpectedEApp3}, reltool_server:get_app(Pid,'*escript* someapp')), + {ok,Someapp3} = ?msym({ok,_}, reltool_server:get_app(Pid,someapp)), + ?m(exclude, Someapp3#app.incl_cond), + ?m(false, Someapp3#app.is_included), + ?m(false, Someapp3#app.is_pre_included), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +set_sys_and_undo(Config) -> + Sys1 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Read sys record + {ok, SysRec} = reltool_server:get_sys(Pid), + + %% Set lib dirs by call to set_sys + NewLib = filename:join(datadir(Config),"faulty_app_file"), + NewLibDirs = [NewLib | SysRec#sys.lib_dirs], + NewSysRec = SysRec#sys{lib_dirs=NewLibDirs}, + ?msym({ok,["a: Cannot parse app file"++_]}, + reltool_server:set_sys(Pid, NewSysRec)), + ?m({ok,NewSysRec}, reltool_server:get_sys(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,SysRec}, reltool_server:get_sys(Pid)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok,reltool_server:undo_config(Pid)), + ?m({ok,NewSysRec}, reltool_server:get_sys(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +load_config_and_undo(Config) -> + Sys1 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok, Sys1}, reltool:get_config(Pid)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(true, Tools1#app.is_pre_included), + ?m(true, Tools1#app.is_included), + {ok,Cover1} = ?msym({ok,#mod{name=cover, + is_included=true, + is_pre_included=true}}, + reltool_server:get_mod(Pid,cover)), + + %% Change tools from include to derived by loading new config + Sys2 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}]}, + ?msym({ok,["a: Cannot parse app file"++_]}, + reltool_server:load_config(Pid,Sys2)), +%%% OTP-0702, 15) ?m({ok, Sys2}, reltool:get_config(Pid)), +%%% Note that {incl_cond,exclude} is removed compared to Sys1 - +%%% config is merged, not overwritten - is this correct??? + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Check that tools is included (since it is used by sasl) but not + %% pre-included (neither included or excluded => undefined) + {ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(undefined, Tools2#app.is_pre_included), + ?m(true, Tools2#app.is_included), + {ok,Cover2} = ?msym({ok,#mod{name=cover, + is_included=true, + is_pre_included=undefined}}, + reltool_server:get_mod(Pid,cover)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that load_config is properly rolled back if it fails +load_config_fail(_Config) -> + Sys1 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok, Sys1}, reltool:get_config(Pid)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + + %% Try to load a config with a faulty rel statement (includes a + %% non-existing application) + Sys2 = {sys,[{incl_cond, exclude}, + {boot_rel, "faulty_rel"}, + {rel, "faulty_rel", "1.0", [kernel, sasl, stdlib, xxx]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}]}, + ?msym({error,"Release \"faulty_rel\" uses non existing application xxx"}, + reltool_server:load_config(Pid,Sys2)), + + %% Check that a rollback is done to the old configuration + ?m({ok, Sys1}, reltool:get_config(Pid,false,false)), + + %% and that tools is not changed (i.e. that the new configuration + %% is not applied) + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with escript + +load_config_escript_path(Config) -> + %% Create escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName = "mymod.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript,8#00744), + + %% Start reltool_server with one escript in configuration + EscriptSys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid1} = ?msym({ok, _}, reltool:start_server([{config, EscriptSys}])), + {ok,[#app{name='*escript* mymod'}=A]} = + ?msym({ok,[_]}, reltool_server:get_apps(Pid1,whitelist)), + ?m(ok, reltool:stop(Pid1)), + + + %% Do same again, but now start reltool first with simple config, + %% then add escript by loading new configuration and check that + %% #app is the same + SimpleSys = + {sys, + [ + {lib_dirs, []} + ]}, + {ok, Pid2} = ?msym({ok, _}, reltool:start_server([{config, SimpleSys}])), + ?m({ok,[]}, reltool_server:get_apps(Pid2,whitelist)), + ?m({ok,[]}, reltool_server:load_config(Pid2,EscriptSys)), + ?m({ok,[A]}, reltool_server:get_apps(Pid2,whitelist)), + + ?m(ok, reltool:stop(Pid2)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with same (source) escript twice and check that the +%% application information is not changed. + +load_config_same_escript_source(_Config) -> + %% Create escript + ExDir = code:lib_dir(reltool, examples), + EscriptName = "display_args", + Escript = filename:join([ExDir, EscriptName]), + + %% Start reltool_server with one escript in configuration + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), +% {ok,[#app{name='*escript* display_args'}]} = + ?msym({ok,[#app{name='*escript* display_args',mods=[_]}]}, + reltool_server:get_apps(Pid,whitelist)), + + %% Load the same config again, then check that app is not changed + ?m({ok,[]}, reltool_server:load_config(Pid,Sys)), + ?msym({ok,[#app{name='*escript* display_args',mods=[_]}]}, + reltool_server:get_apps(Pid,whitelist)), + + ?m(ok, reltool:stop(Pid)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with same (beam) escript twice and check that the +%% application information is not changed. + +load_config_same_escript_beam(Config) -> + %% Create escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName = "mymod.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript,8#00744), + + %% Start reltool_server with one escript in configuration + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + {ok,[#app{name='*escript* mymod'}=A]} = + ?msym({ok,[_]}, reltool_server:get_apps(Pid,whitelist)), + + %% Load the same config again, then check that app is not changed + ?m({ok,[]}, reltool_server:load_config(Pid,Sys)), + ?m({ok,[A]}, reltool_server:get_apps(Pid,whitelist)), + + ?m(ok, reltool:stop(Pid)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with escript + +load_config_add_escript(Config) -> + %% First escript + ExDir = code:lib_dir(reltool, examples), + EscriptName1 = "display_args", + Escript1 = filename:join([ExDir, EscriptName1]), + + %% Second escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName2 = "mymod.escript", + Escript2 = filename:join(?WORK_DIR,EscriptName2), + ok = escript:create(Escript2,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript2,8#00744), + + %% Start reltool_server with one escript in configuration + Sys1 = + {sys, + [ + {lib_dirs, []}, + {escript, Escript2, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + + %% Add second escript by loading new configuration + Sys2 = + {sys, + [ + {lib_dirs, []}, + {escript, Escript1, [{incl_cond, include}]}, + {escript, Escript2, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok,[]} = ?m({ok,[]}, reltool_server:load_config(Pid,Sys2)), + {ok,[#app{name='*escript* display_args'}, + #app{name='*escript* mymod'}]} = + ?msym({ok,[_,_]}, reltool_server:get_apps(Pid,whitelist)), + + ?m(ok, reltool:stop(Pid)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +reset_config_and_undo(Config) -> + Sys1 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {incl_cond, exclude}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok, Sys1}, reltool:get_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(true, Tools1#app.is_pre_included), + ?m(true, Tools1#app.is_included), + {ok,Cover1} = ?msym({ok,#mod{name=cover, + is_included=true, + is_pre_included=true}}, + reltool_server:get_mod(Pid,cover)), + + %% Exclude tools by loading new config + Sys2 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,exclude}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys2)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Check that tools is excluded + {ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(false, Tools2#app.is_pre_included), + ?m(false, Tools2#app.is_included), + {ok,Cover2} = ?msym({ok,#mod{name=cover, + is_included=false, + is_pre_included=false}}, + reltool_server:get_mod(Pid,cover)), + + %% Reset + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:reset_config(Pid)), + ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +gen_rel_files(_Config) -> + %% Configure the server + RelName = "gen_fel_files_test", + RelVsn = "1.0", + Sys = + {sys, + [ + {lib_dirs, []}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [kernel, stdlib]} + ]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + %% Generate .rel, .script and .boot + Dir = filename:join(?WORK_DIR,"gen_rel_files"), + ok = file:make_dir(Dir), + ?m({ok,[]}, reltool_server:gen_rel_files(Pid,Dir)), + + Script = RelName ++ ".script", + Rel = RelName ++ ".rel", + Boot = RelName ++ ".boot", + {ok,Files} = ?msym({ok,_}, file:list_dir(Dir)), + [Boot,Rel,Script] = lists:sort(Files), + + %% Check that contents is reasonable + {ok,[S]} = ?msym({ok,[{script,_,_}]},file:consult(filename:join(Dir,Script))), + ?msym({ok,[{release,_,_,_}]}, file:consult(filename:join(Dir,Rel))), + {ok,Bin} = ?msym({ok,_}, file:read_file(filename:join(Dir,Boot))), + ?m(S,binary_to_term(Bin)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +save_config(Config) -> + PrivDir = ?config(priv_dir,Config), + Sys = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + + Simple = filename:join(PrivDir,"save_simple.reltool"), + ?m(ok, reltool_server:save_config(Pid,Simple,false,false)), + ?m({ok,[Sys]}, file:consult(Simple)), + + Derivates = filename:join(PrivDir,"save_derivates.reltool"), + ?m(ok, reltool_server:save_config(Pid,Derivates,false,true)), + ?msym({ok,[{sys,[{incl_cond, exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}]}, + file:consult(Derivates)), + + Defaults = filename:join(PrivDir,"save_defaults.reltool"), + ?m(ok, reltool_server:save_config(Pid,Defaults,true,false)), + ?msym({ok,[{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include},{vsn,undefined}]}, + {app,sasl,[{incl_cond,include},{vsn,undefined}]}, + {app,stdlib,[{incl_cond,include},{vsn,undefined}]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}]}, + file:consult(Defaults)), + + KVsn = latest(kernel), + StdVsn = latest(stdlib), + + All = filename:join(PrivDir,"save_all.reltool"), + ?m(ok, reltool_server:save_config(Pid,All,true,true)), + ?msym({ok,[{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{vsn,KVsn},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,_},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{vsn,StdVsn},{mod,_,[]}|_]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}]}, + file:consult(All)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test calculation of dependencies +%% The following test applications are used +%% +%% x-1.0: x1.erl x2.erl x3.erl +%% \ / (x2 calls y1, x3 calls y2) +%% y-1.0: y1.erl y2.erl +%% \ (y1 calls z1) +%% z-1.0 z1.erl +%% +%% Test includes x and derives y and z. +%% +dependencies(Config) -> + %% Default: all modules included => y and z are included (derived) + Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[z]}, + #app{name=z}], + rm_missing_app(Der)), + ?msym({ok,[]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding x2 => y still included since y2 is used by x3 + %% z still included since z1 is used by y1 + Sys2 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include},{mod,x2,[{incl_cond,exclude}]}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys2)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der2} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[z]}, + #app{name=z}], + rm_missing_app(Der2)), + ?msym({ok,[]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding x3 => y still included since y1 is used by x2 + %% z still included since z1 is used by y1 + Sys3 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include},{mod,x3,[{incl_cond,exclude}]}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys3)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der3} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[z]}, + #app{name=z}], + rm_missing_app(Der3)), + ?msym({ok,[]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding x2 and x3 => y and z excluded + Sys4 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}, + {mod,x2,[{incl_cond,exclude}]}, + {mod,x3,[{incl_cond,exclude}]}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys4)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der4} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([], rm_missing_app(Der4)), + ?msym({ok,[#app{name=y}, + #app{name=z}]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding y1 => y still included since y2 is used by x3 + %% z excluded since not used by any other than y1 + Sys5 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,derived}, + {mod,y1,[{incl_cond,exclude}]}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys5)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der5} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[]}], rm_missing_app(Der5)), + ?msym({ok,[#app{name=z}]}, + reltool_server:get_apps(Pid,source)), + + ?m(ok, reltool:stop(Pid)), + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -478,6 +2004,20 @@ erl_libs() -> LibStr -> string:tokens(LibStr, ":;") end. +datadir(Config) -> + %% Removes the trailing slash... + filename:nativename(?config(data_dir,Config)). + +latest(App) -> + AppStr = atom_to_list(App), + AppDirs = filelib:wildcard(filename:join(code:lib_dir(),AppStr++"-*")), + [LatestAppDir|_] = lists:reverse(AppDirs), + [_,Vsn] = string:tokens(filename:basename(LatestAppDir),"-"), + Vsn. + +rm_missing_app(Apps) -> + lists:keydelete(?MISSING_APP_NAME,#app.name,Apps). + diff_script(Script, Script) -> equal; diff_script({script, Rel, Commands1}, {script, Rel, Commands2}) -> @@ -610,14 +2150,16 @@ wait_for_process(Node, Name, N) when is_integer(N), N > 0 -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Run escript -run(Dir, Cmd0) -> +run(Dir, Script, Args) -> + Cmd0 = filename:rootname(Script) ++ " " ++ Args, Cmd = case os:type() of {win32,_} -> filename:nativename(Dir) ++ "\\" ++ Cmd0; _ -> Cmd0 end, do_run(Dir, Cmd). -run(Dir, Opts, Cmd0) -> +run(Dir, Opts, Script, Args) -> + Cmd0 = filename:rootname(Script) ++ " " ++ Args, Cmd = case os:type() of {win32,_} -> Opts ++ " " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0; _ -> Opts ++ " " ++ Dir ++ "/" ++ Cmd0 @@ -626,7 +2168,9 @@ run(Dir, Opts, Cmd0) -> do_run(Dir, Cmd) -> io:format("Run: ~p\n", [Cmd]), - Env = [{"PATH",Dir++":"++os:getenv("PATH")}], + Env = [{"PATH",Dir++":"++os:getenv("PATH")}, + {"ERL_FLAGS",""}, % Make sure no flags are set that can override + {"ERL_ZFLAGS",""}], % any of the flags set in the escript. Port = open_port({spawn,Cmd}, [exit_status,eof,in,{env,Env}]), Res = get_data(Port, []), receive diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src index 049e8dd6cc..8ab077d64b 100644 --- a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src +++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src @@ -6,8 +6,19 @@ OTP9229= \ otp_9229/y-1.0/ebin/y.@EMULATOR@ \ otp_9229/y-1.0/ebin/mylib.@EMULATOR@ +DEPENDENCIES= \ + dependencies/x-1.0/ebin/x1.@EMULATOR@ \ + dependencies/x-1.0/ebin/x2.@EMULATOR@ \ + dependencies/x-1.0/ebin/x3.@EMULATOR@ \ + dependencies/y-1.0/ebin/y1.@EMULATOR@ \ + dependencies/y-1.0/ebin/y2.@EMULATOR@ \ + dependencies/z-1.0/ebin/z1.@EMULATOR@ -all: $(OTP9229) +ESCRIPT= \ + escript/someapp-1.0/ebin/mymod.@EMULATOR@ + + +all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) 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 @@ -17,3 +28,19 @@ otp_9229/y-1.0/ebin/y.@EMULATOR@: otp_9229/y-1.0/src/y.erl erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/y.erl otp_9229/y-1.0/ebin/mylib.@EMULATOR@: otp_9229/y-1.0/src/mylib.erl erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/mylib.erl + +dependencies/x-1.0/ebin/x1.@EMULATOR@: dependencies/x-1.0/src/x1.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x1.erl +dependencies/x-1.0/ebin/x2.@EMULATOR@: dependencies/x-1.0/src/x2.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x2.erl +dependencies/x-1.0/ebin/x3.@EMULATOR@: dependencies/x-1.0/src/x3.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x3.erl +dependencies/y-1.0/ebin/y1.@EMULATOR@: dependencies/y-1.0/src/y1.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y1.erl +dependencies/y-1.0/ebin/y2.@EMULATOR@: dependencies/y-1.0/src/y2.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y2.erl +dependencies/z-1.0/ebin/z1.@EMULATOR@: dependencies/z-1.0/src/z1.erl + erlc $(EFLAGS) -odependencies/z-1.0/ebin dependencies/z-1.0/src/z1.erl + +escript/someapp-1.0/ebin/mymod.@EMULATOR@: escript/someapp-1.0/src/mymod.erl + erlc $(EFLAGS) -oescript/someapp-1.0/ebin escript/someapp-1.0/src/mymod.erl diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/ebin/x.app new file mode 100644 index 0000000000..ccaab8a8c7 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, x, + [{description, "Main application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [x1,x2,x3]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x1.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x1.erl new file mode 100644 index 0000000000..bf1e7f9279 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x1.erl @@ -0,0 +1,5 @@ +-module(x1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x2.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x2.erl new file mode 100644 index 0000000000..82191ba278 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x2.erl @@ -0,0 +1,5 @@ +-module(x2). +-compile(export_all). + +f() -> + y1:f(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x3.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x3.erl new file mode 100644 index 0000000000..618c75c9a7 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x3.erl @@ -0,0 +1,5 @@ +-module(x3). +-compile(export_all). + +f() -> + y2:f(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app new file mode 100644 index 0000000000..d9dac371d7 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, y, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [y1,y2]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y1.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y1.erl new file mode 100644 index 0000000000..dd21b33292 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y1.erl @@ -0,0 +1,5 @@ +-module(y1). +-compile(export_all). + +f() -> + z1:f(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y2.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y2.erl new file mode 100644 index 0000000000..bf8ddf6080 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y2.erl @@ -0,0 +1,5 @@ +-module(y2). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/ebin/z.app new file mode 100644 index 0000000000..437a0968e9 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/ebin/z.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, z, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [z1]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/src/z1.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/src/z1.erl new file mode 100644 index 0000000000..97ef90b87f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/src/z1.erl @@ -0,0 +1,5 @@ +-module(z1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dupl_mod/a-1.0/ebin/a.app b/lib/reltool/test/reltool_server_SUITE_data/dupl_mod/a-1.0/ebin/a.app new file mode 100644 index 0000000000..fada34847a --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dupl_mod/a-1.0/ebin/a.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, a, + [{description, "Application with duplicated module name in .app file"}, + {vsn, "1.0"}, + {modules, [a,a]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/ebin/someapp.app b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/ebin/someapp.app new file mode 100644 index 0000000000..ea2209941e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/ebin/someapp.app @@ -0,0 +1,6 @@ +%% -*- erlang -*- +{application, someapp, + [{description, "Some app for reltool test including archives in escripts"}, + {vsn, "1.0"}, + {modules, [someapp]}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl new file mode 100644 index 0000000000..b6c71c666d --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl @@ -0,0 +1,26 @@ +%% ``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(mymod). + +-export([main/1]). + +%%%----------------------------------------------------------------- +%%% escript main function +main(Args) -> + io:format("Root dir: ~s\n", [code:root_dir()]), + io:format("Script args: ~p\n", [Args]). diff --git a/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/ebin/a.app b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/ebin/a.app new file mode 100644 index 0000000000..ea77103598 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/ebin/a.app @@ -0,0 +1 @@ +faulty app file diff --git a/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a.erl b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/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/faulty_app_file/a-1.0/src/a_sup.erl b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/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/reltool/test/reltool_server_SUITE_data/sort_apps/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/x-1.0/ebin/x.app new file mode 100644 index 0000000000..5fa2a92969 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, x, + [{description, "Application in reltool sort app test - circular dependency"}, + {vsn, "1.0"}, + {modules,[]}, + {registered, []}, + {applications, [kernel, stdlib, y]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/y-1.0/ebin/y.app new file mode 100644 index 0000000000..c4bc62f55f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, y, + [{description, "Application in reltool sort app test - circular dependency"}, + {vsn, "1.0"}, + {modules,[]}, + {registered, []}, + {applications, [kernel, stdlib, x]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app new file mode 100644 index 0000000000..8608bc554b --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app @@ -0,0 +1,8 @@ +% -*-erlang-*- +{application, z, + [{description, "Application in reltool sort app test - included applications"}, + {vsn, "1.0"}, + {modules,[]}, + {registered, []}, + {applications, [kernel, stdlib, sasl, inets]}, + {included_applications, [tools, mnesia]}]}. diff --git a/lib/reltool/test/reltool_test_lib.erl b/lib/reltool/test/reltool_test_lib.erl index b8bcbcd009..61f783190c 100644 --- a/lib/reltool/test/reltool_test_lib.erl +++ b/lib/reltool/test/reltool_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -138,10 +138,6 @@ end_per_testcase(_Func, Config) when is_list(Config) -> reset_kill_timer(Config), Config. -%% Backwards compatible with test_server -tc_info(suite) -> []; -tc_info(doc) -> "". - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Use ?log(Format, Args) as wrapper diff --git a/lib/reltool/test/reltool_test_lib.hrl b/lib/reltool/test/reltool_test_lib.hrl index b592ebb2f0..0dfc08b81c 100644 --- a/lib/reltool/test/reltool_test_lib.hrl +++ b/lib/reltool/test/reltool_test_lib.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -18,20 +18,10 @@ -include_lib("wx/include/wx.hrl"). --define(flat_format(Format,Args), lists:flatten(io_lib:format(Format,Args))). -define(log(Format,Args), reltool_test_lib:log(Format,Args,?FILE,?LINE)). --define(warning(Format,Args), ?log("<WARNING>\n " ++ Format,Args)). -define(error(Format,Args), reltool_test_lib:error(Format,Args,?FILE,?LINE)). -define(verbose(Format,Args), reltool_test_lib:verbose(Format,Args,?FILE,?LINE)). --define(fatal(Format,Args), - ?error(Format, Args), - exit({test_case_fatal, Format, Args, ?FILE, ?LINE})). - --define(skip(Format,Args), - ?warning(Format, Args), - exit({skipped, ?flat_format(Format, Args)})). - -define(ignore(Expr), fun() -> AcTuAlReS = (catch (Expr)), @@ -68,25 +58,3 @@ AcTuAlReS end end()). - --define(m_receive(ExpectedMsg), - ?m(ExpectedMsg,reltool_test_lib:pick_msg())). - --define(m_multi_receive(ExpectedMsgs), - fun() -> - TmPeXpCtEdMsGs = lists:sort(ExpectedMsgs), - AcTuAlReS = - lists:sort(lists:map(fun(_) -> - reltool_test_lib:pick_msg() - end, TmPeXpCtEdMsGs)), - case AcTuAlReS of - TmPeXpCtEdMsGs -> - ?verbose("ok: ~p\n",[AcTuAlReS]), - AcTuAlReS; - _ -> - reltool_test_lib:error("Not matching actual result was:\n ~p \nExpected ~p\n", - [AcTuAlReS, ExpectedMsgs], - ?FILE, ?LINE), - AcTuAlReS - end - end()). diff --git a/lib/reltool/test/reltool_wx_SUITE.erl b/lib/reltool/test/reltool_wx_SUITE.erl index 424bc7d189..13d71f4fd6 100644 --- a/lib/reltool/test/reltool_wx_SUITE.erl +++ b/lib/reltool/test/reltool_wx_SUITE.erl @@ -1,7 +1,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 @@ -61,9 +61,46 @@ start_all_windows(TestInfo) when is_atom(TestInfo) -> reltool_test_lib:tc_info(TestInfo); start_all_windows(_Config) -> {ok, SysPid} = ?msym({ok, _}, reltool:start([{trap_exit, false}])), + erlang:monitor(process,SysPid), {ok, AppPid} = ?msym({ok, _}, reltool_sys_win:open_app(SysPid, stdlib)), - ?msym({ok, _}, reltool_app_win:open_mod(AppPid, escript)), + erlang:monitor(process,AppPid), + {ok, ModPid} = ?msym({ok, _}, reltool_app_win:open_mod(AppPid, escript)), + erlang:monitor(process,ModPid), + + %% Let all windows get started timer:sleep(timer:seconds(10)), + + %% Test that server pid can be fetched, and that server is alive + {ok, Server} = ?msym({ok,_}, reltool:get_server(SysPid)), + ?m(true, erlang:is_process_alive(Server)), + ?m({ok,{sys,[]}}, reltool:get_config(Server)), + + %% Terminate + check_no_win_crash(), ?m(ok, reltool:stop(SysPid)), - + wait_terminate([{sys,SysPid},{app,AppPid},{mod,ModPid}]), + ok. + + +%%%----------------------------------------------------------------- +%%% Internal functions +check_no_win_crash() -> + receive {'DOWN',_,_,_,_} = Down -> + ct:log("Unexpected termination of window:~n~p",[Down]), + ct:fail("window crashed") + after 0 -> + ok + end. + +wait_terminate([]) -> + ok; +wait_terminate([{Win,P}|Rest]) -> + receive + {'DOWN',_,process,P,shutdown} -> + wait_terminate(Rest); + {'DOWN',_,process,P,Reason} -> + ct:log("~p window terminated with unexpected reason:~n~p", + [Win,Reason]), + ct:fail("unexpected exit reason from window") + end. diff --git a/lib/runtime_tools/c_src/Makefile.in b/lib/runtime_tools/c_src/Makefile.in index 3d9a7ed69d..120f9b913f 100644 --- a/lib/runtime_tools/c_src/Makefile.in +++ b/lib/runtime_tools/c_src/Makefile.in @@ -21,6 +21,11 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk include $(ERL_TOP)/make/$(TARGET)/otp_ded.mk # ---------------------------------------------------- +# Items from top-level configure +# ---------------------------------------------------- +DTRACE_ENABLED=@DTRACE_ENABLED@ +DTRACE_ENABLED_2STEP=@DTRACE_ENABLED_2STEP@ +# ---------------------------------------------------- # Application version # ---------------------------------------------------- include ../vsn.mk @@ -38,6 +43,8 @@ SHELL = /bin/sh LIBS = $(DED_LIBS) LDFLAGS += $(DED_LDFLAGS) +DTRACE_LIBNAME = dyntrace + SYSINCLUDE = $(DED_SYS_INCLUDE) ifeq ($(findstring vxworks,$(TARGET)),vxworks) SYSINCLUDE += -I$(ERL_TOP)/erts/etc/vxworks @@ -45,15 +52,20 @@ endif TRACE_DRV_INCLUDES = $(SYSINCLUDE) -ALL_CFLAGS = $(CFLAGS) @DEFS@ $(TYPE_FLAGS) $(TRACE_DRV_INCLUDES) - +ALL_CFLAGS = $(CFLAGS) @DEFS@ $(TYPE_FLAGS) $(TRACE_DRV_INCLUDES) \ + -I$(OBJDIR) -I$(ERL_TOP)/erts/emulator/$(TARGET) ifeq ($(TYPE),debug) TYPEMARKER = .debug -TYPE_FLAGS = -g -DDEBUG @DEBUG_FLAGS@ +TYPE_FLAGS = $(subst -O3,,$(subst -O2,,$(CFLAGS))) -DDEBUG @DEBUG_FLAGS@ +else +ifeq ($(TYPE),valgrind) +TYPEMARKER = .valgrind +TYPE_FLAGS = $(subst -O3,,$(subst -O2,,$(CFLAGS))) -DVALGRIND else TYPEMARKER = -TYPE_FLAGS = -O2 +TYPE_FLAGS = $(CFLAGS) +endif endif ROOTDIR = $(ERL_TOP)/lib @@ -69,6 +81,16 @@ RELSYSDIR = $(RELEASE_PATH)/lib/runtime_tools-$(VSN) # ---------------------------------------------------- # Misc Macros # ---------------------------------------------------- +before_DTrace_OBJS = $(OBJDIR)/dyntrace$(TYPEMARKER).o +## NIF_MAKEFILE = $(PRIVDIR)/Makefile + +# Higher-level makefiles says that we can only compile on UNIX flavors +NIF_LIB = $(LIBDIR)/dyntrace$(TYPEMARKER).@DED_EXT@ + +ifeq ($(HOST_OS),) +HOST_OS := $(shell $(ERL_TOP)/erts/autoconf/config.guess) +endif + TRACE_IP_DRV_OBJS = \ $(OBJDIR)/trace_ip_drv.o @@ -91,7 +113,44 @@ endif _create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR)) -debug opt: $(SOLIBS) +debug opt valgrind: $(SOLIBS) $(OBJDIR) $(LIBDIR) $(NIF_LIB) + +ifdef DTRACE_ENABLED +DTRACE_USER_HEADER=$(OBJDIR)/dtrace_user.h +$(OBJDIR)/dtrace_user.h: ./dtrace_user.d + dtrace -h -C $(INCLUDES) \ + -s ./dtrace_user.d \ + -o ./dtrace_user.tmp + sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./dtrace_user.tmp > $@ + rm ./dtrace_user.tmp +else +DTRACE_USER_HEADER= +endif + +DTRACE_OBJS = +ifdef DTRACE_ENABLED_2STEP +DTRACE_OBJS += $(OBJDIR)/dtrace_user.o +$(OBJDIR)/dtrace_user.o: $(before_DTrace_OBJS) $(OBJDIR)/dtrace_user.h + dtrace -G -C \ + -s ./dtrace_user.d \ + -o $@ $(before_DTrace_OBJS) +endif + +DYNTRACE_OBJS = $(before_DTrace_OBJS) $(DTRACE_OBJS) + +$(OBJDIR): + -@mkdir -p $(OBJDIR) + +$(LIBDIR): + -@mkdir -p $(LIBDIR) + +$(OBJDIR)/dyntrace$(TYPEMARKER).o: dyntrace.c $(DTRACE_USER_HEADER) + $(INSTALL_DIR) $(OBJDIR) + $(CC) -c -o $@ $(ALL_CFLAGS) $< + +$(NIF_LIB): $(DYNTRACE_OBJS) + $(INSTALL_DIR) $(LIBDIR) + $(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(OBJDIR)/%.o: %.c $(CC) -c -o $@ $(ALL_CFLAGS) $< @@ -118,6 +177,12 @@ $(LIBDIR)/trace_file_drv.eld: $(TRACE_FILE_DRV_OBJS) clean: rm -f $(SOLIBS) $(TRACE_IP_DRV_OBJS) $(TRACE_FILE_DRV_OBJS) + rm -f $(LIBDIR)/dyntrace.@DED_EXT@ + rm -f $(LIBDIR)/dyntrace.debug.@DED_EXT@ + rm -f $(LIBDIR)/dyntrace.valgrind.@DED_EXT@ + rm -f $(OBJDIR)/dyntrace.o + rm -f $(OBJDIR)/dyntrace.debug.o + rm -f $(OBJDIR)/dyntrace.valgrind.o rm -f core *~ docs: @@ -128,8 +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) $(SOLIBS) $(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 new file mode 100644 index 0000000000..45d3ef3b66 --- /dev/null +++ b/lib/runtime_tools/c_src/dtrace_user.d @@ -0,0 +1,53 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +provider erlang { + /** + * Send a single string to a probe. + * + * @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. + * + * @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 +#pragma D attributes Private/Private/Common provider erlang module +#pragma D attributes Private/Private/Common provider erlang function +#pragma D attributes Evolving/Evolving/Common provider erlang name +#pragma D attributes Evolving/Evolving/Common provider erlang args diff --git a/lib/runtime_tools/c_src/dyntrace.c b/lib/runtime_tools/c_src/dyntrace.c new file mode 100644 index 0000000000..d94014559d --- /dev/null +++ b/lib/runtime_tools/c_src/dyntrace.c @@ -0,0 +1,172 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +/* + * Purpose: Dynamically loadable NIF library for DTrace + */ + + + +#include "erl_nif.h" +#include "config.h" +#include "sys.h" +#include "dtrace-wrapper.h" +#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP)) +#define HAVE_USE_DTRACE 1 +#endif +#ifdef HAVE_USE_DTRACE +#include "dtrace_user.h" +#endif + +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 VALGRIND + # include <valgrind/memcheck.h> +#endif + +#ifdef __GNUC__ + # define INLINE __inline__ +#else + # define INLINE +#endif + +#define MESSAGE_BUFSIZ 1024 + +/* NIF interface declarations */ +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); + +/* The NIFs: */ +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 ErlNifFunc nif_funcs[] = { + {"available", 0, available}, + {"user_trace_s1", 1, user_trace_s1}, + {"user_trace_i4s4", 9, user_trace_i4s4} +}; + +ERL_NIF_INIT(dyntrace, nif_funcs, load, NULL, NULL, NULL) + +static ERL_NIF_TERM atom_true; +static ERL_NIF_TERM atom_false; +static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_not_available; +static ERL_NIF_TERM atom_badarg; +static ERL_NIF_TERM atom_ok; + +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_error = enif_make_atom(env,"error"); + atom_not_available = enif_make_atom(env,"not_available"); + atom_badarg = enif_make_atom(env,"badarg"); + atom_ok = enif_make_atom(env,"ok"); + + return 0; +} + +static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef HAVE_USE_DTRACE + return atom_true; +#else + return atom_false; +#endif +} + +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; + } +#else + return atom_error; +#endif +} + +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; + } +} + +static ERL_NIF_TERM user_trace_i4s4(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; + } +#else + return atom_error; +#endif +} diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile index dbbae81cfe..f1dd788f80 100644 --- a/lib/runtime_tools/doc/src/Makefile +++ b/lib/runtime_tools/doc/src/Makefile @@ -40,7 +40,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = ref_man.xml -XML_REF3_FILES = dbg.xml erts_alloc_config.xml +XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml XML_REF6_FILES = runtime_tools_app.xml XML_PART_FILES = part_notes.xml part_notes_history.xml diff --git a/lib/runtime_tools/doc/src/dyntrace.xml b/lib/runtime_tools/doc/src/dyntrace.xml new file mode 100644 index 0000000000..5fc5530f25 --- /dev/null +++ b/lib/runtime_tools/doc/src/dyntrace.xml @@ -0,0 +1,209 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>1996</year><year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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. + + </legalnotice> + + <title>dyntrace</title> + <prepared>Patrik Nyblom</prepared> + <responsible></responsible> + <docno>1</docno> + <approved>ETX/B/SFP (Kenneth Lundin)</approved> + <checked></checked> + <date>12-03-20</date> + <rev>A</rev> + <file>dyntrace.xml</file> + </header> + <module>dyntrace</module> + <modulesummary>Interface to dynamic tracing</modulesummary> + <description> + <p>This module implements interfaces to dynamic tracing, should such be compiled into the virtual machine. For a standard and/or commercial build, no dynamic tracing is available, in which case none of the functions in this module is usable or give any effect.</p> + <p>Should dynamic tracing be enabled in the current build, either by configuring with <c>./configure --with-dynamic-trace=dtrace</c> or with <c>./configure --with-dynamic-trace=systemtap</c>, the module can be used for two things:</p> + <list type="bulleted"> + <item>Trigger the user-probe <c>user_trace_i4s4</c> in the NIF library <c>dyntrace.so</c> by calling <c>dyntrace:p/{1,2,3,4,5,6,7,8}</c>.</item> + <item>Set a user specified tag that will be present in the trace messages of both the <c>efile_drv</c> and the user-probe mentioned above.</item> + </list> + <p>Both building with dynamic trace probes and using them is experimental and unsupported by Erlang/OTP. It is included as an option for the developer to trace and debug performance issues in their systems.</p> + <p>The original implementation is mostly done by Scott Lystiger Fritchie as an Open Source Contribution and it should be viewed as such even though the source for dynamic tracing as well as this module is included in the main distribution. However, the ability to use dynamic tracing of the virtual machine is a very valuable contribution which OTP has every intention to maintain as a tool for the developer.</p> + <p>How to write <c>d</c> programs or <c>systemtap</c> scripts can be learned from books and from a lot of pages on the Internet. This manual page does not include any documentation about using the dynamic trace tools of respective platform. The <c>examples</c> directory of the <c>runtime_tools</c> application however contains comprehensive examples of both <c>d</c> and <c>systemtap</c> programs that will help you get started. Another source of information is the <c>README.dtrace(.md)</c> and <c>README.systemtap(.md)</c> files in the Erlang source top directory.</p> + </description> + <funcs> + <func> + <name>available() -> boolean()</name> + <fsummary>Check if dynamic tracing is available</fsummary> + <desc> + <p>This function uses the NIF library to determine if dynamic + tracing is available. Usually calling <seealso + marker="erts:erlang#system_info/1">erlang:system_info/1</seealso> + is a better indicator of the availability of dynamic + tracing.</p> + <p>The function will throw an exception if the <c>dyntrace</c> NIF library could not be loaded by the on_load function of this module.</p> + </desc> + </func> + <func> + <name>p() -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message only containing the user tag and zeroes/empty strings in all other fields.</p> + </desc> + </func> + <func> + <name>p(integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer or string parameter in the first integer/string field.</p> + </desc> + </func> + <func> + <name>p(integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters. I.e. <c>p(1,"Hello")</c> is ok, as is <c>p(1,1)</c> and <c>p("Hello","Again")</c>, but not <c>p("Hello",1)</c>.</p> + </desc> + </func> + <func> + <name>p(integer() | string(), integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + </desc> + </func> + <func> + <name>p(integer() | string(), integer() | string(), integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + </desc> + </func> + <func> + <name>p(integer(), integer() | string(), integer() | string(), integer() | string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + <p>There can be no more than four parameters of any type (integer() or string()), so the first parameter has to be an integer() and the last a string().</p> + </desc> + </func> + <func> + <name>p(integer(), integer(), integer() | string(), integer() | string(), string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + <p>There can be no more than four parameters of any type (integer() or string()), so the first two parameters has to be integer()'s and the last two string()'s.</p> + </desc> + </func> + <func> + <name>p(integer(), integer(), integer(), integer() | string(), string(), string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + <p>There can be no more than four parameters of any type (integer() or string()), so the first three parameters has to be integer()'s and the last three string()'s.</p> + </desc> + </func> + <func> + <name>p(integer(), integer(), integer(), integer(), string(), string(), string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing all the integer()'s and string()'s provided, as well as any user tag set in the current process.</p> + </desc> + </func> + <func> + <name>get_tag() -> binary() | undefined</name> + <fsummary>Get the user tag set in the process.</fsummary> + <desc> + <p>This function returns the user tag set in the current + process. If no tag is set or dynamic tracing is not available, + it returns <c>undefined</c></p> + </desc> + </func> + <func> + <name>get_tag() -> binary() | undefined</name> + <fsummary>Get the user tag set in the process or sent to the process.</fsummary> + <desc> + <p>This function returns the user tag set in the current + process or, if no user tag is present, the last user tag sent + to the process together with a message (in the same way as + <seealso marker="kernel:seq_trace">sequential trace + tokens</seealso> are spread to other processes together with + messages. For an explanation of how user tags can be spread + together with messages, see <seealso + marker="#spread_tag/1">spread_tag/1</seealso>. If no tag is + found or dynamic tracing is not available, it returns + <c>undefined</c></p> + </desc> + </func> + + <func> + <name>put_tag(Item) -> binary() | undefined </name> + <fsummary>Set the user tag of the current process.</fsummary> + <type> + <v>Item = iodata()</v> + </type> + <desc> + <p>This function sets the user tag of the current process. The user tag is a binary(), but can be specified as any iodata(), which is automatically converted to a binary by this function.</p> + <p>The user tag is provided to the user probes triggered by calls top <c>dyntrace:p/{1,2,3,4,5,6,7,8}</c> as well as probes in the efile_driver. In the future, user tags might be added to more probes.</p> + <p>The old user tag (if any) is returned, or <c>undefined</c> if no user tag was present or dynamic tracing is not enabled.</p> + </desc> + </func> + <func> + <name>spread_tag(boolean()) -> TagData</name> + <fsummary>Start or stop spreading dynamic trace user tags with the next message.</fsummary> + <type> + <v>TagData = opaque data that can be used as parameter to <seealso marker="#restore_tag/1">restore_tag/1</seealso></v> + </type> + <desc> + <p>This function controls if user tags are to be spread to other processes with the next message. Spreading of user tags work like spreading of sequential trace tokens, so that a received user tag will be active in the process until the next message arrives (if that message does not also contain the user tag.</p> + <p>This functionality is used when a client process communicates with a file i/o-server to spread the user tag to the I/O-server and then down to the efile_drv driver. By using <c>spread_tag/1</c> and <c>restore_tag/1</c>, one can enable or disable spreading of user tags to other processes and then restore the previous state of the user tag. The TagData returned from this call contains all previous information so the state (including any previously spread user tags) will be completely restored by a later call to <c>restore_tag/1</c>.</p> + <p>The <seealso marker="kernel:file">file</seealso> module already spread's tags, so there is noo need to manually call these function to get user tags spread to the efile driver through that module.</p> + <p>The most use of this function would be if one for example uses the <seealso marker="stdlib:io">io</seealso> module to communicate with an I/O-server for a regular file, like in the following example:</p> +<pre> +f() -> + {ok, F} = file:open("test.tst",[write]), + Saved = dyntrace:spread_tag(true), + io:format(F,"Hello world!",[]), + dyntrace:restore_tag(Saved), + file:close(F). +</pre> + <p>In this example, any user tag set in the calling process will be spread to the I/O-server when the io:format call is done.</p> + </desc> + </func> + <func> + <name>restore_tag(TagData) -> true</name> + <fsummary>Restore to a previous state of user tag spreading.</fsummary> + <type> + <v>TagData = opaque data returned by <seealso marker="#spread_tag/1">spread_tag/1</seealso></v> + </type> + <desc> + <p>Restores the previous state of user tags and their spreading as it was before a call to <seealso marker="#spread_tag/1">spread_tag/1</seealso>. Note that the restoring is not limited to the same process, one can utilize this to turn off spreding in one process and restore it in a newly created, the one that actually is going to send messages:</p> +<pre> +f() -> + TagData=dyntrace:spread_tag(false), + spawn(fun() -> + dyntrace:restore_tag(TagData), + do_something() + end), + do_something_else(), + dyntrace:restore_tag(TagData). +</pre> + <p>Correctly handling user tags and their spreading might take some effort, as Erlang programs tend to send and receive messages so that sometimes the user tag gets lost due to various things, like double receives or communication with a port (ports do not handle user tags, in the same way as they do not handle regular sequential trace tokens).</p> + </desc> + </func> + </funcs> + </erlref> + diff --git a/lib/runtime_tools/doc/src/ref_man.xml b/lib/runtime_tools/doc/src/ref_man.xml index 579efcd969..41fa5f17c4 100644 --- a/lib/runtime_tools/doc/src/ref_man.xml +++ b/lib/runtime_tools/doc/src/ref_man.xml @@ -33,6 +33,7 @@ </description> <xi:include href="runtime_tools_app.xml"/> <xi:include href="dbg.xml"/> + <xi:include href="dyntrace.xml"/> <xi:include href="erts_alloc_config.xml"/> </application> diff --git a/lib/runtime_tools/examples/dist.d b/lib/runtime_tools/examples/dist.d new file mode 100644 index 0000000000..550e10d363 --- /dev/null +++ b/lib/runtime_tools/examples/dist.d @@ -0,0 +1,62 @@ +/* example usage: dtrace -q -s /path/to/dist.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +erlang*:::dist-monitor +{ + printf("monitor: pid %d, who %s, what %s, node %s, type %s, reason %s\n", + pid, + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3), + copyinstr(arg4)); +} + +erlang*:::dist-port_busy +{ + printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); + /* + * For variable use advice, see: + * http://dtrace.org/blogs/brendan/2011/11/25/dtrace-variable-types/ + * + * Howevever, it's quite possible for the blocked events to span + * threads, so we'll use globals. + */ + blocked_procs[copyinstr(arg3)] = timestamp; +} + +erlang*:::dist-output +{ + printf("dist output: node %s, port %s, remote_node %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::dist-outputv +{ + printf("port outputv: node %s, port %s, remote_node %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::process-scheduled +/blocked_procs[copyinstr(arg0)]/ +{ + pidstr = copyinstr(arg0); + printf("blocked pid %s scheduled now, waited %d microseconds\n", + pidstr, (timestamp - blocked_procs[pidstr]) / 1000); + blocked_procs[pidstr] = 0; +} diff --git a/lib/runtime_tools/examples/dist.systemtap b/lib/runtime_tools/examples/dist.systemtap new file mode 100644 index 0000000000..af27b2cab6 --- /dev/null +++ b/lib/runtime_tools/examples/dist.systemtap @@ -0,0 +1,76 @@ +/* example usage: stap /path/to/dist.systemtap -x <pid> */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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("dist-monitor") +{ + printf("monitor: pid %d, who %s, what %s, node %s, type %s, reason %s\n", + pid(), + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4), + user_string($arg5)); +} + +probe process("beam").mark("dist-port_busy") +{ + printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); + blocked_procs[user_string($arg4)] = timestamp; +} + +probe process("beam").mark("dist-port_busy") +{ + printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); + blocked_procs[user_string($arg4)] = timestamp; +} + +probe process("beam").mark("dist-output") +{ + printf("dist output: node %s, port %s, remote_node %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("dist-outputv") +{ + printf("port outputv: node %s, port %s, remote_node %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("process-scheduled") +{ + pidstr = user_string($arg1); + if (pidstr in blocked_procs) { + printf("blocked pid %s scheduled now, waited %d microseconds\n", + pidstr, (timestamp - blocked_procs[pidstr]) / 1000); + delete blocked_procs[pidstr]; + } +} + +global blocked_procs; diff --git a/lib/runtime_tools/examples/driver1.d b/lib/runtime_tools/examples/driver1.d new file mode 100644 index 0000000000..9f53ffeb2a --- /dev/null +++ b/lib/runtime_tools/examples/driver1.d @@ -0,0 +1,114 @@ +/* example usage: dtrace -q -s /path/to/driver1.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +erlang*:::driver-init +{ + printf("driver init name %s major %d minor %d flags %d\n", + copyinstr(arg0), arg1, arg2, arg3); +} + +erlang*:::driver-start +{ + printf("driver start pid %s driver name %s port %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-stop +{ + printf("driver stop pid %s driver name %s port %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-finish +{ + printf("driver finish driver name %s port %s\n", + copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::driver-flush +{ + printf("driver flush pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-output +{ + printf("driver output pid %s port %s port name %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::driver-outputv +{ + printf("driver outputv pid %s port %s port name %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::driver-control +{ + printf("driver control pid %s port %s port name %s command %d bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3, arg4); +} + +erlang*:::driver-call +{ + printf("driver call pid %s port %s port name %s command %d bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3, arg4); +} + +erlang*:::driver-event +{ + printf("driver event pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-ready_input +{ + printf("driver ready_input pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-ready_output +{ + printf("driver ready_output pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-timeout +{ + printf("driver timeout pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-ready_async +{ + printf("driver ready_async pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-process_exit +{ + printf("driver process_exit pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-stop_select +{ + printf("driver stop_select driver name %s\n", copyinstr(arg0)); +} diff --git a/lib/runtime_tools/examples/driver1.systemtap b/lib/runtime_tools/examples/driver1.systemtap new file mode 100644 index 0000000000..8b99e465b7 --- /dev/null +++ b/lib/runtime_tools/examples/driver1.systemtap @@ -0,0 +1,125 @@ +/* example usage: stap /path/to/driver1.systemtap -x <pid> */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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("driver-init") +{ + printf("driver init name %s major %d minor %d flags %d\n", + user_string($arg1), $arg2, $arg3, $arg4); +} + +probe process("beam").mark("driver-start") +{ + printf("driver start pid %s driver name %s port %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-stop") +{ + printf("driver stop pid %s driver name %s port %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-finish") +{ + printf("driver finish driver name %s\n", + user_string($arg1)); +} + +probe process("beam").mark("driver-flush") +{ + printf("driver flush pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-output") +{ + printf("driver output pid %s port %s port name %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("driver-outputv") +{ + printf("driver outputv pid %s port %s port name %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("driver-control") +{ + printf("driver control pid %s port %s port name %s command %d bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); +} + +probe process("beam").mark("driver-call") +{ + printf("driver call pid %s port %s port name %s command %d bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); +} + +probe process("beam").mark("driver-event") +{ + printf("driver event pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-ready_input") +{ + printf("driver ready_input pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-ready_output") +{ + printf("driver ready_output pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-timeout") +{ + printf("driver timeout pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-ready_async") +{ + printf("driver ready_async pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-process_exit") +{ + printf("driver process_exit pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-stop_select") +{ + printf("driver stop_select driver name %s\n", user_string($arg1)); +} diff --git a/lib/runtime_tools/examples/efile_drv.d b/lib/runtime_tools/examples/efile_drv.d new file mode 100644 index 0000000000..085995ce58 --- /dev/null +++ b/lib/runtime_tools/examples/efile_drv.d @@ -0,0 +1,104 @@ +/* example usage: dtrace -q -s /path/to/efile_drv.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +BEGIN +{ + op_map[1] = "OPEN"; + op_map[2] = "READ"; + op_map[3] = "LSEEK"; + op_map[4] = "WRITE"; + op_map[5] = "FSTAT"; + op_map[6] = "PWD"; + op_map[7] = "READDIR"; + op_map[8] = "CHDIR"; + op_map[9] = "FSYNC"; + op_map[10] = "MKDIR"; + op_map[11] = "DELETE"; + op_map[12] = "RENAME"; + op_map[13] = "RMDIR"; + op_map[14] = "TRUNCATE"; + op_map[15] = "READ_FILE"; + op_map[16] = "WRITE_INFO"; + op_map[19] = "LSTAT"; + op_map[20] = "READLINK"; + op_map[21] = "LINK"; + op_map[22] = "SYMLINK"; + op_map[23] = "CLOSE"; + op_map[24] = "PWRITEV"; + op_map[25] = "PREADV"; + op_map[26] = "SETOPT"; + op_map[27] = "IPREAD"; + op_map[28] = "ALTNAME"; + op_map[29] = "READ_LINE"; + op_map[30] = "FDATASYNC"; + op_map[31] = "FADVISE"; +} + +erlang*:::aio_pool-add +{ + printf("async I/O pool port %s queue len %d\n", copyinstr(arg0), arg1); +} + +erlang*:::aio_pool-get +{ + printf("async I/O pool port %s queue len %d\n", copyinstr(arg0), arg1); +} + +erlang*:::efile_drv-entry +{ + printf("efile_drv enter tag={%d,%d} %s%s | %s (%d) | args: %s %s , %d %d (port %s)\n", + arg0, arg1, + arg2 == NULL ? "" : "user tag ", + arg2 == NULL ? "" : copyinstr(arg2), + op_map[arg3], arg3, + arg4 == NULL ? "" : copyinstr(arg4), + arg5 == NULL ? "" : copyinstr(arg5), arg6, arg7, + /* NOTE: port name in args[10] is experimental */ + (args[10] == NULL) ? + "?" : copyinstr((user_addr_t) args[10])); +} + +erlang*:::efile_drv-int* +{ + printf("async I/O worker tag={%d,%d} | %s (%d) | %s\n", + arg0, arg1, op_map[arg2], arg2, probename); +} + +/* efile_drv-return error case */ +erlang*:::efile_drv-return +/arg4 == 0/ +{ + printf("efile_drv return tag={%d,%d} %s%s | %s (%d) | errno %d\n", + arg0, arg1, + arg2 == NULL ? "" : "user tag ", + arg2 == NULL ? "" : copyinstr(arg2), + op_map[arg3], arg3, + arg5); +} + +/* efile_drv-return success case */ +erlang*:::efile_drv-return +/arg4 != 0/ +{ + printf("efile_drv return tag={%d,%d} %s | %s (%d) ok\n", + arg0, arg1, + arg2 == NULL ? "" : copyinstr(arg2), + op_map[arg3], arg3); +} diff --git a/lib/runtime_tools/examples/efile_drv.systemtap b/lib/runtime_tools/examples/efile_drv.systemtap new file mode 100644 index 0000000000..5a47b3e22b --- /dev/null +++ b/lib/runtime_tools/examples/efile_drv.systemtap @@ -0,0 +1,112 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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 begin +{ + op_map[1] = "OPEN"; + op_map[2] = "READ"; + op_map[3] = "LSEEK"; + op_map[4] = "WRITE"; + op_map[5] = "FSTAT"; + op_map[6] = "PWD"; + op_map[7] = "READDIR"; + op_map[8] = "CHDIR"; + op_map[9] = "FSYNC"; + op_map[10] = "MKDIR"; + op_map[11] = "DELETE"; + op_map[12] = "RENAME"; + op_map[13] = "RMDIR"; + op_map[14] = "TRUNCATE"; + op_map[15] = "READ_FILE"; + op_map[16] = "WRITE_INFO"; + op_map[19] = "LSTAT"; + op_map[20] = "READLINK"; + op_map[21] = "LINK"; + op_map[22] = "SYMLINK"; + op_map[23] = "CLOSE"; + op_map[24] = "PWRITEV"; + op_map[25] = "PREADV"; + op_map[26] = "SETOPT"; + op_map[27] = "IPREAD"; + op_map[28] = "ALTNAME"; + op_map[29] = "READ_LINE"; + op_map[30] = "FDATASYNC"; + op_map[31] = "FADVISE"; +} + +probe process("beam").mark("aio_pool-add") +{ + printf("async I/O pool port %s queue len %d\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("aio_pool-get") +{ + printf("async I/O pool port %s queue len %d\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("efile_drv-entry") +{ + printf("efile_drv enter tag={%d,%d} %s%s | %s (%d) | args: %s %s , %d %d (port %s)\n", + $arg1, $arg2, + $arg3 == NULL ? "" : "user tag ", + $arg3 == NULL ? "" : user_string($arg3), + op_map[$arg4], $arg4, + $arg5 == NULL ? "" : user_string($arg5), + $arg6 == NULL ? "" : user_string($arg6), $arg7, $arg8, + /* NOTE: port name in $arg[11] is experimental */ + user_string($arg11)) +} + +probe process("beam").mark("efile_drv-int*") +{ + printf("async I/O worker tag={%d,%d} | %s (%d) | %s\n", + $arg1, $arg2, op_map[$arg3], $arg3, probefunc()); +} + +probe process("beam").mark("efile_drv-return") +{ + if ($arg5 == 0) { + /* efile_drv-return error case */ + printf("efile_drv return tag={%d,%d} %s%s | %s (%d) | errno %d\n", + $arg1, $arg2, + $arg3 == NULL ? "" : "user tag ", + $arg3 == NULL ? "" : user_string($arg3), + op_map[$arg4], $arg4, + $arg6); + } else { + /* efile_drv-return success case */ + printf("efile_drv return tag={%d,%d} %s | %s (%d) ok\n", + $arg1, $arg2, + $arg3 == NULL ? "" : user_string($arg3), + op_map[$arg4], $arg4); + } +} + +global op_map; diff --git a/lib/runtime_tools/examples/function-calls.d b/lib/runtime_tools/examples/function-calls.d new file mode 100644 index 0000000000..3e015dc2d2 --- /dev/null +++ b/lib/runtime_tools/examples/function-calls.d @@ -0,0 +1,57 @@ +/* example usage: dtrace -q -s /path/to/function-calls.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +erlang*:::local-function-entry +{ + printf("pid %s enter (local) %s depth %d\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::global-function-entry +{ + printf("pid %s enter (global) %s depth %d\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::function-return +{ + printf("pid %s return %s depth %d\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::bif-entry +{ + printf("pid %s BIF entry mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::bif-return +{ + printf("pid %s BIF return mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::nif-entry +{ + printf("pid %s NIF entry mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::nif-return +{ + printf("pid %s NIF return mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} diff --git a/lib/runtime_tools/examples/function-calls.systemtap b/lib/runtime_tools/examples/function-calls.systemtap new file mode 100644 index 0000000000..16c9c04918 --- /dev/null +++ b/lib/runtime_tools/examples/function-calls.systemtap @@ -0,0 +1,67 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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("local-function-entry") +{ + printf("pid %s enter (local) %s depth %d\n", + user_string($arg1), user_string($arg2), $arg3); +} + +probe process("beam").mark("global-function-entry") +{ + printf("pid %s enter (global) %s depth %d\n", + user_string($arg1), user_string($arg2), $arg3); +} + +probe process("beam").mark("function-return") +{ + printf("pid %s return %s depth %d\n", + user_string($arg1), user_string($arg2), $arg3); +} + +probe process("beam").mark("bif-entry") +{ + printf("pid %s BIF entry mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("bif-return") +{ + printf("pid %s BIF return mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("nif-entry") +{ + printf("pid %s NIF entry mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("nif-return") +{ + printf("pid %s NIF return mfa %s\n", user_string($arg1), user_string($arg2)); +} diff --git a/lib/runtime_tools/examples/garbage-collection.d b/lib/runtime_tools/examples/garbage-collection.d new file mode 100644 index 0000000000..f234e7d4db --- /dev/null +++ b/lib/runtime_tools/examples/garbage-collection.d @@ -0,0 +1,39 @@ +/* example usage: dtrace -q -s /path/to/garbage-collection.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +erlang*:::gc_major-start +{ + printf("GC major start pid %s need %d words\n", copyinstr(arg0), arg1); +} + +erlang*:::gc_minor-start +{ + printf("GC minor start pid %s need %d words\n", copyinstr(arg0), arg1); +} + +erlang*:::gc_major-end +{ + printf("GC major end pid %s reclaimed %d words\n", copyinstr(arg0), arg1); +} + +erlang*:::gc_minor-start +{ + printf("GC minor end pid %s reclaimed %d words\n", copyinstr(arg0), arg1); +} diff --git a/lib/runtime_tools/examples/garbage-collection.systemtap b/lib/runtime_tools/examples/garbage-collection.systemtap new file mode 100644 index 0000000000..64d69c6fbd --- /dev/null +++ b/lib/runtime_tools/examples/garbage-collection.systemtap @@ -0,0 +1,49 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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("gc_major-start") +{ + printf("GC major start pid %s need %d words\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("gc_minor-start") +{ + printf("GC minor start pid %s need %d words\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("gc_major-end") +{ + printf("GC major end pid %s reclaimed %d words\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("gc_minor-start") +{ + printf("GC minor end pid %s reclaimed %d words\n", user_string($arg1), $arg2); +} diff --git a/lib/runtime_tools/examples/memory1.d b/lib/runtime_tools/examples/memory1.d new file mode 100644 index 0000000000..c2e16e0779 --- /dev/null +++ b/lib/runtime_tools/examples/memory1.d @@ -0,0 +1,41 @@ +/* example usage: dtrace -q -s /path/to/memory1.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +erlang*:::copy-struct +{ + printf("copy_struct %d bytes\n", arg0); +} + +erlang*:::copy-object +{ + printf("copy_object pid %s %d bytes\n", copyinstr(arg0), arg1); +} + +erlang*:::process-heap_grow +{ + printf("proc heap grow pid %s %d -> %d bytes\n", copyinstr(arg0), + arg1, arg2); +} + +erlang*:::process-heap_shrink +{ + printf("proc heap shrink pid %s %d -> %d bytes\n", copyinstr(arg0), + arg1, arg2); +} diff --git a/lib/runtime_tools/examples/memory1.systemtap b/lib/runtime_tools/examples/memory1.systemtap new file mode 100644 index 0000000000..9723f2d02d --- /dev/null +++ b/lib/runtime_tools/examples/memory1.systemtap @@ -0,0 +1,51 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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("copy-struct") +{ + printf("copy_struct %d bytes\n", $arg1); +} + +probe process("beam").mark("copy-object") +{ + printf("copy_object pid %s %d bytes\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("process-heap_grow") +{ + printf("proc heap grow pid %s %d -> %d bytes\n", user_string($arg1), + $arg2, $arg3); +} + +probe process("beam").mark("process-heap_shrink") +{ + printf("proc heap shrink pid %s %d -> %d bytes\n", user_string($arg1), + $arg2, $arg3); +} diff --git a/lib/runtime_tools/examples/messages.d b/lib/runtime_tools/examples/messages.d new file mode 100644 index 0000000000..6361f3a220 --- /dev/null +++ b/lib/runtime_tools/examples/messages.d @@ -0,0 +1,94 @@ +/* example usage: dtrace -q -s /path/to/messages.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +BEGIN +{ + printf("\n"); + printf("NOTE: message-queue message size 4294967295 means an external\n"); + printf(" message that the code isn't smart enough to determine\n"); + printf(" the actual size.\n"); + printf("\n"); +} + +erlang*:::message-send +/arg3 == 0 && arg4 == 0 && arg5 == 0/ +{ + printf("send: %s -> %s: %d words\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::message-send +/arg3 != 0 || arg4 != 0 || arg5 != 0/ +{ + printf("send: %s label %d token {%d,%d} -> %s: %d words\n", + copyinstr(arg0), + arg3, arg4, arg5, + copyinstr(arg1), arg2); +} + +/* + * TODO: + * Weird, on my OS X box, beam says arg6 = 0 but this script says 4294967296. + */ + +erlang*:::message-send-remote +/arg4 == 0 && arg5 == 0 && (arg6 == 0 || arg6 >= 4294967296)/ +{ + printf("send : %s -> %s %s: %d words\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::message-send-remote +/arg4 != 0 || arg5 != 0 || arg6 < 4294967296/ +{ + printf("send : %s label %d token {%d,%d} -> %s %s: %d words\n", + copyinstr(arg0), + arg4, arg5, arg6, + copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::message-queued +/arg3 == 0 && arg4 == 0 && arg5 == 0/ +{ + printf("queued: %s: %d words, queue len %d\n", copyinstr(arg0), arg1, arg2); +} + +erlang*:::message-queued +/arg3 != 0 || arg4 != 0 || arg5 != 0/ +{ + printf("queued: %s label %d token {%d,%d}: %d words, queue len %d\n", + copyinstr(arg0), arg3, arg4, arg5, + arg1, arg2); +} + +erlang*:::message-receive +/arg3 == 0 && arg4 == 0 && arg5 == 0/ +{ + printf("receive: %s: %d words, queue len %d\n", + copyinstr(arg0), arg1, arg2); +} + +erlang*:::message-receive +/arg3 != 0 || arg4 != 0 || arg5 != 0/ +{ + printf("receive: %s label %d token {%d,%d}: %d words, queue len %d\n", + copyinstr(arg0), arg3, arg4, arg5, + arg1, arg2); +} diff --git a/lib/runtime_tools/examples/messages.systemtap b/lib/runtime_tools/examples/messages.systemtap new file mode 100644 index 0000000000..ff8f4076b1 --- /dev/null +++ b/lib/runtime_tools/examples/messages.systemtap @@ -0,0 +1,87 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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 begin +{ + printf("\n"); + printf("NOTE: message-queue message size 4294967295 means an external\n"); + printf(" message that the code isn't smart enough to determine\n"); + printf(" the actual size.\n"); + printf("\n"); +} + +probe process("beam").mark("message-send") +{ + if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { + printf("send: %s -> %s: %d words\n", + user_string($arg1), user_string($arg2), $arg3); + } else { + printf("send: %s label %d token {%d,%d} -> %s: %d words\n", + user_string($arg1), + $arg4, $arg5, $arg6, + user_string($arg2), $arg3); + } +} + +probe process("beam").mark("message-send-remote") +{ + if ($arg5 == 0 && $arg6 == 0 && $arg7 == 0) { + printf("send : %s -> %s %s: %d words\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); + } else { + printf("send : %s label %d token {%d,%d} -> %s %s: %d words\n", + user_string($arg1), + $arg5, $arg6, $arg7, + user_string($arg2), user_string($arg3), $arg4); + } +} + +probe process("beam").mark("message-queued") +{ + if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { + printf("queued: %s: %d words, queue len %d\n", user_string($arg1), $arg2, $arg3); + } else { + printf("queued: %s label %d token {%d,%d}: %d words, queue len %d\n", + user_string($arg1), $arg4, $arg5, $arg6, + $arg2, $arg3); + } +} + +probe process("beam").mark("message-receive") +{ + if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { + printf("receive: %s: %d words, queue len %d\n", + user_string($arg1), $arg2, $arg3); + } else { + printf("receive: %s label %d token {%d,%d}: %d words, queue len %d\n", + user_string($arg1), $arg4, $arg5, $arg6, + $arg2, $arg3); + } +} diff --git a/lib/runtime_tools/examples/port1.d b/lib/runtime_tools/examples/port1.d new file mode 100644 index 0000000000..204abbd3b8 --- /dev/null +++ b/lib/runtime_tools/examples/port1.d @@ -0,0 +1,142 @@ +/* example usage: dtrace -q -s /path/to/port1.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +BEGIN +{ + driver_map["tcp_inet", 1] = "OPEN"; + driver_map["tcp_inet", 2] = "CLOSE"; + driver_map["tcp_inet", 3] = "CONNECT"; + driver_map["tcp_inet", 4] = "PEER"; + driver_map["tcp_inet", 5] = "NAME"; + driver_map["tcp_inet", 6] = "BIND"; + driver_map["tcp_inet", 7] = "SETOPTS"; + driver_map["tcp_inet", 8] = "GETOPTS"; + driver_map["tcp_inet", 11] = "GETSTAT"; + driver_map["tcp_inet", 12] = "GETHOSTNAME"; + driver_map["tcp_inet", 13] = "FDOPEN"; + driver_map["tcp_inet", 14] = "GETFD"; + driver_map["tcp_inet", 15] = "GETTYPE"; + driver_map["tcp_inet", 16] = "GETSTATUS"; + driver_map["tcp_inet", 17] = "GETSERVBYNAME"; + driver_map["tcp_inet", 18] = "GETSERVBYPORT"; + driver_map["tcp_inet", 19] = "SETNAME"; + driver_map["tcp_inet", 20] = "SETPEER"; + driver_map["tcp_inet", 21] = "GETIFLIST"; + driver_map["tcp_inet", 22] = "IFGET"; + driver_map["tcp_inet", 23] = "IFSET"; + driver_map["tcp_inet", 24] = "SUBSCRIBE"; + driver_map["tcp_inet", 25] = "GETIFADDRS"; + driver_map["tcp_inet", 40] = "ACCEPT"; + driver_map["tcp_inet", 41] = "LISTEN"; + driver_map["tcp_inet", 42] = "RECV"; + driver_map["tcp_inet", 43] = "UNRECV"; + driver_map["tcp_inet", 44] = "SHUTDOWN"; + driver_map["tcp_inet", 60] = "RECV"; + driver_map["tcp_inet", 61] = "LISTEN"; + driver_map["tcp_inet", 62] = "BINDX"; + /* No looping constructs, so repeat for udp_inet */ + driver_map["udp_inet", 1] = "OPEN"; + driver_map["udp_inet", 2] = "CLOSE"; + driver_map["udp_inet", 3] = "CONNECT"; + driver_map["udp_inet", 4] = "PEER"; + driver_map["udp_inet", 5] = "NAME"; + driver_map["udp_inet", 6] = "BIND"; + driver_map["udp_inet", 7] = "SETOPTS"; + driver_map["udp_inet", 8] = "GETOPTS"; + driver_map["udp_inet", 11] = "GETSTAT"; + driver_map["udp_inet", 12] = "GETHOSTNAME"; + driver_map["udp_inet", 13] = "FDOPEN"; + driver_map["udp_inet", 14] = "GETFD"; + driver_map["udp_inet", 15] = "GETTYPE"; + driver_map["udp_inet", 16] = "GETSTATUS"; + driver_map["udp_inet", 17] = "GETSERVBYNAME"; + driver_map["udp_inet", 18] = "GETSERVBYPORT"; + driver_map["udp_inet", 19] = "SETNAME"; + driver_map["udp_inet", 20] = "SETPEER"; + driver_map["udp_inet", 21] = "GETIFLIST"; + driver_map["udp_inet", 22] = "IFGET"; + driver_map["udp_inet", 23] = "IFSET"; + driver_map["udp_inet", 24] = "SUBSCRIBE"; + driver_map["udp_inet", 25] = "GETIFADDRS"; + driver_map["udp_inet", 40] = "ACCEPT"; + driver_map["udp_inet", 41] = "LISTEN"; + driver_map["udp_inet", 42] = "RECV"; + driver_map["udp_inet", 43] = "UNRECV"; + driver_map["udp_inet", 44] = "SHUTDOWN"; + driver_map["udp_inet", 60] = "RECV"; + driver_map["udp_inet", 61] = "LISTEN"; + driver_map["udp_inet", 62] = "BINDX"; +} + +erlang*:::port-open +{ + printf("port open pid %s port name %s port %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::port-command +{ + printf("port command pid %s port %s port name %s command type %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} + +erlang*:::port-control +{ + /* http://dtrace.org/blogs/brendan/2011/11/25/dtrace-variable-types/ */ + this->cmd = driver_map[copyinstr(arg2), arg3]; + this->cmd_str = (this->cmd == 0) ? "unknown" : this->cmd; + printf("port control pid %s port %s port name %s command %d %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3, + this->cmd_str); +} + +/* port-exit is fired as a result of port_close() or exit signal */ + +erlang*:::port-exit +{ + printf("port exit pid %s port %s port name %s reason %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} + +erlang*:::port-connect +{ + printf("port connect pid %s port %s port name %s new pid %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} + +erlang*:::port-busy +{ + printf("port busy %s\n", copyinstr(arg0)); +} + +erlang*:::port-not_busy +{ + printf("port not busy %s\n", copyinstr(arg0)); +} + +erlang*:::aio_pool-add +{ + printf("async I/O pool add thread %d queue len %d\n", arg0, arg1); +} + +erlang*:::aio_pool-get +{ + printf("async I/O pool get thread %d queue len %d\n", arg0, arg1); +} diff --git a/lib/runtime_tools/examples/port1.systemtap b/lib/runtime_tools/examples/port1.systemtap new file mode 100644 index 0000000000..a63d9b670c --- /dev/null +++ b/lib/runtime_tools/examples/port1.systemtap @@ -0,0 +1,152 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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 begin +{ + driver_map["tcp_inet", 1] = "OPEN"; + driver_map["tcp_inet", 2] = "CLOSE"; + driver_map["tcp_inet", 3] = "CONNECT"; + driver_map["tcp_inet", 4] = "PEER"; + driver_map["tcp_inet", 5] = "NAME"; + driver_map["tcp_inet", 6] = "BIND"; + driver_map["tcp_inet", 7] = "SETOPTS"; + driver_map["tcp_inet", 8] = "GETOPTS"; + driver_map["tcp_inet", 11] = "GETSTAT"; + driver_map["tcp_inet", 12] = "GETHOSTNAME"; + driver_map["tcp_inet", 13] = "FDOPEN"; + driver_map["tcp_inet", 14] = "GETFD"; + driver_map["tcp_inet", 15] = "GETTYPE"; + driver_map["tcp_inet", 16] = "GETSTATUS"; + driver_map["tcp_inet", 17] = "GETSERVBYNAME"; + driver_map["tcp_inet", 18] = "GETSERVBYPORT"; + driver_map["tcp_inet", 19] = "SETNAME"; + driver_map["tcp_inet", 20] = "SETPEER"; + driver_map["tcp_inet", 21] = "GETIFLIST"; + driver_map["tcp_inet", 22] = "IFGET"; + driver_map["tcp_inet", 23] = "IFSET"; + driver_map["tcp_inet", 24] = "SUBSCRIBE"; + driver_map["tcp_inet", 25] = "GETIFADDRS"; + driver_map["tcp_inet", 40] = "ACCEPT"; + driver_map["tcp_inet", 41] = "LISTEN"; + driver_map["tcp_inet", 42] = "RECV"; + driver_map["tcp_inet", 43] = "UNRECV"; + driver_map["tcp_inet", 44] = "SHUTDOWN"; + driver_map["tcp_inet", 60] = "RECV"; + driver_map["tcp_inet", 61] = "LISTEN"; + driver_map["tcp_inet", 62] = "BINDX"; + /* No looping constructs, so repeat for udp_inet */ + driver_map["udp_inet", 1] = "OPEN"; + driver_map["udp_inet", 2] = "CLOSE"; + driver_map["udp_inet", 3] = "CONNECT"; + driver_map["udp_inet", 4] = "PEER"; + driver_map["udp_inet", 5] = "NAME"; + driver_map["udp_inet", 6] = "BIND"; + driver_map["udp_inet", 7] = "SETOPTS"; + driver_map["udp_inet", 8] = "GETOPTS"; + driver_map["udp_inet", 11] = "GETSTAT"; + driver_map["udp_inet", 12] = "GETHOSTNAME"; + driver_map["udp_inet", 13] = "FDOPEN"; + driver_map["udp_inet", 14] = "GETFD"; + driver_map["udp_inet", 15] = "GETTYPE"; + driver_map["udp_inet", 16] = "GETSTATUS"; + driver_map["udp_inet", 17] = "GETSERVBYNAME"; + driver_map["udp_inet", 18] = "GETSERVBYPORT"; + driver_map["udp_inet", 19] = "SETNAME"; + driver_map["udp_inet", 20] = "SETPEER"; + driver_map["udp_inet", 21] = "GETIFLIST"; + driver_map["udp_inet", 22] = "IFGET"; + driver_map["udp_inet", 23] = "IFSET"; + driver_map["udp_inet", 24] = "SUBSCRIBE"; + driver_map["udp_inet", 25] = "GETIFADDRS"; + driver_map["udp_inet", 40] = "ACCEPT"; + driver_map["udp_inet", 41] = "LISTEN"; + driver_map["udp_inet", 42] = "RECV"; + driver_map["udp_inet", 43] = "UNRECV"; + driver_map["udp_inet", 44] = "SHUTDOWN"; + driver_map["udp_inet", 60] = "RECV"; + driver_map["udp_inet", 61] = "LISTEN"; + driver_map["udp_inet", 62] = "BINDX"; +} + +probe process("beam").mark("port-open") +{ + printf("port open pid %s port name %s port %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("port-command") +{ + printf("port command pid %s port %s port name %s command type %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} + +probe process("beam").mark("port-control") +{ + cmd = driver_map[user_string($arg3), $arg4]; + cmd_str = (cmd == "") ? "unknown" : cmd; + printf("port control pid %s port %s port name %s command %d %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4, cmd_str); +} + +/* port-exit is fired as a result of port_close() or exit signal */ + +probe process("beam").mark("port-exit") +{ + printf("port exit pid %s port %s port name %s reason %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} + +probe process("beam").mark("port-connect") +{ + printf("port connect pid %s port %s port name %s new pid %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} + +probe process("beam").mark("port-busy") +{ + printf("port busy %s\n", user_string($arg1)); +} + +probe process("beam").mark("port-not_busy") +{ + printf("port not busy %s\n", user_string($arg1)); +} + +probe process("beam").mark("aio_pool-add") +{ + printf("async I/O pool add thread %d queue len %d\n", $arg1, $arg2); +} + +probe process("beam").mark("aio_pool-get") +{ + printf("async I/O pool get thread %d queue len %d\n", $arg1, $arg2); +} + +global driver_map;
\ No newline at end of file diff --git a/lib/runtime_tools/examples/process-scheduling.d b/lib/runtime_tools/examples/process-scheduling.d new file mode 100644 index 0000000000..79e9cc598c --- /dev/null +++ b/lib/runtime_tools/examples/process-scheduling.d @@ -0,0 +1,35 @@ +/* example usage: dtrace -q -s /path/to/process-scheduling.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +erlang*:::process-scheduled +{ + printf(" Schedule pid %s mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::process-unscheduled +{ + printf("Unschedule pid %s\n", copyinstr(arg0)); +} + +erlang*:::process-hibernate +{ + printf(" Hibernate pid %s resume mfa %s\n", + copyinstr(arg0), copyinstr(arg1)); +} diff --git a/lib/runtime_tools/examples/process-scheduling.systemtap b/lib/runtime_tools/examples/process-scheduling.systemtap new file mode 100644 index 0000000000..c8cee60a07 --- /dev/null +++ b/lib/runtime_tools/examples/process-scheduling.systemtap @@ -0,0 +1,45 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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("process-scheduled") +{ + printf(" Schedule pid %s mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("process-unscheduled") +{ + printf("Unschedule pid %s\n", user_string($arg1)); +} + +probe process("beam").mark("process-hibernate") +{ + printf(" Hibernate pid %s resume mfa %s\n", + user_string($arg1), user_string($arg2)); +} diff --git a/lib/runtime_tools/examples/spawn-exit.d b/lib/runtime_tools/examples/spawn-exit.d new file mode 100644 index 0000000000..7310f3343d --- /dev/null +++ b/lib/runtime_tools/examples/spawn-exit.d @@ -0,0 +1,41 @@ +/* example usage: dtrace -q -s /path/to/spawn-exit.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +erlang*:::process-spawn +{ + printf("pid %s mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::process-exit +{ + printf("pid %s reason %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::process-exit_signal +{ + printf("sender %s -> pid %s reason %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::process-exit_signal-remote +{ + printf("sender %s -> node %s pid %s reason %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} diff --git a/lib/runtime_tools/examples/spawn-exit.systemtap b/lib/runtime_tools/examples/spawn-exit.systemtap new file mode 100644 index 0000000000..5e3be9fc1b --- /dev/null +++ b/lib/runtime_tools/examples/spawn-exit.systemtap @@ -0,0 +1,51 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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("process-spawn") +{ + printf("pid %s mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("process-exit") +{ + printf("pid %s reason %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("process-exit_signal") +{ + printf("sender %s -> pid %s reason %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("process-exit_signal-remote") +{ + printf("sender %s -> node %s pid %s reason %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} diff --git a/lib/runtime_tools/examples/user-probe.d b/lib/runtime_tools/examples/user-probe.d new file mode 100644 index 0000000000..13baff6a32 --- /dev/null +++ b/lib/runtime_tools/examples/user-probe.d @@ -0,0 +1,36 @@ +/* example usage: dtrace -q -s /path/to/user-probe.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 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% + */ + +erlang*:::user_trace-s1 +{ + printf("%s\n", copyinstr(arg0)); +} + +erlang*:::user_trace-i4s4 +{ + printf("%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.systemtap b/lib/runtime_tools/examples/user-probe.systemtap new file mode 100644 index 0000000000..84a45709e8 --- /dev/null +++ b/lib/runtime_tools/examples/user-probe.systemtap @@ -0,0 +1,46 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 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% + */ +/* + * 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("dyntrace.so").mark("user_trace-s1") +{ + printf("%s\n", user_string($arg1)); +} + +probe process("dyntrace.so").mark("user_trace-i4s4") +{ + printf("%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/src/Makefile b/lib/runtime_tools/src/Makefile index 946409b262..6dc89bea32 100644 --- a/lib/runtime_tools/src/Makefile +++ b/lib/runtime_tools/src/Makefile @@ -45,6 +45,7 @@ MODULES= \ runtime_tools \ runtime_tools_sup \ dbg \ + dyntrace \ percept_profile \ observer_backend \ ttb_autostart @@ -64,10 +65,15 @@ APPUP_FILE= runtime_tools.appup APPUP_SRC= $(APPUP_FILE).src APPUP_TARGET= $(EBIN)/$(APPUP_FILE) +EXAMPLE_FILES= \ + ../examples/* # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += -I../include +ERL_COMPILE_FLAGS += \ + -I../include \ + -I ../../et/include \ + -I ../../../libraries/et/include # ---------------------------------------------------- # Targets @@ -97,6 +103,8 @@ release_spec: opt $(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 diff --git a/lib/runtime_tools/src/dyntrace.erl b/lib/runtime_tools/src/dyntrace.erl new file mode 100644 index 0000000000..388c7679b9 --- /dev/null +++ b/lib/runtime_tools/src/dyntrace.erl @@ -0,0 +1,279 @@ +-module(dyntrace). + +%%% @doc The Dynamic tracing interface module +%%% +%%% This Dynamic tracing interface module, with the corresponding NIFs, should +%%% 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: +%%% ``` +%%% 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"). +%%% true +%%% ''' +%%% +%%% Output from the example D script `user-probe.d' looks like: +%%% ``` +%%% <0.34.0> GGOOOAAALL!!!!! 7 8 9 0 'one' 'four' '' '' +%%% ''' +%%% +%%% If the expected type of variable is not present, e.g. integer when +%%% integer() is expected, or an I/O list when iolist() is expected, +%%% then the driver will ignore the user's input and use a default +%%% value of 0 or NULL, respectively. + +-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]). +-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. + +%% The *_maybe() types use atom() instead of a stricter 'undef' +%% because user_trace_i4s4/9 is exposed to the outside world, and +%% because the driver will allow any atom to be used as a "not +%% present" indication, we'll allow any atom in the types. + +-type integer_maybe() :: integer() | atom(). +-type iolist_maybe() :: iolist() | atom(). + +on_load() -> + PrivDir = code:priv_dir(runtime_tools), + LibName = "dyntrace", + Lib = filename:join([PrivDir, "lib", LibName]), + Status = case erlang:load_nif(Lib, 0) of + ok -> ok; + {error, {load_failed, _}}=Error1 -> + ArchLibDir = + filename:join([PrivDir, "lib", + erlang:system_info(system_architecture)]), + Candidate = + filelib:wildcard(filename:join([ArchLibDir,LibName ++ "*" ])), + case Candidate of + [] -> Error1; + _ -> + ArchLib = filename:join([ArchLibDir, LibName]), + erlang:load_nif(ArchLib, 0) + end; + Error1 -> Error1 + end, + case Status of + ok -> ok; + {error, {E, Str}} -> + case erlang:system_info(dynamic_trace) of + none -> + ok; + _ -> + error_logger:error_msg("Unable to load dyntrace library. Failed with error:~n +\"~p, ~s\"~n" + "Dynamic tracing is enabled but the driver is not built correctly~n",[ + E,Str]), + Status + end + end. + +%%% +%%% NIF placeholders +%%% + +-spec available() -> true | false. + +available() -> + erlang:nif_error(nif_not_loaded). + +-spec user_trace_s1(iolist()) -> true | false | error | badarg. + +user_trace_s1(_Message) -> + erlang:nif_error(nif_not_loaded). + +-spec user_trace_i4s4(iolist(), + integer_maybe(), integer_maybe(), + integer_maybe(), integer_maybe(), + iolist_maybe(), iolist_maybe(), + iolist_maybe(), iolist_maybe()) -> + true | false | error | badarg. + +user_trace_i4s4(_, _, _, _, _, _, _, _, _) -> + erlang:nif_error(nif_not_loaded). + +%%% +%%% Erlang support functions +%%% + +-spec p() -> true | false | error | badarg. + +p() -> + user_trace_int(undef, undef, undef, undef, undef, undef, undef, undef). + +-spec p(probe_arg()) -> true | false | error | badarg. + +p(I1) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, undef, undef, undef, undef); +p(S1) -> + user_trace_int(undef, undef, undef, undef, S1, undef, undef, undef). + +-spec p(probe_arg(), probe_arg()) -> true | false | error | badarg. + +p(I1, I2) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, undef, undef, undef, undef); +p(I1, S1) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, undef, undef, undef); +p(S1, S2) -> + user_trace_int(undef, undef, undef, undef, S1, S2, undef, undef). + +-spec p(probe_arg(), probe_arg(), probe_arg()) -> true | false | error | badarg. + +p(I1, I2, I3) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, undef, undef, undef, undef); +p(I1, I2, S1) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, undef, undef, undef); +p(I1, S1, S2) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, S2, undef, undef); +p(S1, S2, S3) -> + user_trace_int(undef, undef, undef, undef, S1, S2, S3, undef). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, undef, undef, undef, undef); +p(I1, I2, I3, S1) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, undef, undef, undef); +p(I1, I2, S1, S2) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, S2, undef, undef); +p(I1, S1, S2, S3) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, S2, S3, undef); +p(S1, S2, S3, S4) -> + user_trace_int(undef, undef, undef, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, undef, undef, undef); +p(I1, I2, I3, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, S2, undef, undef); +p(I1, I2, S1, S2, S3) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, S2, S3, undef); +p(I1, S1, S2, S3, S4) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, S2, undef, undef); +p(I1, I2, I3, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, S2, S3, undef); +p(I1, I2, S1, S2, S3, S4) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, S2, S3, undef); +p(I1, I2, I3, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, S2, S3, S4). + +-spec user_trace_int(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_int(I1, I2, I3, I4, S1, S2, S3, S4) -> + UTag = get_tag(), + try + user_trace_i4s4(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)). + +-spec get_tag() -> binary() | undefined. +get_tag() -> + erlang:dt_get_tag(). + +-spec get_tag_data() -> binary() | undefined. +%% Gets tag if set, otherwise the spread tag data from last incoming message +get_tag_data() -> + erlang:dt_get_tag_data(). + +-spec spread_tag(boolean()) -> true | {non_neg_integer(), binary() | []}. +%% Makes the tag behave as a sequential trace token, will spread with +%% messages to be picked up by someone using get_tag_data +spread_tag(B) -> + erlang:dt_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/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src index 76fd998530..a9d2d68857 100644 --- a/lib/runtime_tools/src/runtime_tools.app.src +++ b/lib/runtime_tools/src/runtime_tools.app.src @@ -23,7 +23,7 @@ inviso_rt,inviso_rt_lib,inviso_rt_meta, inviso_as_lib,inviso_autostart,inviso_autostart_server, runtime_tools,runtime_tools_sup,erts_alloc_config, - ttb_autostart]}, + ttb_autostart,dyntrace]}, {registered, [runtime_tools_sup,inviso_rt,inviso_rt_meta]}, {applications, [kernel, stdlib]}, % {env, [{inviso_autostart_mod,your_own_autostart_module}]}, diff --git a/lib/sasl/doc/src/rel.xml b/lib/sasl/doc/src/rel.xml index 470adf3c03..68ef90330f 100644 --- a/lib/sasl/doc/src/rel.xml +++ b/lib/sasl/doc/src/rel.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1997</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -88,7 +88,7 @@ <p>The list must be a subset of the included applications specified in the application resource file (<c>Application.app</c>) and overrides this value. Defaults - to the empty list.</p> + to the same value as in the application resource file.</p> </item> </list> <note> diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index 12ba2a5476..3c1002e4a6 100644 --- a/lib/sasl/src/systools_make.erl +++ b/lib/sasl/src/systools_make.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 @@ -375,7 +375,7 @@ get_release1(File, Path, ModTestP, Machine) -> {ok, Release, Warnings1} = read_release(File, Path), {ok, Appls0} = collect_applications(Release, Path), {ok, Appls1} = check_applications(Appls0), - {ok, Appls2} = sort_included_applications(Appls1, Release), % OTP-4121 + {ok, Appls2} = sort_used_and_incl_appls(Appls1, Release), % OTP-4121, OTP-9984 {ok, Warnings2} = check_modules(Appls2, Path, ModTestP, Machine), {ok, Appls} = sort_appls(Appls2), {ok, Release, Appls, Warnings1 ++ Warnings2}. @@ -842,34 +842,45 @@ undefined_applications(Appls) -> filter(fun(X) -> not member(X, Defined) end, Uses). %%______________________________________________________________________ -%% sort_included_applications(Applications, Release) -> Applications +%% sort_used_and_incl_appls(Applications, Release) -> Applications %% Applications = [{{Name,Vsn},#application}] %% Release = #release{} %% -%% Check that included applications are given in the same order as in -%% the release resource file (.rel). Otherwise load instructions in -%% the boot script, and consequently release upgrade instructions in -%% relup, may end up in the wrong order. +%% OTP-4121, OTP-9984 +%% Check that used and included applications are given in the same +%% order as in the release resource file (.rel). Otherwise load and +%% start instructions in the boot script, and consequently release +%% upgrade instructions in relup, may end up in the wrong order. -sort_included_applications(Applications, Release) when is_tuple(Release) -> +sort_used_and_incl_appls(Applications, Release) when is_tuple(Release) -> {ok, - sort_included_applications(Applications, Release#release.applications)}; - -sort_included_applications([{Tuple,Appl}|Appls], OrderedAppls) -> - case Appl#application.includes of - Incls when length(Incls)>1 -> - IndexedIncls = find_pos(Incls, OrderedAppls), - SortedIndexedIncls = lists:keysort(1, IndexedIncls), - Incls2 = lists:map(fun({_Index,Name}) -> Name end, - SortedIndexedIncls), - Appl2 = Appl#application{includes=Incls2}, - [{Tuple,Appl2}|sort_included_applications(Appls, OrderedAppls)]; - _Incls -> - [{Tuple,Appl}|sort_included_applications(Appls, OrderedAppls)] - end; -sort_included_applications([], _OrderedAppls) -> + sort_used_and_incl_appls(Applications, Release#release.applications)}; + +sort_used_and_incl_appls([{Tuple,Appl}|Appls], OrderedAppls) -> + Incls2 = + case Appl#application.includes of + Incls when length(Incls)>1 -> + sort_appl_list(Incls, OrderedAppls); + Incls -> + Incls + end, + Uses2 = + case Appl#application.uses of + Uses when length(Uses)>1 -> + sort_appl_list(Uses, OrderedAppls); + Uses -> + Uses + end, + Appl2 = Appl#application{includes=Incls2, uses=Uses2}, + [{Tuple,Appl2}|sort_used_and_incl_appls(Appls, OrderedAppls)]; +sort_used_and_incl_appls([], _OrderedAppls) -> []. +sort_appl_list(List, Order) -> + IndexedList = find_pos(List, Order), + SortedIndexedList = lists:keysort(1, IndexedList), + lists:map(fun({_Index,Name}) -> Name end, SortedIndexedList). + find_pos([Name|Incs], OrderedAppls) -> [find_pos(1, Name, OrderedAppls)|find_pos(Incs, OrderedAppls)]; find_pos([], _OrderedAppls) -> @@ -1253,7 +1264,8 @@ sort_appls(Appls) -> {ok, sort_appls(Appls, [], [], [])}. sort_appls([{N, A}|T], Missing, Circular, Visited) -> {Name,_Vsn} = N, - {Uses, T1, NotFnd1} = find_all(Name, A#application.uses, T, Visited, [], []), + {Uses, T1, NotFnd1} = find_all(Name, lists:reverse(A#application.uses), + T, Visited, [], []), {Incs, T2, NotFnd2} = find_all(Name, lists:reverse(A#application.includes), T1, Visited, [], []), Missing1 = NotFnd1 ++ NotFnd2 ++ Missing, diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl index 4cf7364d74..72b3eb8954 100644 --- a/lib/sasl/test/systools_SUITE.erl +++ b/lib/sasl/test/systools_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% 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 @@ -45,7 +45,7 @@ abnormal_script/1, src_tests_script/1, crazy_script/1, included_script/1, included_override_script/1, included_fail_script/1, included_bug_script/1, exref_script/1, - otp_3065_circular_dependenies/1]). + otp_3065_circular_dependenies/1, included_and_used_sort_script/1]). -export([tar_options/1, normal_tar/1, no_mod_vsn_tar/1, system_files_tar/1, system_files_tar/2, invalid_system_files_tar/1, invalid_system_files_tar/2, variable_tar/1, @@ -80,7 +80,7 @@ groups() -> no_sasl_script, src_tests_script, crazy_script, included_script, included_override_script, included_fail_script, included_bug_script, exref_script, - otp_3065_circular_dependenies]}, + otp_3065_circular_dependenies, included_and_used_sort_script]}, {tar, [], [tar_options, normal_tar, no_mod_vsn_tar, system_files_tar, invalid_system_files_tar, variable_tar, @@ -600,6 +600,24 @@ otp_3065_circular_dependenies(Config) when is_list(Config) -> ok = file:set_cwd(OldDir), ok. +%% Test sorting of included applications and used applications +included_and_used_sort_script(Config) when is_list(Config) -> + {ok, OldDir} = file:get_cwd(), + {LatestDir1, LatestName1} = create_include_files(sort_apps, Config), + ok = file:set_cwd(LatestDir1), + ok = systools:make_script(LatestName1), + ok = check_include_script(LatestName1, + [t20,t19,t18,t17,t16,t15,t14],[t20,t19,t18,t14]), + + {LatestDir2, LatestName2} = create_include_files(sort_apps_rev, Config), + ok = file:set_cwd(LatestDir2), + ok = systools:make_script(LatestName2), + ok = check_include_script(LatestName2, + [t18,t19,t20,t15,t16,t17,t14],[t18,t19,t20,t14]), + + ok = file:set_cwd(OldDir), + ok. + %% make_script: Check that make_script exref option works. exref_script(Config) when is_list(Config) -> @@ -2301,8 +2319,53 @@ create_include_files(otp_3065_circular_dependenies, Config) -> " {chAts, \"1.0\"}, {aa12, \"1.0\"}, \n" " {chTraffic, \"1.0\"}]}.\n", file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(sort_apps, Config) -> + PrivDir = ?privdir, + Name = fname(PrivDir, sort_apps), + create_sort_apps(PrivDir), + + Apps = application_controller:which_applications(), + {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t14, \"1.0\"}, \n" + " {t20, \"1.0\"}, \n" + " {t19, \"1.0\"}, \n" + " {t18, \"1.0\"}, \n" + " {t17, \"1.0\"}, \n" + " {t16, \"1.0\"}, \n" + " {t15, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(sort_apps_rev, Config) -> + PrivDir = ?privdir, + Name = fname(PrivDir, sort_apps_rev), + create_sort_apps(PrivDir), + + Apps = application_controller:which_applications(), + {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t14, \"1.0\"}, \n" + " {t18, \"1.0\"}, \n" + " {t19, \"1.0\"}, \n" + " {t20, \"1.0\"}, \n" + " {t15, \"1.0\"}, \n" + " {t16, \"1.0\"}, \n" + " {t17, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), {filename:dirname(Name), filename:basename(Name)}. + create_apps(Dir) -> T1 = "{application, t1,\n" " [{vsn, \"1.0\"},\n" @@ -2451,6 +2514,70 @@ create_apps_3065(Dir) -> " {registered, []}]}.\n", file:write_file(fname(Dir, 'aa12.app'), list_to_binary(T13)). +create_sort_apps(Dir) -> + T14 = "{application, t14,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t18,t20,t19]},\n" + " {included_applications, [t15,t17,t16]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't14.app'), list_to_binary(T14)), + + T15 = "{application, t15,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't15.app'), list_to_binary(T15)), + + T16 = "{application, t16,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't16.app'), list_to_binary(T16)), + + T17 = "{application, t17,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't17.app'), list_to_binary(T17)), + + T18 = "{application, t18,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't18.app'), list_to_binary(T18)), + + T19 = "{application, t19,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't19.app'), list_to_binary(T19)), + + T20 = "{application, t20,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't20.app'), list_to_binary(T20)). + fname(N) -> filename:join(N). diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 21a0582c06..0542054596 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -18,26 +18,12 @@ %% {"%VSN%", - [ - {"2.0.8", [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, - {load_module, ssh_channel, soft_purge, soft_purge, []}]}, - {"2.0.7", [{load_module, ssh_sftp, soft_purge, soft_purge, []}]}, - {"2.0.6", [{load_module, ssh_userreg, soft_purge, soft_purge, []}, - {load_module, ssh_sftp, soft_purge, soft_purge, []}]}, - {"2.0.5", [{load_module, ssh_userreg, soft_purge, soft_purge, []}, - {load_module, ssh_sftp, soft_purge, soft_purge, []}, - {load_module, ssh_connection_handler, soft_purge, soft_purge, [ssh_userreg]}]} + [ + {<<"2\\.*">>, [{restart_application, ssh}]}, + {<<"1\\.*">>, [{restart_application, ssh}]} ], [ - {"2.0.8", [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, - {load_module, ssh_channel, soft_purge, soft_purge, []}]}, - {"2.0.7", [{load_module, ssh_sftp, soft_purge, soft_purge, []}]}, - {"2.0.6", [{load_module, ssh_userreg, soft_purge, soft_purge, []}, - {load_module, ssh_sftp, soft_purge, soft_purge, []}]}, - {"2.0.5", [{load_module, ssh_userreg, soft_purge, soft_purge, []}, - {load_module, ssh_sftp, soft_purge, soft_purge, []}, - {load_module, ssh_connection_handler, soft_purge, soft_purge, [ssh_userreg]}]} + {<<"2\\.*">>, [{restart_application, ssh}]}, + {<<"1\\.*">>, [{restart_application, ssh}]} ] }. - - diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 42f860d6ae..bff73a1b40 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.0.9 +SSH_VSN = 2.1 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 1b07e76d6a..e346b1e9e6 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,23 +1,13 @@ %% -*- erlang -*- {"%VSN%", [ - {"4.1.6", [{restart_application, ssl}]}, - {"4.1.5", [{restart_application, ssl}]}, - {"4.1.4", [{restart_application, ssl}]}, - {"4.1.3", [{restart_application, ssl}]}, - {"4.1.2", [{restart_application, ssl}]}, - {"4.1.1", [{restart_application, ssl}]}, - {"4.1", [{restart_application, ssl}]}, - {"4.0.1", [{restart_application, ssl}]} + {"5.0", [{restart_application, ssl}]}, + {<<"4\\.*">>, [{restart_application, ssl}]}, + {<<"3\\.*">>, [{restart_application, ssl}]} ], [ - {"4.1.6", [{restart_application, ssl}]}, - {"4.1.5", [{restart_application, ssl}]}, - {"4.1.4", [{restart_application, ssl}]}, - {"4.1.3", [{restart_application, ssl}]}, - {"4.1.2", [{restart_application, ssl}]}, - {"4.1.1", [{restart_application, ssl}]}, - {"4.1", [{restart_application, ssl}]}, - {"4.0.1", [{restart_application, ssl}]} + {"5.0", [{restart_application, ssl}]}, + {<<"4\\.*">>, [{restart_application, ssl}]}, + {<<"3\\.*">>, [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 2255798f1d..0fccbfe908 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 5.0 +SSL_VSN = 5.0.1 diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl index ff032b129c..81bec21a3f 100644 --- a/lib/stdlib/src/erl_compile.erl +++ b/lib/stdlib/src/erl_compile.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 @@ -57,17 +57,7 @@ compile_cmdline(List) -> end. my_halt(Reason) -> - case process_info(group_leader(), status) of - {_,waiting} -> - %% Now all output data is down in the driver. - %% Give the driver some extra time before halting. - receive after 1 -> ok end, - halt(Reason); - _ -> - %% Probably still processing I/O requests. - erlang:yield(), - my_halt(Reason) - end. + erlang:halt(Reason). %% Run the the compiler in a separate process, trapping EXITs. diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index cd3b531d10..3063881890 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-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 @@ -287,6 +287,7 @@ bif(group_leader, 0) -> true; bif(group_leader, 2) -> true; bif(halt, 0) -> true; bif(halt, 1) -> true; +bif(halt, 2) -> true; bif(hd, 1) -> true; bif(integer_to_list, 1) -> true; bif(integer_to_list, 2) -> true; diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index ad49d89908..27e70ac4d4 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.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 @@ -848,17 +848,7 @@ fatal(Str) -> throw(Str). my_halt(Reason) -> - case process_info(group_leader(), status) of - {_,waiting} -> - %% Now all output data is down in the driver. - %% Give the driver some extra time before halting. - receive after 1 -> ok end, - halt(Reason); - _ -> - %% Probably still processing I/O requests. - erlang:yield(), - my_halt(Reason) - end. + erlang:halt(Reason). hidden_apply(App, M, F, Args) -> try diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl index a0e198ce09..f8d4fb4b6a 100644 --- a/lib/stdlib/test/edlin_expand_SUITE.erl +++ b/lib/stdlib/test/edlin_expand_SUITE.erl @@ -46,10 +46,10 @@ groups() -> []. init_per_suite(Config) -> - true = code:delete(expand_test), - true = code:delete(expand_test1), - true = code:delete('ExpandTestCaps'), - true = code:delete('ExpandTestCaps1'), + (catch code:delete(expand_test)), + (catch code:delete(expand_test1)), + (catch code:delete('ExpandTestCaps')), + (catch code:delete('ExpandTestCaps1')), Config. end_per_suite(_Config) -> diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl index b69cd74edb..0b74d04b85 100644 --- a/lib/stdlib/test/io_proto_SUITE.erl +++ b/lib/stdlib/test/io_proto_SUITE.erl @@ -50,7 +50,7 @@ -define(privdir(Conf), ?config(priv_dir, Conf)). -endif. --define(debug, true). +%%-define(debug, true). -ifdef(debug). -define(format(S, A), io:format(S, A)). diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index 50a76cdfb5..1e74ad7727 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -7927,15 +7927,23 @@ run_test(Config, Extra, {cres, Body, Opts, ExpectedCompileReturn}) -> ok end, + wait_for_expected(R, Before, SourceFile, true), + code:purge(Mod); +run_test(Config, Extra, Body) -> + run_test(Config, Extra, {cres,Body,[]}). + +wait_for_expected(R, Before, SourceFile, Wait) -> Ms = erlang:process_info(self(),messages), After = {get(), pps(), ets:all(), Ms}, - code:purge(Mod), case {R, After} of - {ok, Before} -> ok; - _ -> expected({ok,Before}, {R,After}, SourceFile) - end; -run_test(Config, Extra, Body) -> - run_test(Config, Extra, {cres,Body,[]}). + {ok, Before} -> + ok; + _ when Wait -> + timer:sleep(1000), + wait_for_expected(R, Before, SourceFile, false); + _ -> + expected({ok,Before}, {R,After}, SourceFile) + end. unload_pt() -> erlang:garbage_collect(), % get rid of references to qlc_pt... diff --git a/lib/wx/api_gen/gl_gen.erl b/lib/wx/api_gen/gl_gen.erl index ce0cb922e9..331ba32ba4 100644 --- a/lib/wx/api_gen/gl_gen.erl +++ b/lib/wx/api_gen/gl_gen.erl @@ -190,14 +190,17 @@ parse_define([#xmlElement{name=initializer,content=[#xmlText{value=V}]}|_],Def,_ try case Val0 of "0x" ++ Val1 -> - Val = http_util:hexlist_to_integer(Val1), - Def#def{val=Val, type=hex}; + _ = http_util:hexlist_to_integer(Val1), + Def#def{val=Val1, type=hex}; _ -> Val = list_to_integer(Val0), Def#def{val=Val, type=int} end - catch _:_ -> - Def#def{val=Val0, type=string} + catch _:_ -> + case catch list_to_float(Val0) of + {'EXIT', _} -> Def#def{val=Val0, type=string}; + _ -> Def#def{val=Val0, type=float_str} + end end; parse_define([_|R], D, Opts) -> parse_define(R, D, Opts); diff --git a/lib/wx/api_gen/gl_gen_erl.erl b/lib/wx/api_gen/gl_gen_erl.erl index f77b2d8e24..25f89e4ad4 100644 --- a/lib/wx/api_gen/gl_gen_erl.erl +++ b/lib/wx/api_gen/gl_gen_erl.erl @@ -54,8 +54,10 @@ glu_defines(Defs) -> gen_define(#def{name=N, val=Val, type=int}) -> w("-define(~s, ~p).~n", [N,Val]); +gen_define(#def{name=N, val=Val, type=float_str}) -> + w("-define(~s, ~s).~n", [N,Val]); gen_define(#def{name=N, val=Val, type=hex}) -> - w("-define(~s, ~.16#).~n", [N,Val]); + w("-define(~s, 16#~s).~n", [N,Val]); gen_define(#def{name=N, val=Val, type=string}) -> w("-define(~s, ?~s).~n", [N,Val]); gen_define(#def{name="GLEXT_64_TYPES"++_, val=undefined, type=undefined}) -> diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl index 69e7510a95..2209e4a53b 100644 --- a/lib/wx/api_gen/wx_gen_cpp.erl +++ b/lib/wx/api_gen/wx_gen_cpp.erl @@ -350,8 +350,12 @@ declare_type(N,false,_,#type{name="wxDateTime"}) -> w(" wxDateTime ~s;~n", [N]); declare_type(N,false,_,#type{name="wxColour"}) -> w(" wxColour ~s;~n", [N]); +declare_type(N,false,_,#type{name=Type, base=int, ref=reference}) -> + w(" ~s ~s;~n", [Type,N]); declare_type(N,false,_,#type{name=Type, base=int64, ref=reference}) -> w(" ~s ~s;~n", [Type,N]); +declare_type(N,false,_,#type{base={comp,_,_},single=true,name=Type,ref=reference}) -> + w(" ~s ~s;~n", [Type,N]); declare_type(N,true,Def,#type{base=Base,single=true,name=Type,by_val=true}) when Base =:= int; Base =:= long; Base =:= float; Base =:= double; Base =:= bool -> w(" ~s ~s=~s;~n", [Type,N,Def]); @@ -812,6 +816,7 @@ call_arg(#param{name=N,in=false,type=#type{by_val=false, single=true}}) -> "&" + call_arg(#param{name=N,def=Def,type=#type{base={comp,_,_},ref={pointer,1},single=true}}) when Def =:= none -> "&" ++N; +call_arg(#param{name=N,type=#type{base=int, ref=reference, single=true}}) -> "*" ++ N; call_arg(#param{name=N,type=#type{by_val=false}}) -> N; call_arg(#param{name=N,type={merged,_,#type{base={class,_},single=true, by_val=ByVal, @@ -888,7 +893,7 @@ build_return_vals(Type,Ps) -> build_ret_types(void,Ps) -> Calc = fun(#param{name=N,in=False,type=T}, Free) when False =/= true -> - case build_ret(N, False, T) of + case build_ret(N, {arg, False}, T) of ok -> Free; Other -> [Other|Free] end; @@ -896,12 +901,12 @@ build_ret_types(void,Ps) -> end, lists:foldl(Calc, [], Ps); build_ret_types(Type,Ps) -> - Free = case build_ret("Result", out, Type) of + Free = case build_ret("Result", {ret, out}, Type) of ok -> []; FreeStr -> [FreeStr] end, Calc = fun(#param{name=N,in=False,type=T}, FreeAcc) when False =/= true -> - case build_ret(N, False, T) of + case build_ret(N, {arg, False}, T) of ok -> FreeAcc; FreeMe -> [FreeMe|FreeAcc] end; @@ -927,13 +932,13 @@ build_ret(Name,_,#type{base={enum,_Type},single=true}) -> w(" rt.addInt(~s);~n",[Name]); build_ret(Name,_,#type{base={comp,_,{record, _}},single=true}) -> w(" rt.add(~s);~n", [Name]); -build_ret(Name,_,#type{base={comp,_,_},single=true, ref=reference}) -> +build_ret(Name,{ret,_},#type{base={comp,_,_},single=true, ref=reference}) -> w(" rt.add((*~s));~n",[Name]); build_ret(Name,_,#type{base={comp,_,_},single=true}) -> w(" rt.add(~s);~n",[Name]); build_ret(Name,_,#type{base=bool,single=true,by_val=true}) -> w(" rt.addBool(~s);~n",[Name]); -build_ret(Name,both,#type{base=int,single=true,mod=M}) -> +build_ret(Name,{arg, both},#type{base=int,single=true,mod=M}) -> case lists:member(unsigned, M) of true -> w(" rt.addUint(*~s);~n",[Name]); false -> w(" rt.addInt(*~s);~n",[Name]) diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl index aac57586bc..6159b7c4bd 100644 --- a/lib/wx/api_gen/wx_gen_erl.erl +++ b/lib/wx/api_gen/wx_gen_erl.erl @@ -177,7 +177,18 @@ parents_check([Parent|Ps]) -> w("parent_class(~s) -> true;~n",[Parent]), parents_check(Ps). -check_class(#type{base={class,"wx"}}) -> ok; +check_class(#type{name="wxObject", base={class,"wx"}}) -> + "wx:wx_object()"; +check_class(#type{name="wxIconLocation", base={class,"wx"}}) -> + "wx:wx_object()"; +check_class(#type{name="wxToolBarToolBase", base={class,"wx"}, mod=Mod}) -> + %% Implement this some day + "wx:wx_object()"; +check_class(#type{name="wxValidator", base={class,"wx"}, mod=Mod}) -> + %% Implement this some day + "wx:wx_object()"; +check_class(#type{name=Name, base={class,"wx"}}) -> + exit({class, Name}); check_class(#type{base={class,Name},xml=Xml}) -> case get({class,Name}) of undefined -> @@ -190,12 +201,15 @@ check_class(#type{base={class,Name},xml=Xml}) -> _ -> ?warning("~s:~s: Class ~p used but not defined~n (see ~p)~n", [get(current_class),get(current_func),Name, Xml]) - end; + end, + "wx:wx_object()"; _ -> ?warning("~s:~s: Class ~p used is enum~n", - [get(current_class),get(current_func),Name]) + [get(current_class),get(current_func),Name]), + exit(class_enum) end; - _ -> ok + _ -> + Name ++ ":" ++ Name ++ "()" end. gen_export(#class{name=Class,abstract=Abs},Ms0) -> @@ -766,21 +780,21 @@ doc_arg_type3(#type{base=eventType}, _) -> "atom()"; doc_arg_type3(#type{base={ref,N}}, _) -> N++"()"; doc_arg_type3(#type{base={term,_N}}, _) -> "term()"; doc_arg_type3(T=#type{base={class,N}}, _) -> - check_class(T), - case get(current_class) of - N -> N ++ "()"; - _ -> N++":" ++ N++"()" + ClassType = check_class(T), + Current = get(current_class), + if N =:= Current -> N ++ "()"; + true -> ClassType end; doc_arg_type3({merged,_,T1=#type{base={class,N1}},_,_,T2=#type{base={class,N2}},_}, _) -> - check_class(T1), - check_class(T2), + CT1 = check_class(T1), + CT2 = check_class(T2), Curr = get(current_class), if - N1 =:= Curr, N2 =:= Curr -> N1++"() | "++ N2++"()"; - N1 =:= Curr -> N1++"() | "++ N2++":" ++ N2++"()"; - N2 =:= Curr -> N1++":" ++ N1++"() | "++ N2++"()"; + N1 =:= Curr, N2 =:= Curr -> N1++"()"; + N1 =:= Curr -> N1++"() | "++ CT2; + N2 =:= Curr -> CT1 ++ " | "++ N2++"()"; true -> - N1++":" ++ N1++"() | "++ N2++":" ++ N2++"()" + CT1 ++ " | " ++ CT2 end; %% doc_arg_type3(#type{base={enum,{_,N}}}, _) -> uppercase(N); %% doc_arg_type3(#type{base={enum,N}}, _) -> uppercase(N); @@ -1010,12 +1024,13 @@ enum_name(Name) -> gen_enums_ints() -> %% open_write("../include/wx.hrl"), opened in gen_event_recs w("~n%% Hardcoded Records~n", []), - w("-record(wxMouseState, {x, y, %% integer()~n" - " leftDown, middleDown, rightDown, %% bool()~n" - " controlDown, shiftDown, altDown, metaDown, cmdDown %% bool()~n" + w("-record(wxMouseState, {x :: integer(), y :: integer(),~n" + " leftDown :: boolean(), middleDown :: boolean, rightDown :: boolean, ~n" + " controlDown :: boolean(), shiftDown :: boolean(),~n" + " altDown :: boolean(), metaDown :: boolean(), cmdDown :: boolean()~n" " }).~n", []), w("-record(wxHtmlLinkInfo, {~n" - " href, target %% unicode:chardata()~n" + " href :: unicode:chardata(), target :: unicode:chardata()~n" " }).~n", []), w("~n%% Hardcoded Defines~n", []), Enums = [E || {{enum,_},E = #enum{as_atom=false}} <- get()], @@ -1045,16 +1060,16 @@ build_enum_ints(#enum{from=From, vals=Vals},Done) -> Format = fun(#const{name="wxEVT_" ++ _}) -> ignore; %% Ignore event macros they are not valid in our event model - (#const{name=Name,val=Value,is_const=true}) when is_integer(Value) -> + (#const{name=Name,val=Value,is_const=true}) when is_number(Value) -> w("-define(~s, ~p).~n", [enum_name(Name),Value]); - (#const{name=Name,val=Value,is_const=false}) when is_integer(Value) -> + (#const{name=Name,val=Value,is_const=false}) when is_number(Value) -> w("-define(~s, wxe_util:get_const(~s)).~n", [enum_name(Name),enum_name(Name)]); (#const{name=Name,val={Str,0}}) -> case string:tokens(Str, " |()") of [Token] -> - w("-define(~s, ?~s).~n", [enum_name(Name),Token]); + w("-define(~s, ~s).~n", [enum_name(Name),const_value(Token)]); Tokens -> - Def = args(fun(T) -> [$?|T] end, " bor ", Tokens), + Def = args(fun(T) -> const_value(T) end, " bor ", Tokens), w("-define(~s, (~s)).~n", [enum_name(Name),Def]) end; (#const{name=Name,val={Str,N}}) -> @@ -1079,6 +1094,17 @@ build_enum_ints(#enum{from=From, vals=Vals},Done) -> end, lists:foldl(Write, Done, Vals). +const_value(V) when is_integer(V) -> integer_to_list(V); +const_value(V = "16#" ++ IntList) -> + _ = http_util:hexlist_to_integer(IntList), %% ASSERT + V; +const_value(V0) -> + try + _ = list_to_integer(V0), + V0 + catch _:_ -> [$?|V0] + end. + gen_event_recs() -> open_write("../include/wx.hrl"), erl_copyright(), diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf index 383addfe3f..1f6225ce60 100644 --- a/lib/wx/api_gen/wxapi.conf +++ b/lib/wx/api_gen/wxapi.conf @@ -776,10 +776,12 @@ 'GetEditControl', 'GetImageList','GetItem','GetItemBackgroundColour', 'GetItemCount',{'GetItemData', [{return, {base,int}}]}, - 'GetItemFont','GetItemPosition','GetItemRect', - 'GetItemSpacing','GetItemState','GetItemText','GetItemTextColour', + 'GetItemFont', {'GetItemPosition', [{"pos", out}]},{'GetItemRect',[{"rect", out}]}, + 'GetItemSpacing','GetItemState', + 'GetItemText','GetItemTextColour', 'GetNextItem','GetSelectedItemCount','GetTextColour','GetTopItem', - 'GetViewRect',{'HitTest',[{"pSubItem",nowhere}]},'InsertColumn','InsertItem', + 'GetViewRect',{'HitTest',[{"pSubItem",nowhere}, {"flags", in}]}, + 'InsertColumn','InsertItem', %%'OnGetItemAttr', 'OnGetItemImage','OnGetItemText', 'RefreshItem','RefreshItems','ScrollList', 'SetBackgroundColour','SetColumn','SetColumnWidth','SetImageList','SetItem', diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp index c81b3c88c0..15012011ed 100644 --- a/lib/wx/c_src/gen/wxe_funcs.cpp +++ b/lib/wx/c_src/gen/wxe_funcs.cpp @@ -15420,25 +15420,21 @@ case wxListCtrl_GetItemFont: { // wxListCtrl::GetItemFont break; } case wxListCtrl_GetItemPosition: { // wxListCtrl::GetItemPosition + wxPoint pos; wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4; int * item = (int *) bp; bp += 4; - int * posX = (int *) bp; bp += 4; - int * posY = (int *) bp; bp += 4; - wxPoint pos = wxPoint(*posX,*posY); if(!This) throw wxe_badarg(0); bool Result = This->GetItemPosition((long) *item,pos); rt.addBool(Result); + rt.add(pos); + rt.addTupleCount(2); break; } case wxListCtrl_GetItemRect: { // wxListCtrl::GetItemRect + wxRect rect; int code=wxLIST_RECT_BOUNDS; wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4; int * item = (int *) bp; bp += 4; - int * rectX = (int *) bp; bp += 4; - int * rectY = (int *) bp; bp += 4; - int * rectW = (int *) bp; bp += 4; - int * rectH = (int *) bp; bp += 4; - wxRect rect = wxRect(*rectX,*rectY,*rectW,*rectH); while( * (int*) bp) { switch (* (int*) bp) { case 1: {bp += 4; code = (int)*(int *) bp; bp += 4; @@ -15447,6 +15443,8 @@ case wxListCtrl_GetItemRect: { // wxListCtrl::GetItemRect if(!This) throw wxe_badarg(0); bool Result = This->GetItemRect((long) *item,rect,code); rt.addBool(Result); + rt.add(rect); + rt.addTupleCount(2); break; } case wxListCtrl_GetItemSpacing: { // wxListCtrl::GetItemSpacing @@ -15528,16 +15526,14 @@ case wxListCtrl_GetViewRect: { // wxListCtrl::GetViewRect break; } case wxListCtrl_HitTest: { // wxListCtrl::HitTest - int flags; wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4; int * pointX = (int *) bp; bp += 4; int * pointY = (int *) bp; bp += 4; wxPoint point = wxPoint(*pointX,*pointY); + int * flags = (int *) bp; bp += 4; if(!This) throw wxe_badarg(0); - long Result = This->HitTest(point,flags); + long Result = This->HitTest(point,*flags); rt.addInt(Result); - rt.addInt(flags); - rt.addTupleCount(2); break; } case wxListCtrl_InsertColumn_2: { // wxListCtrl::InsertColumn diff --git a/lib/wx/include/gl.hrl b/lib/wx/include/gl.hrl index c643ae0962..9004a8aa31 100644 --- a/lib/wx/include/gl.hrl +++ b/lib/wx/include/gl.hrl @@ -37,16 +37,16 @@ -define(GL_3_BYTES, 16#1408). -define(GL_4_BYTES, 16#1409). -define(GL_DOUBLE, 16#140A). --define(GL_POINTS, 16#0). --define(GL_LINES, 16#1). --define(GL_LINE_LOOP, 16#2). --define(GL_LINE_STRIP, 16#3). --define(GL_TRIANGLES, 16#4). --define(GL_TRIANGLE_STRIP, 16#5). --define(GL_TRIANGLE_FAN, 16#6). --define(GL_QUADS, 16#7). --define(GL_QUAD_STRIP, 16#8). --define(GL_POLYGON, 16#9). +-define(GL_POINTS, 16#0000). +-define(GL_LINES, 16#0001). +-define(GL_LINE_LOOP, 16#0002). +-define(GL_LINE_STRIP, 16#0003). +-define(GL_TRIANGLES, 16#0004). +-define(GL_TRIANGLE_STRIP, 16#0005). +-define(GL_TRIANGLE_FAN, 16#0006). +-define(GL_QUADS, 16#0007). +-define(GL_QUAD_STRIP, 16#0008). +-define(GL_POLYGON, 16#0009). -define(GL_VERTEX_ARRAY, 16#8074). -define(GL_NORMAL_ARRAY, 16#8075). -define(GL_COLOR_ARRAY, 16#8076). @@ -87,35 +87,35 @@ -define(GL_T2F_N3F_V3F, 16#2A2B). -define(GL_T2F_C4F_N3F_V3F, 16#2A2C). -define(GL_T4F_C4F_N3F_V4F, 16#2A2D). --define(GL_MATRIX_MODE, 16#BA0). +-define(GL_MATRIX_MODE, 16#0BA0). -define(GL_MODELVIEW, 16#1700). -define(GL_PROJECTION, 16#1701). -define(GL_TEXTURE, 16#1702). --define(GL_POINT_SMOOTH, 16#B10). --define(GL_POINT_SIZE, 16#B11). --define(GL_POINT_SIZE_GRANULARITY, 16#B13). --define(GL_POINT_SIZE_RANGE, 16#B12). --define(GL_LINE_SMOOTH, 16#B20). --define(GL_LINE_STIPPLE, 16#B24). --define(GL_LINE_STIPPLE_PATTERN, 16#B25). --define(GL_LINE_STIPPLE_REPEAT, 16#B26). --define(GL_LINE_WIDTH, 16#B21). --define(GL_LINE_WIDTH_GRANULARITY, 16#B23). --define(GL_LINE_WIDTH_RANGE, 16#B22). +-define(GL_POINT_SMOOTH, 16#0B10). +-define(GL_POINT_SIZE, 16#0B11). +-define(GL_POINT_SIZE_GRANULARITY, 16#0B13). +-define(GL_POINT_SIZE_RANGE, 16#0B12). +-define(GL_LINE_SMOOTH, 16#0B20). +-define(GL_LINE_STIPPLE, 16#0B24). +-define(GL_LINE_STIPPLE_PATTERN, 16#0B25). +-define(GL_LINE_STIPPLE_REPEAT, 16#0B26). +-define(GL_LINE_WIDTH, 16#0B21). +-define(GL_LINE_WIDTH_GRANULARITY, 16#0B23). +-define(GL_LINE_WIDTH_RANGE, 16#0B22). -define(GL_POINT, 16#1B00). -define(GL_LINE, 16#1B01). -define(GL_FILL, 16#1B02). --define(GL_CW, 16#900). --define(GL_CCW, 16#901). --define(GL_FRONT, 16#404). --define(GL_BACK, 16#405). --define(GL_POLYGON_MODE, 16#B40). --define(GL_POLYGON_SMOOTH, 16#B41). --define(GL_POLYGON_STIPPLE, 16#B42). --define(GL_EDGE_FLAG, 16#B43). --define(GL_CULL_FACE, 16#B44). --define(GL_CULL_FACE_MODE, 16#B45). --define(GL_FRONT_FACE, 16#B46). +-define(GL_CW, 16#0900). +-define(GL_CCW, 16#0901). +-define(GL_FRONT, 16#0404). +-define(GL_BACK, 16#0405). +-define(GL_POLYGON_MODE, 16#0B40). +-define(GL_POLYGON_SMOOTH, 16#0B41). +-define(GL_POLYGON_STIPPLE, 16#0B42). +-define(GL_EDGE_FLAG, 16#0B43). +-define(GL_CULL_FACE, 16#0B44). +-define(GL_CULL_FACE_MODE, 16#0B45). +-define(GL_FRONT_FACE, 16#0B46). -define(GL_POLYGON_OFFSET_FACTOR, 16#8038). -define(GL_POLYGON_OFFSET_UNITS, 16#2A00). -define(GL_POLYGON_OFFSET_POINT, 16#2A01). @@ -123,25 +123,25 @@ -define(GL_POLYGON_OFFSET_FILL, 16#8037). -define(GL_COMPILE, 16#1300). -define(GL_COMPILE_AND_EXECUTE, 16#1301). --define(GL_LIST_BASE, 16#B32). --define(GL_LIST_INDEX, 16#B33). --define(GL_LIST_MODE, 16#B30). --define(GL_NEVER, 16#200). --define(GL_LESS, 16#201). --define(GL_EQUAL, 16#202). --define(GL_LEQUAL, 16#203). --define(GL_GREATER, 16#204). --define(GL_NOTEQUAL, 16#205). --define(GL_GEQUAL, 16#206). --define(GL_ALWAYS, 16#207). --define(GL_DEPTH_TEST, 16#B71). --define(GL_DEPTH_BITS, 16#D56). --define(GL_DEPTH_CLEAR_VALUE, 16#B73). --define(GL_DEPTH_FUNC, 16#B74). --define(GL_DEPTH_RANGE, 16#B70). --define(GL_DEPTH_WRITEMASK, 16#B72). +-define(GL_LIST_BASE, 16#0B32). +-define(GL_LIST_INDEX, 16#0B33). +-define(GL_LIST_MODE, 16#0B30). +-define(GL_NEVER, 16#0200). +-define(GL_LESS, 16#0201). +-define(GL_EQUAL, 16#0202). +-define(GL_LEQUAL, 16#0203). +-define(GL_GREATER, 16#0204). +-define(GL_NOTEQUAL, 16#0205). +-define(GL_GEQUAL, 16#0206). +-define(GL_ALWAYS, 16#0207). +-define(GL_DEPTH_TEST, 16#0B71). +-define(GL_DEPTH_BITS, 16#0D56). +-define(GL_DEPTH_CLEAR_VALUE, 16#0B73). +-define(GL_DEPTH_FUNC, 16#0B74). +-define(GL_DEPTH_RANGE, 16#0B70). +-define(GL_DEPTH_WRITEMASK, 16#0B72). -define(GL_DEPTH_COMPONENT, 16#1902). --define(GL_LIGHTING, 16#B50). +-define(GL_LIGHTING, 16#0B50). -define(GL_LIGHT0, 16#4000). -define(GL_LIGHT1, 16#4001). -define(GL_LIGHT2, 16#4002). @@ -164,85 +164,85 @@ -define(GL_SPOT_DIRECTION, 16#1204). -define(GL_AMBIENT_AND_DIFFUSE, 16#1602). -define(GL_COLOR_INDEXES, 16#1603). --define(GL_LIGHT_MODEL_TWO_SIDE, 16#B52). --define(GL_LIGHT_MODEL_LOCAL_VIEWER, 16#B51). --define(GL_LIGHT_MODEL_AMBIENT, 16#B53). --define(GL_FRONT_AND_BACK, 16#408). --define(GL_SHADE_MODEL, 16#B54). +-define(GL_LIGHT_MODEL_TWO_SIDE, 16#0B52). +-define(GL_LIGHT_MODEL_LOCAL_VIEWER, 16#0B51). +-define(GL_LIGHT_MODEL_AMBIENT, 16#0B53). +-define(GL_FRONT_AND_BACK, 16#0408). +-define(GL_SHADE_MODEL, 16#0B54). -define(GL_FLAT, 16#1D00). -define(GL_SMOOTH, 16#1D01). --define(GL_COLOR_MATERIAL, 16#B57). --define(GL_COLOR_MATERIAL_FACE, 16#B55). --define(GL_COLOR_MATERIAL_PARAMETER, 16#B56). --define(GL_NORMALIZE, 16#BA1). +-define(GL_COLOR_MATERIAL, 16#0B57). +-define(GL_COLOR_MATERIAL_FACE, 16#0B55). +-define(GL_COLOR_MATERIAL_PARAMETER, 16#0B56). +-define(GL_NORMALIZE, 16#0BA1). -define(GL_CLIP_PLANE0, 16#3000). -define(GL_CLIP_PLANE1, 16#3001). -define(GL_CLIP_PLANE2, 16#3002). -define(GL_CLIP_PLANE3, 16#3003). -define(GL_CLIP_PLANE4, 16#3004). -define(GL_CLIP_PLANE5, 16#3005). --define(GL_ACCUM_RED_BITS, 16#D58). --define(GL_ACCUM_GREEN_BITS, 16#D59). --define(GL_ACCUM_BLUE_BITS, 16#D5A). --define(GL_ACCUM_ALPHA_BITS, 16#D5B). --define(GL_ACCUM_CLEAR_VALUE, 16#B80). --define(GL_ACCUM, 16#100). --define(GL_ADD, 16#104). --define(GL_LOAD, 16#101). --define(GL_MULT, 16#103). --define(GL_RETURN, 16#102). --define(GL_ALPHA_TEST, 16#BC0). --define(GL_ALPHA_TEST_REF, 16#BC2). --define(GL_ALPHA_TEST_FUNC, 16#BC1). --define(GL_BLEND, 16#BE2). --define(GL_BLEND_SRC, 16#BE1). --define(GL_BLEND_DST, 16#BE0). +-define(GL_ACCUM_RED_BITS, 16#0D58). +-define(GL_ACCUM_GREEN_BITS, 16#0D59). +-define(GL_ACCUM_BLUE_BITS, 16#0D5A). +-define(GL_ACCUM_ALPHA_BITS, 16#0D5B). +-define(GL_ACCUM_CLEAR_VALUE, 16#0B80). +-define(GL_ACCUM, 16#0100). +-define(GL_ADD, 16#0104). +-define(GL_LOAD, 16#0101). +-define(GL_MULT, 16#0103). +-define(GL_RETURN, 16#0102). +-define(GL_ALPHA_TEST, 16#0BC0). +-define(GL_ALPHA_TEST_REF, 16#0BC2). +-define(GL_ALPHA_TEST_FUNC, 16#0BC1). +-define(GL_BLEND, 16#0BE2). +-define(GL_BLEND_SRC, 16#0BE1). +-define(GL_BLEND_DST, 16#0BE0). -define(GL_ZERO, 16#0). -define(GL_ONE, 16#1). --define(GL_SRC_COLOR, 16#300). --define(GL_ONE_MINUS_SRC_COLOR, 16#301). --define(GL_SRC_ALPHA, 16#302). --define(GL_ONE_MINUS_SRC_ALPHA, 16#303). --define(GL_DST_ALPHA, 16#304). --define(GL_ONE_MINUS_DST_ALPHA, 16#305). --define(GL_DST_COLOR, 16#306). --define(GL_ONE_MINUS_DST_COLOR, 16#307). --define(GL_SRC_ALPHA_SATURATE, 16#308). +-define(GL_SRC_COLOR, 16#0300). +-define(GL_ONE_MINUS_SRC_COLOR, 16#0301). +-define(GL_SRC_ALPHA, 16#0302). +-define(GL_ONE_MINUS_SRC_ALPHA, 16#0303). +-define(GL_DST_ALPHA, 16#0304). +-define(GL_ONE_MINUS_DST_ALPHA, 16#0305). +-define(GL_DST_COLOR, 16#0306). +-define(GL_ONE_MINUS_DST_COLOR, 16#0307). +-define(GL_SRC_ALPHA_SATURATE, 16#0308). -define(GL_FEEDBACK, 16#1C01). -define(GL_RENDER, 16#1C00). -define(GL_SELECT, 16#1C02). --define(GL_2D, 16#600). --define(GL_3D, 16#601). --define(GL_3D_COLOR, 16#602). --define(GL_3D_COLOR_TEXTURE, 16#603). --define(GL_4D_COLOR_TEXTURE, 16#604). --define(GL_POINT_TOKEN, 16#701). --define(GL_LINE_TOKEN, 16#702). --define(GL_LINE_RESET_TOKEN, 16#707). --define(GL_POLYGON_TOKEN, 16#703). --define(GL_BITMAP_TOKEN, 16#704). --define(GL_DRAW_PIXEL_TOKEN, 16#705). --define(GL_COPY_PIXEL_TOKEN, 16#706). --define(GL_PASS_THROUGH_TOKEN, 16#700). --define(GL_FEEDBACK_BUFFER_POINTER, 16#DF0). --define(GL_FEEDBACK_BUFFER_SIZE, 16#DF1). --define(GL_FEEDBACK_BUFFER_TYPE, 16#DF2). --define(GL_SELECTION_BUFFER_POINTER, 16#DF3). --define(GL_SELECTION_BUFFER_SIZE, 16#DF4). --define(GL_FOG, 16#B60). --define(GL_FOG_MODE, 16#B65). --define(GL_FOG_DENSITY, 16#B62). --define(GL_FOG_COLOR, 16#B66). --define(GL_FOG_INDEX, 16#B61). --define(GL_FOG_START, 16#B63). --define(GL_FOG_END, 16#B64). +-define(GL_2D, 16#0600). +-define(GL_3D, 16#0601). +-define(GL_3D_COLOR, 16#0602). +-define(GL_3D_COLOR_TEXTURE, 16#0603). +-define(GL_4D_COLOR_TEXTURE, 16#0604). +-define(GL_POINT_TOKEN, 16#0701). +-define(GL_LINE_TOKEN, 16#0702). +-define(GL_LINE_RESET_TOKEN, 16#0707). +-define(GL_POLYGON_TOKEN, 16#0703). +-define(GL_BITMAP_TOKEN, 16#0704). +-define(GL_DRAW_PIXEL_TOKEN, 16#0705). +-define(GL_COPY_PIXEL_TOKEN, 16#0706). +-define(GL_PASS_THROUGH_TOKEN, 16#0700). +-define(GL_FEEDBACK_BUFFER_POINTER, 16#0DF0). +-define(GL_FEEDBACK_BUFFER_SIZE, 16#0DF1). +-define(GL_FEEDBACK_BUFFER_TYPE, 16#0DF2). +-define(GL_SELECTION_BUFFER_POINTER, 16#0DF3). +-define(GL_SELECTION_BUFFER_SIZE, 16#0DF4). +-define(GL_FOG, 16#0B60). +-define(GL_FOG_MODE, 16#0B65). +-define(GL_FOG_DENSITY, 16#0B62). +-define(GL_FOG_COLOR, 16#0B66). +-define(GL_FOG_INDEX, 16#0B61). +-define(GL_FOG_START, 16#0B63). +-define(GL_FOG_END, 16#0B64). -define(GL_LINEAR, 16#2601). --define(GL_EXP, 16#800). --define(GL_EXP2, 16#801). --define(GL_LOGIC_OP, 16#BF1). --define(GL_INDEX_LOGIC_OP, 16#BF1). --define(GL_COLOR_LOGIC_OP, 16#BF2). --define(GL_LOGIC_OP_MODE, 16#BF0). +-define(GL_EXP, 16#0800). +-define(GL_EXP2, 16#0801). +-define(GL_LOGIC_OP, 16#0BF1). +-define(GL_INDEX_LOGIC_OP, 16#0BF1). +-define(GL_COLOR_LOGIC_OP, 16#0BF2). +-define(GL_LOGIC_OP_MODE, 16#0BF0). -define(GL_CLEAR, 16#1500). -define(GL_SET, 16#150F). -define(GL_COPY, 16#1503). @@ -259,32 +259,32 @@ -define(GL_AND_INVERTED, 16#1504). -define(GL_OR_REVERSE, 16#150B). -define(GL_OR_INVERTED, 16#150D). --define(GL_STENCIL_BITS, 16#D57). --define(GL_STENCIL_TEST, 16#B90). --define(GL_STENCIL_CLEAR_VALUE, 16#B91). --define(GL_STENCIL_FUNC, 16#B92). --define(GL_STENCIL_VALUE_MASK, 16#B93). --define(GL_STENCIL_FAIL, 16#B94). --define(GL_STENCIL_PASS_DEPTH_FAIL, 16#B95). --define(GL_STENCIL_PASS_DEPTH_PASS, 16#B96). --define(GL_STENCIL_REF, 16#B97). --define(GL_STENCIL_WRITEMASK, 16#B98). +-define(GL_STENCIL_BITS, 16#0D57). +-define(GL_STENCIL_TEST, 16#0B90). +-define(GL_STENCIL_CLEAR_VALUE, 16#0B91). +-define(GL_STENCIL_FUNC, 16#0B92). +-define(GL_STENCIL_VALUE_MASK, 16#0B93). +-define(GL_STENCIL_FAIL, 16#0B94). +-define(GL_STENCIL_PASS_DEPTH_FAIL, 16#0B95). +-define(GL_STENCIL_PASS_DEPTH_PASS, 16#0B96). +-define(GL_STENCIL_REF, 16#0B97). +-define(GL_STENCIL_WRITEMASK, 16#0B98). -define(GL_STENCIL_INDEX, 16#1901). -define(GL_KEEP, 16#1E00). -define(GL_REPLACE, 16#1E01). -define(GL_INCR, 16#1E02). -define(GL_DECR, 16#1E03). -define(GL_NONE, 16#0). --define(GL_LEFT, 16#406). --define(GL_RIGHT, 16#407). --define(GL_FRONT_LEFT, 16#400). --define(GL_FRONT_RIGHT, 16#401). --define(GL_BACK_LEFT, 16#402). --define(GL_BACK_RIGHT, 16#403). --define(GL_AUX0, 16#409). --define(GL_AUX1, 16#40A). --define(GL_AUX2, 16#40B). --define(GL_AUX3, 16#40C). +-define(GL_LEFT, 16#0406). +-define(GL_RIGHT, 16#0407). +-define(GL_FRONT_LEFT, 16#0400). +-define(GL_FRONT_RIGHT, 16#0401). +-define(GL_BACK_LEFT, 16#0402). +-define(GL_BACK_RIGHT, 16#0403). +-define(GL_AUX0, 16#0409). +-define(GL_AUX1, 16#040A). +-define(GL_AUX2, 16#040B). +-define(GL_AUX3, 16#040C). -define(GL_COLOR_INDEX, 16#1900). -define(GL_RED, 16#1903). -define(GL_GREEN, 16#1904). @@ -292,161 +292,161 @@ -define(GL_ALPHA, 16#1906). -define(GL_LUMINANCE, 16#1909). -define(GL_LUMINANCE_ALPHA, 16#190A). --define(GL_ALPHA_BITS, 16#D55). --define(GL_RED_BITS, 16#D52). --define(GL_GREEN_BITS, 16#D53). --define(GL_BLUE_BITS, 16#D54). --define(GL_INDEX_BITS, 16#D51). --define(GL_SUBPIXEL_BITS, 16#D50). --define(GL_AUX_BUFFERS, 16#C00). --define(GL_READ_BUFFER, 16#C02). --define(GL_DRAW_BUFFER, 16#C01). --define(GL_DOUBLEBUFFER, 16#C32). --define(GL_STEREO, 16#C33). +-define(GL_ALPHA_BITS, 16#0D55). +-define(GL_RED_BITS, 16#0D52). +-define(GL_GREEN_BITS, 16#0D53). +-define(GL_BLUE_BITS, 16#0D54). +-define(GL_INDEX_BITS, 16#0D51). +-define(GL_SUBPIXEL_BITS, 16#0D50). +-define(GL_AUX_BUFFERS, 16#0C00). +-define(GL_READ_BUFFER, 16#0C02). +-define(GL_DRAW_BUFFER, 16#0C01). +-define(GL_DOUBLEBUFFER, 16#0C32). +-define(GL_STEREO, 16#0C33). -define(GL_BITMAP, 16#1A00). -define(GL_COLOR, 16#1800). -define(GL_DEPTH, 16#1801). -define(GL_STENCIL, 16#1802). --define(GL_DITHER, 16#BD0). +-define(GL_DITHER, 16#0BD0). -define(GL_RGB, 16#1907). -define(GL_RGBA, 16#1908). --define(GL_MAX_LIST_NESTING, 16#B31). --define(GL_MAX_EVAL_ORDER, 16#D30). --define(GL_MAX_LIGHTS, 16#D31). --define(GL_MAX_CLIP_PLANES, 16#D32). --define(GL_MAX_TEXTURE_SIZE, 16#D33). --define(GL_MAX_PIXEL_MAP_TABLE, 16#D34). --define(GL_MAX_ATTRIB_STACK_DEPTH, 16#D35). --define(GL_MAX_MODELVIEW_STACK_DEPTH, 16#D36). --define(GL_MAX_NAME_STACK_DEPTH, 16#D37). --define(GL_MAX_PROJECTION_STACK_DEPTH, 16#D38). --define(GL_MAX_TEXTURE_STACK_DEPTH, 16#D39). --define(GL_MAX_VIEWPORT_DIMS, 16#D3A). --define(GL_MAX_CLIENT_ATTRIB_STACK_DEPTH, 16#D3B). --define(GL_ATTRIB_STACK_DEPTH, 16#BB0). --define(GL_CLIENT_ATTRIB_STACK_DEPTH, 16#BB1). --define(GL_COLOR_CLEAR_VALUE, 16#C22). --define(GL_COLOR_WRITEMASK, 16#C23). --define(GL_CURRENT_INDEX, 16#B01). --define(GL_CURRENT_COLOR, 16#B00). --define(GL_CURRENT_NORMAL, 16#B02). --define(GL_CURRENT_RASTER_COLOR, 16#B04). --define(GL_CURRENT_RASTER_DISTANCE, 16#B09). --define(GL_CURRENT_RASTER_INDEX, 16#B05). --define(GL_CURRENT_RASTER_POSITION, 16#B07). --define(GL_CURRENT_RASTER_TEXTURE_COORDS, 16#B06). --define(GL_CURRENT_RASTER_POSITION_VALID, 16#B08). --define(GL_CURRENT_TEXTURE_COORDS, 16#B03). --define(GL_INDEX_CLEAR_VALUE, 16#C20). --define(GL_INDEX_MODE, 16#C30). --define(GL_INDEX_WRITEMASK, 16#C21). --define(GL_MODELVIEW_MATRIX, 16#BA6). --define(GL_MODELVIEW_STACK_DEPTH, 16#BA3). --define(GL_NAME_STACK_DEPTH, 16#D70). --define(GL_PROJECTION_MATRIX, 16#BA7). --define(GL_PROJECTION_STACK_DEPTH, 16#BA4). --define(GL_RENDER_MODE, 16#C40). --define(GL_RGBA_MODE, 16#C31). --define(GL_TEXTURE_MATRIX, 16#BA8). --define(GL_TEXTURE_STACK_DEPTH, 16#BA5). --define(GL_VIEWPORT, 16#BA2). --define(GL_AUTO_NORMAL, 16#D80). --define(GL_MAP1_COLOR_4, 16#D90). --define(GL_MAP1_INDEX, 16#D91). --define(GL_MAP1_NORMAL, 16#D92). --define(GL_MAP1_TEXTURE_COORD_1, 16#D93). --define(GL_MAP1_TEXTURE_COORD_2, 16#D94). --define(GL_MAP1_TEXTURE_COORD_3, 16#D95). --define(GL_MAP1_TEXTURE_COORD_4, 16#D96). --define(GL_MAP1_VERTEX_3, 16#D97). --define(GL_MAP1_VERTEX_4, 16#D98). --define(GL_MAP2_COLOR_4, 16#DB0). --define(GL_MAP2_INDEX, 16#DB1). --define(GL_MAP2_NORMAL, 16#DB2). --define(GL_MAP2_TEXTURE_COORD_1, 16#DB3). --define(GL_MAP2_TEXTURE_COORD_2, 16#DB4). --define(GL_MAP2_TEXTURE_COORD_3, 16#DB5). --define(GL_MAP2_TEXTURE_COORD_4, 16#DB6). --define(GL_MAP2_VERTEX_3, 16#DB7). --define(GL_MAP2_VERTEX_4, 16#DB8). --define(GL_MAP1_GRID_DOMAIN, 16#DD0). --define(GL_MAP1_GRID_SEGMENTS, 16#DD1). --define(GL_MAP2_GRID_DOMAIN, 16#DD2). --define(GL_MAP2_GRID_SEGMENTS, 16#DD3). --define(GL_COEFF, 16#A00). --define(GL_ORDER, 16#A01). --define(GL_DOMAIN, 16#A02). --define(GL_PERSPECTIVE_CORRECTION_HINT, 16#C50). --define(GL_POINT_SMOOTH_HINT, 16#C51). --define(GL_LINE_SMOOTH_HINT, 16#C52). --define(GL_POLYGON_SMOOTH_HINT, 16#C53). --define(GL_FOG_HINT, 16#C54). +-define(GL_MAX_LIST_NESTING, 16#0B31). +-define(GL_MAX_EVAL_ORDER, 16#0D30). +-define(GL_MAX_LIGHTS, 16#0D31). +-define(GL_MAX_CLIP_PLANES, 16#0D32). +-define(GL_MAX_TEXTURE_SIZE, 16#0D33). +-define(GL_MAX_PIXEL_MAP_TABLE, 16#0D34). +-define(GL_MAX_ATTRIB_STACK_DEPTH, 16#0D35). +-define(GL_MAX_MODELVIEW_STACK_DEPTH, 16#0D36). +-define(GL_MAX_NAME_STACK_DEPTH, 16#0D37). +-define(GL_MAX_PROJECTION_STACK_DEPTH, 16#0D38). +-define(GL_MAX_TEXTURE_STACK_DEPTH, 16#0D39). +-define(GL_MAX_VIEWPORT_DIMS, 16#0D3A). +-define(GL_MAX_CLIENT_ATTRIB_STACK_DEPTH, 16#0D3B). +-define(GL_ATTRIB_STACK_DEPTH, 16#0BB0). +-define(GL_CLIENT_ATTRIB_STACK_DEPTH, 16#0BB1). +-define(GL_COLOR_CLEAR_VALUE, 16#0C22). +-define(GL_COLOR_WRITEMASK, 16#0C23). +-define(GL_CURRENT_INDEX, 16#0B01). +-define(GL_CURRENT_COLOR, 16#0B00). +-define(GL_CURRENT_NORMAL, 16#0B02). +-define(GL_CURRENT_RASTER_COLOR, 16#0B04). +-define(GL_CURRENT_RASTER_DISTANCE, 16#0B09). +-define(GL_CURRENT_RASTER_INDEX, 16#0B05). +-define(GL_CURRENT_RASTER_POSITION, 16#0B07). +-define(GL_CURRENT_RASTER_TEXTURE_COORDS, 16#0B06). +-define(GL_CURRENT_RASTER_POSITION_VALID, 16#0B08). +-define(GL_CURRENT_TEXTURE_COORDS, 16#0B03). +-define(GL_INDEX_CLEAR_VALUE, 16#0C20). +-define(GL_INDEX_MODE, 16#0C30). +-define(GL_INDEX_WRITEMASK, 16#0C21). +-define(GL_MODELVIEW_MATRIX, 16#0BA6). +-define(GL_MODELVIEW_STACK_DEPTH, 16#0BA3). +-define(GL_NAME_STACK_DEPTH, 16#0D70). +-define(GL_PROJECTION_MATRIX, 16#0BA7). +-define(GL_PROJECTION_STACK_DEPTH, 16#0BA4). +-define(GL_RENDER_MODE, 16#0C40). +-define(GL_RGBA_MODE, 16#0C31). +-define(GL_TEXTURE_MATRIX, 16#0BA8). +-define(GL_TEXTURE_STACK_DEPTH, 16#0BA5). +-define(GL_VIEWPORT, 16#0BA2). +-define(GL_AUTO_NORMAL, 16#0D80). +-define(GL_MAP1_COLOR_4, 16#0D90). +-define(GL_MAP1_INDEX, 16#0D91). +-define(GL_MAP1_NORMAL, 16#0D92). +-define(GL_MAP1_TEXTURE_COORD_1, 16#0D93). +-define(GL_MAP1_TEXTURE_COORD_2, 16#0D94). +-define(GL_MAP1_TEXTURE_COORD_3, 16#0D95). +-define(GL_MAP1_TEXTURE_COORD_4, 16#0D96). +-define(GL_MAP1_VERTEX_3, 16#0D97). +-define(GL_MAP1_VERTEX_4, 16#0D98). +-define(GL_MAP2_COLOR_4, 16#0DB0). +-define(GL_MAP2_INDEX, 16#0DB1). +-define(GL_MAP2_NORMAL, 16#0DB2). +-define(GL_MAP2_TEXTURE_COORD_1, 16#0DB3). +-define(GL_MAP2_TEXTURE_COORD_2, 16#0DB4). +-define(GL_MAP2_TEXTURE_COORD_3, 16#0DB5). +-define(GL_MAP2_TEXTURE_COORD_4, 16#0DB6). +-define(GL_MAP2_VERTEX_3, 16#0DB7). +-define(GL_MAP2_VERTEX_4, 16#0DB8). +-define(GL_MAP1_GRID_DOMAIN, 16#0DD0). +-define(GL_MAP1_GRID_SEGMENTS, 16#0DD1). +-define(GL_MAP2_GRID_DOMAIN, 16#0DD2). +-define(GL_MAP2_GRID_SEGMENTS, 16#0DD3). +-define(GL_COEFF, 16#0A00). +-define(GL_ORDER, 16#0A01). +-define(GL_DOMAIN, 16#0A02). +-define(GL_PERSPECTIVE_CORRECTION_HINT, 16#0C50). +-define(GL_POINT_SMOOTH_HINT, 16#0C51). +-define(GL_LINE_SMOOTH_HINT, 16#0C52). +-define(GL_POLYGON_SMOOTH_HINT, 16#0C53). +-define(GL_FOG_HINT, 16#0C54). -define(GL_DONT_CARE, 16#1100). -define(GL_FASTEST, 16#1101). -define(GL_NICEST, 16#1102). --define(GL_SCISSOR_BOX, 16#C10). --define(GL_SCISSOR_TEST, 16#C11). --define(GL_MAP_COLOR, 16#D10). --define(GL_MAP_STENCIL, 16#D11). --define(GL_INDEX_SHIFT, 16#D12). --define(GL_INDEX_OFFSET, 16#D13). --define(GL_RED_SCALE, 16#D14). --define(GL_RED_BIAS, 16#D15). --define(GL_GREEN_SCALE, 16#D18). --define(GL_GREEN_BIAS, 16#D19). --define(GL_BLUE_SCALE, 16#D1A). --define(GL_BLUE_BIAS, 16#D1B). --define(GL_ALPHA_SCALE, 16#D1C). --define(GL_ALPHA_BIAS, 16#D1D). --define(GL_DEPTH_SCALE, 16#D1E). --define(GL_DEPTH_BIAS, 16#D1F). --define(GL_PIXEL_MAP_S_TO_S_SIZE, 16#CB1). --define(GL_PIXEL_MAP_I_TO_I_SIZE, 16#CB0). --define(GL_PIXEL_MAP_I_TO_R_SIZE, 16#CB2). --define(GL_PIXEL_MAP_I_TO_G_SIZE, 16#CB3). --define(GL_PIXEL_MAP_I_TO_B_SIZE, 16#CB4). --define(GL_PIXEL_MAP_I_TO_A_SIZE, 16#CB5). --define(GL_PIXEL_MAP_R_TO_R_SIZE, 16#CB6). --define(GL_PIXEL_MAP_G_TO_G_SIZE, 16#CB7). --define(GL_PIXEL_MAP_B_TO_B_SIZE, 16#CB8). --define(GL_PIXEL_MAP_A_TO_A_SIZE, 16#CB9). --define(GL_PIXEL_MAP_S_TO_S, 16#C71). --define(GL_PIXEL_MAP_I_TO_I, 16#C70). --define(GL_PIXEL_MAP_I_TO_R, 16#C72). --define(GL_PIXEL_MAP_I_TO_G, 16#C73). --define(GL_PIXEL_MAP_I_TO_B, 16#C74). --define(GL_PIXEL_MAP_I_TO_A, 16#C75). --define(GL_PIXEL_MAP_R_TO_R, 16#C76). --define(GL_PIXEL_MAP_G_TO_G, 16#C77). --define(GL_PIXEL_MAP_B_TO_B, 16#C78). --define(GL_PIXEL_MAP_A_TO_A, 16#C79). --define(GL_PACK_ALIGNMENT, 16#D05). --define(GL_PACK_LSB_FIRST, 16#D01). --define(GL_PACK_ROW_LENGTH, 16#D02). --define(GL_PACK_SKIP_PIXELS, 16#D04). --define(GL_PACK_SKIP_ROWS, 16#D03). --define(GL_PACK_SWAP_BYTES, 16#D00). --define(GL_UNPACK_ALIGNMENT, 16#CF5). --define(GL_UNPACK_LSB_FIRST, 16#CF1). --define(GL_UNPACK_ROW_LENGTH, 16#CF2). --define(GL_UNPACK_SKIP_PIXELS, 16#CF4). --define(GL_UNPACK_SKIP_ROWS, 16#CF3). --define(GL_UNPACK_SWAP_BYTES, 16#CF0). --define(GL_ZOOM_X, 16#D16). --define(GL_ZOOM_Y, 16#D17). +-define(GL_SCISSOR_BOX, 16#0C10). +-define(GL_SCISSOR_TEST, 16#0C11). +-define(GL_MAP_COLOR, 16#0D10). +-define(GL_MAP_STENCIL, 16#0D11). +-define(GL_INDEX_SHIFT, 16#0D12). +-define(GL_INDEX_OFFSET, 16#0D13). +-define(GL_RED_SCALE, 16#0D14). +-define(GL_RED_BIAS, 16#0D15). +-define(GL_GREEN_SCALE, 16#0D18). +-define(GL_GREEN_BIAS, 16#0D19). +-define(GL_BLUE_SCALE, 16#0D1A). +-define(GL_BLUE_BIAS, 16#0D1B). +-define(GL_ALPHA_SCALE, 16#0D1C). +-define(GL_ALPHA_BIAS, 16#0D1D). +-define(GL_DEPTH_SCALE, 16#0D1E). +-define(GL_DEPTH_BIAS, 16#0D1F). +-define(GL_PIXEL_MAP_S_TO_S_SIZE, 16#0CB1). +-define(GL_PIXEL_MAP_I_TO_I_SIZE, 16#0CB0). +-define(GL_PIXEL_MAP_I_TO_R_SIZE, 16#0CB2). +-define(GL_PIXEL_MAP_I_TO_G_SIZE, 16#0CB3). +-define(GL_PIXEL_MAP_I_TO_B_SIZE, 16#0CB4). +-define(GL_PIXEL_MAP_I_TO_A_SIZE, 16#0CB5). +-define(GL_PIXEL_MAP_R_TO_R_SIZE, 16#0CB6). +-define(GL_PIXEL_MAP_G_TO_G_SIZE, 16#0CB7). +-define(GL_PIXEL_MAP_B_TO_B_SIZE, 16#0CB8). +-define(GL_PIXEL_MAP_A_TO_A_SIZE, 16#0CB9). +-define(GL_PIXEL_MAP_S_TO_S, 16#0C71). +-define(GL_PIXEL_MAP_I_TO_I, 16#0C70). +-define(GL_PIXEL_MAP_I_TO_R, 16#0C72). +-define(GL_PIXEL_MAP_I_TO_G, 16#0C73). +-define(GL_PIXEL_MAP_I_TO_B, 16#0C74). +-define(GL_PIXEL_MAP_I_TO_A, 16#0C75). +-define(GL_PIXEL_MAP_R_TO_R, 16#0C76). +-define(GL_PIXEL_MAP_G_TO_G, 16#0C77). +-define(GL_PIXEL_MAP_B_TO_B, 16#0C78). +-define(GL_PIXEL_MAP_A_TO_A, 16#0C79). +-define(GL_PACK_ALIGNMENT, 16#0D05). +-define(GL_PACK_LSB_FIRST, 16#0D01). +-define(GL_PACK_ROW_LENGTH, 16#0D02). +-define(GL_PACK_SKIP_PIXELS, 16#0D04). +-define(GL_PACK_SKIP_ROWS, 16#0D03). +-define(GL_PACK_SWAP_BYTES, 16#0D00). +-define(GL_UNPACK_ALIGNMENT, 16#0CF5). +-define(GL_UNPACK_LSB_FIRST, 16#0CF1). +-define(GL_UNPACK_ROW_LENGTH, 16#0CF2). +-define(GL_UNPACK_SKIP_PIXELS, 16#0CF4). +-define(GL_UNPACK_SKIP_ROWS, 16#0CF3). +-define(GL_UNPACK_SWAP_BYTES, 16#0CF0). +-define(GL_ZOOM_X, 16#0D16). +-define(GL_ZOOM_Y, 16#0D17). -define(GL_TEXTURE_ENV, 16#2300). -define(GL_TEXTURE_ENV_MODE, 16#2200). --define(GL_TEXTURE_1D, 16#DE0). --define(GL_TEXTURE_2D, 16#DE1). +-define(GL_TEXTURE_1D, 16#0DE0). +-define(GL_TEXTURE_2D, 16#0DE1). -define(GL_TEXTURE_WRAP_S, 16#2802). -define(GL_TEXTURE_WRAP_T, 16#2803). -define(GL_TEXTURE_MAG_FILTER, 16#2800). -define(GL_TEXTURE_MIN_FILTER, 16#2801). -define(GL_TEXTURE_ENV_COLOR, 16#2201). --define(GL_TEXTURE_GEN_S, 16#C60). --define(GL_TEXTURE_GEN_T, 16#C61). --define(GL_TEXTURE_GEN_R, 16#C62). --define(GL_TEXTURE_GEN_Q, 16#C63). +-define(GL_TEXTURE_GEN_S, 16#0C60). +-define(GL_TEXTURE_GEN_T, 16#0C61). +-define(GL_TEXTURE_GEN_R, 16#0C62). +-define(GL_TEXTURE_GEN_Q, 16#0C63). -define(GL_TEXTURE_GEN_MODE, 16#2500). -define(GL_TEXTURE_BORDER_COLOR, 16#1004). -define(GL_TEXTURE_WIDTH, 16#1000). @@ -482,33 +482,33 @@ -define(GL_VERSION, 16#1F02). -define(GL_EXTENSIONS, 16#1F03). -define(GL_NO_ERROR, 16#0). --define(GL_INVALID_ENUM, 16#500). --define(GL_INVALID_VALUE, 16#501). --define(GL_INVALID_OPERATION, 16#502). --define(GL_STACK_OVERFLOW, 16#503). --define(GL_STACK_UNDERFLOW, 16#504). --define(GL_OUT_OF_MEMORY, 16#505). --define(GL_CURRENT_BIT, 16#1). --define(GL_POINT_BIT, 16#2). --define(GL_LINE_BIT, 16#4). --define(GL_POLYGON_BIT, 16#8). --define(GL_POLYGON_STIPPLE_BIT, 16#10). --define(GL_PIXEL_MODE_BIT, 16#20). --define(GL_LIGHTING_BIT, 16#40). --define(GL_FOG_BIT, 16#80). --define(GL_DEPTH_BUFFER_BIT, 16#100). --define(GL_ACCUM_BUFFER_BIT, 16#200). --define(GL_STENCIL_BUFFER_BIT, 16#400). --define(GL_VIEWPORT_BIT, 16#800). --define(GL_TRANSFORM_BIT, 16#1000). --define(GL_ENABLE_BIT, 16#2000). --define(GL_COLOR_BUFFER_BIT, 16#4000). --define(GL_HINT_BIT, 16#8000). --define(GL_EVAL_BIT, 16#10000). --define(GL_LIST_BIT, 16#20000). --define(GL_TEXTURE_BIT, 16#40000). --define(GL_SCISSOR_BIT, 16#80000). --define(GL_ALL_ATTRIB_BITS, 16#FFFFF). +-define(GL_INVALID_ENUM, 16#0500). +-define(GL_INVALID_VALUE, 16#0501). +-define(GL_INVALID_OPERATION, 16#0502). +-define(GL_STACK_OVERFLOW, 16#0503). +-define(GL_STACK_UNDERFLOW, 16#0504). +-define(GL_OUT_OF_MEMORY, 16#0505). +-define(GL_CURRENT_BIT, 16#00000001). +-define(GL_POINT_BIT, 16#00000002). +-define(GL_LINE_BIT, 16#00000004). +-define(GL_POLYGON_BIT, 16#00000008). +-define(GL_POLYGON_STIPPLE_BIT, 16#00000010). +-define(GL_PIXEL_MODE_BIT, 16#00000020). +-define(GL_LIGHTING_BIT, 16#00000040). +-define(GL_FOG_BIT, 16#00000080). +-define(GL_DEPTH_BUFFER_BIT, 16#00000100). +-define(GL_ACCUM_BUFFER_BIT, 16#00000200). +-define(GL_STENCIL_BUFFER_BIT, 16#00000400). +-define(GL_VIEWPORT_BIT, 16#00000800). +-define(GL_TRANSFORM_BIT, 16#00001000). +-define(GL_ENABLE_BIT, 16#00002000). +-define(GL_COLOR_BUFFER_BIT, 16#00004000). +-define(GL_HINT_BIT, 16#00008000). +-define(GL_EVAL_BIT, 16#00010000). +-define(GL_LIST_BIT, 16#00020000). +-define(GL_TEXTURE_BIT, 16#00040000). +-define(GL_SCISSOR_BIT, 16#00080000). +-define(GL_ALL_ATTRIB_BITS, 16#000FFFFF). -define(GL_PROXY_TEXTURE_1D, 16#8063). -define(GL_PROXY_TEXTURE_2D, 16#8064). -define(GL_TEXTURE_PRIORITY, 16#8066). @@ -549,8 +549,8 @@ -define(GL_RGB10_A2, 16#8059). -define(GL_RGBA12, 16#805A). -define(GL_RGBA16, 16#805B). --define(GL_CLIENT_PIXEL_STORE_BIT, 16#1). --define(GL_CLIENT_VERTEX_ARRAY_BIT, 16#2). +-define(GL_CLIENT_PIXEL_STORE_BIT, 16#00000001). +-define(GL_CLIENT_VERTEX_ARRAY_BIT, 16#00000002). -define(GL_ALL_CLIENT_ATTRIB_BITS, 16#FFFFFFFF). -define(GL_CLIENT_ALL_ATTRIB_BITS, 16#FFFFFFFF). -define(GL_RESCALE_NORMAL, 16#803A). @@ -578,10 +578,10 @@ -define(GL_TEXTURE_MAX_LOD, 16#813B). -define(GL_TEXTURE_BASE_LEVEL, 16#813C). -define(GL_TEXTURE_MAX_LEVEL, 16#813D). --define(GL_SMOOTH_POINT_SIZE_RANGE, 16#B12). --define(GL_SMOOTH_POINT_SIZE_GRANULARITY, 16#B13). --define(GL_SMOOTH_LINE_WIDTH_RANGE, 16#B22). --define(GL_SMOOTH_LINE_WIDTH_GRANULARITY, 16#B23). +-define(GL_SMOOTH_POINT_SIZE_RANGE, 16#0B12). +-define(GL_SMOOTH_POINT_SIZE_GRANULARITY, 16#0B13). +-define(GL_SMOOTH_LINE_WIDTH_RANGE, 16#0B22). +-define(GL_SMOOTH_LINE_WIDTH_GRANULARITY, 16#0B23). -define(GL_ALIASED_POINT_SIZE_RANGE, 16#846D). -define(GL_ALIASED_LINE_WIDTH_RANGE, 16#846E). -define(GL_PACK_SKIP_IMAGES, 16#806B). @@ -808,14 +808,14 @@ -define(GL_UNSIGNED_SHORT_15_1_MESA, 16#8753). -define(GL_UNSIGNED_SHORT_1_15_REV_MESA, 16#8754). -define(GL_MESA_program_debug, 1). --define(GL_FRAGMENT_PROGRAM_POSITION_MESA, 16#8BB0). --define(GL_FRAGMENT_PROGRAM_CALLBACK_MESA, 16#8BB1). --define(GL_FRAGMENT_PROGRAM_CALLBACK_FUNC_MESA, 16#8BB2). --define(GL_FRAGMENT_PROGRAM_CALLBACK_DATA_MESA, 16#8BB3). --define(GL_VERTEX_PROGRAM_POSITION_MESA, 16#8BB4). --define(GL_VERTEX_PROGRAM_CALLBACK_MESA, 16#8BB5). --define(GL_VERTEX_PROGRAM_CALLBACK_FUNC_MESA, 16#8BB6). --define(GL_VERTEX_PROGRAM_CALLBACK_DATA_MESA, 16#8BB7). +-define(GL_FRAGMENT_PROGRAM_POSITION_MESA, 16#8bb0). +-define(GL_FRAGMENT_PROGRAM_CALLBACK_MESA, 16#8bb1). +-define(GL_FRAGMENT_PROGRAM_CALLBACK_FUNC_MESA, 16#8bb2). +-define(GL_FRAGMENT_PROGRAM_CALLBACK_DATA_MESA, 16#8bb3). +-define(GL_VERTEX_PROGRAM_POSITION_MESA, 16#8bb4). +-define(GL_VERTEX_PROGRAM_CALLBACK_MESA, 16#8bb5). +-define(GL_VERTEX_PROGRAM_CALLBACK_FUNC_MESA, 16#8bb6). +-define(GL_VERTEX_PROGRAM_CALLBACK_DATA_MESA, 16#8bb7). -define(GL_MESA_texture_array, 1). -define(GL_TEXTURE_1D_ARRAY_EXT, 16#8C18). -define(GL_PROXY_TEXTURE_1D_ARRAY_EXT, 16#8C19). @@ -1034,14 +1034,14 @@ -define(GL_CLIP_DISTANCE5, 16#3005). -define(GL_CLIP_DISTANCE6, 16#3006). -define(GL_CLIP_DISTANCE7, 16#3007). --define(GL_MAX_CLIP_DISTANCES, 16#D32). +-define(GL_MAX_CLIP_DISTANCES, 16#0D32). -define(GL_MAJOR_VERSION, 16#821B). -define(GL_MINOR_VERSION, 16#821C). -define(GL_NUM_EXTENSIONS, 16#821D). -define(GL_CONTEXT_FLAGS, 16#821E). -define(GL_COMPRESSED_RED, 16#8225). -define(GL_COMPRESSED_RG, 16#8226). --define(GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT, 16#1). +-define(GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT, 16#0001). -define(GL_RGBA32F, 16#8814). -define(GL_RGB32F, 16#8815). -define(GL_RGBA16F, 16#881A). @@ -1159,12 +1159,12 @@ -define(GL_SIGNED_NORMALIZED, 16#8F9C). -define(GL_PRIMITIVE_RESTART, 16#8F9D). -define(GL_PRIMITIVE_RESTART_INDEX, 16#8F9E). --define(GL_CONTEXT_CORE_PROFILE_BIT, 16#1). --define(GL_CONTEXT_COMPATIBILITY_PROFILE_BIT, 16#2). --define(GL_LINES_ADJACENCY, 16#A). --define(GL_LINE_STRIP_ADJACENCY, 16#B). --define(GL_TRIANGLES_ADJACENCY, 16#C). --define(GL_TRIANGLE_STRIP_ADJACENCY, 16#D). +-define(GL_CONTEXT_CORE_PROFILE_BIT, 16#00000001). +-define(GL_CONTEXT_COMPATIBILITY_PROFILE_BIT, 16#00000002). +-define(GL_LINES_ADJACENCY, 16#000A). +-define(GL_LINE_STRIP_ADJACENCY, 16#000B). +-define(GL_TRIANGLES_ADJACENCY, 16#000C). +-define(GL_TRIANGLE_STRIP_ADJACENCY, 16#000D). -define(GL_PROGRAM_POINT_SIZE, 16#8642). -define(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, 16#8C29). -define(GL_FRAMEBUFFER_ATTACHMENT_LAYERED, 16#8DA7). @@ -1551,7 +1551,7 @@ -define(GL_DEPTH_COMPONENT32F, 16#8CAC). -define(GL_DEPTH32F_STENCIL8, 16#8CAD). -define(GL_FLOAT_32_UNSIGNED_INT_24_8_REV, 16#8DAD). --define(GL_INVALID_FRAMEBUFFER_OPERATION, 16#506). +-define(GL_INVALID_FRAMEBUFFER_OPERATION, 16#0506). -define(GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, 16#8210). -define(GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, 16#8211). -define(GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, 16#8212). @@ -1632,10 +1632,10 @@ -define(GL_TEXTURE_LUMINANCE_TYPE, 16#8C14). -define(GL_TEXTURE_INTENSITY_TYPE, 16#8C15). -define(GL_FRAMEBUFFER_SRGB, 16#8DB9). --define(GL_LINES_ADJACENCY_ARB, 16#A). --define(GL_LINE_STRIP_ADJACENCY_ARB, 16#B). --define(GL_TRIANGLES_ADJACENCY_ARB, 16#C). --define(GL_TRIANGLE_STRIP_ADJACENCY_ARB, 16#D). +-define(GL_LINES_ADJACENCY_ARB, 16#000A). +-define(GL_LINE_STRIP_ADJACENCY_ARB, 16#000B). +-define(GL_TRIANGLES_ADJACENCY_ARB, 16#000C). +-define(GL_TRIANGLE_STRIP_ADJACENCY_ARB, 16#000D). -define(GL_PROGRAM_POINT_SIZE_ARB, 16#8642). -define(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB, 16#8C29). -define(GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB, 16#8DA7). @@ -1652,12 +1652,12 @@ -define(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB, 16#8DE1). -define(GL_HALF_FLOAT, 16#140B). -define(GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB, 16#88FE). --define(GL_MAP_READ_BIT, 16#1). --define(GL_MAP_WRITE_BIT, 16#2). --define(GL_MAP_INVALIDATE_RANGE_BIT, 16#4). --define(GL_MAP_INVALIDATE_BUFFER_BIT, 16#8). --define(GL_MAP_FLUSH_EXPLICIT_BIT, 16#10). --define(GL_MAP_UNSYNCHRONIZED_BIT, 16#20). +-define(GL_MAP_READ_BIT, 16#0001). +-define(GL_MAP_WRITE_BIT, 16#0002). +-define(GL_MAP_INVALIDATE_RANGE_BIT, 16#0004). +-define(GL_MAP_INVALIDATE_BUFFER_BIT, 16#0008). +-define(GL_MAP_FLUSH_EXPLICIT_BIT, 16#0010). +-define(GL_MAP_UNSYNCHRONIZED_BIT, 16#0020). -define(GL_TEXTURE_BUFFER_ARB, 16#8C2A). -define(GL_MAX_TEXTURE_BUFFER_SIZE_ARB, 16#8C2B). -define(GL_TEXTURE_BINDING_BUFFER_ARB, 16#8C2C). @@ -1722,7 +1722,7 @@ -define(GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, 16#8A44). -define(GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER, 16#8A45). -define(GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, 16#8A46). --define(GL_INVALID_INDEX, 16#FFFFFFFF). +-define(GL_INVALID_INDEX, 16#FFFFFFFFu). -define(GL_COPY_READ_BUFFER, 16#8F36). -define(GL_COPY_WRITE_BUFFER, 16#8F37). -define(GL_DEPTH_CLAMP, 16#864F). @@ -1744,8 +1744,8 @@ -define(GL_TIMEOUT_EXPIRED, 16#911B). -define(GL_CONDITION_SATISFIED, 16#911C). -define(GL_WAIT_FAILED, 16#911D). --define(GL_SYNC_FLUSH_COMMANDS_BIT, 16#1). --define(GL_TIMEOUT_IGNORED, 16#FFFFFFFFFFFFFFFF). +-define(GL_SYNC_FLUSH_COMMANDS_BIT, 16#00000001). +-define(GL_TIMEOUT_IGNORED, 16#FFFFFFFFFFFFFFFFull). -define(GL_SAMPLE_POSITION, 16#8E50). -define(GL_SAMPLE_MASK, 16#8E51). -define(GL_SAMPLE_MASK_VALUE, 16#8E52). @@ -1828,7 +1828,7 @@ -define(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, 16#8DE8). -define(GL_NUM_COMPATIBLE_SUBROUTINES, 16#8E4A). -define(GL_COMPATIBLE_SUBROUTINES, 16#8E4B). --define(GL_PATCHES, 16#E). +-define(GL_PATCHES, 16#000E). -define(GL_PATCH_VERTICES, 16#8E72). -define(GL_PATCH_DEFAULT_INNER_LEVEL, 16#8E73). -define(GL_PATCH_DEFAULT_OUTER_LEVEL, 16#8E74). @@ -1884,11 +1884,11 @@ -define(GL_PROGRAM_BINARY_LENGTH, 16#8741). -define(GL_NUM_PROGRAM_BINARY_FORMATS, 16#87FE). -define(GL_PROGRAM_BINARY_FORMATS, 16#87FF). --define(GL_VERTEX_SHADER_BIT, 16#1). --define(GL_FRAGMENT_SHADER_BIT, 16#2). --define(GL_GEOMETRY_SHADER_BIT, 16#4). --define(GL_TESS_CONTROL_SHADER_BIT, 16#8). --define(GL_TESS_EVALUATION_SHADER_BIT, 16#10). +-define(GL_VERTEX_SHADER_BIT, 16#00000001). +-define(GL_FRAGMENT_SHADER_BIT, 16#00000002). +-define(GL_GEOMETRY_SHADER_BIT, 16#00000004). +-define(GL_TESS_CONTROL_SHADER_BIT, 16#00000008). +-define(GL_TESS_EVALUATION_SHADER_BIT, 16#00000010). -define(GL_ALL_SHADER_BITS, 16#FFFFFFFF). -define(GL_PROGRAM_SEPARABLE, 16#8258). -define(GL_ACTIVE_PROGRAM, 16#8259). @@ -1923,7 +1923,7 @@ -define(GL_DEBUG_SEVERITY_HIGH_ARB, 16#9146). -define(GL_DEBUG_SEVERITY_MEDIUM_ARB, 16#9147). -define(GL_DEBUG_SEVERITY_LOW_ARB, 16#9148). --define(GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB, 16#4). +-define(GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB, 16#00000004). -define(GL_LOSE_CONTEXT_ON_RESET_ARB, 16#8252). -define(GL_GUILTY_CONTEXT_RESET_ARB, 16#8253). -define(GL_INNOCENT_CONTEXT_RESET_ARB, 16#8254). @@ -1969,18 +1969,18 @@ -define(GL_ACTIVE_ATOMIC_COUNTER_BUFFERS, 16#92D9). -define(GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX, 16#92DA). -define(GL_UNSIGNED_INT_ATOMIC_COUNTER, 16#92DB). --define(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT, 16#1). --define(GL_ELEMENT_ARRAY_BARRIER_BIT, 16#2). --define(GL_UNIFORM_BARRIER_BIT, 16#4). --define(GL_TEXTURE_FETCH_BARRIER_BIT, 16#8). --define(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT, 16#20). --define(GL_COMMAND_BARRIER_BIT, 16#40). --define(GL_PIXEL_BUFFER_BARRIER_BIT, 16#80). --define(GL_TEXTURE_UPDATE_BARRIER_BIT, 16#100). --define(GL_BUFFER_UPDATE_BARRIER_BIT, 16#200). --define(GL_FRAMEBUFFER_BARRIER_BIT, 16#400). --define(GL_TRANSFORM_FEEDBACK_BARRIER_BIT, 16#800). --define(GL_ATOMIC_COUNTER_BARRIER_BIT, 16#1000). +-define(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT, 16#00000001). +-define(GL_ELEMENT_ARRAY_BARRIER_BIT, 16#00000002). +-define(GL_UNIFORM_BARRIER_BIT, 16#00000004). +-define(GL_TEXTURE_FETCH_BARRIER_BIT, 16#00000008). +-define(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT, 16#00000020). +-define(GL_COMMAND_BARRIER_BIT, 16#00000040). +-define(GL_PIXEL_BUFFER_BARRIER_BIT, 16#00000080). +-define(GL_TEXTURE_UPDATE_BARRIER_BIT, 16#00000100). +-define(GL_BUFFER_UPDATE_BARRIER_BIT, 16#00000200). +-define(GL_FRAMEBUFFER_BARRIER_BIT, 16#00000400). +-define(GL_TRANSFORM_FEEDBACK_BARRIER_BIT, 16#00000800). +-define(GL_ATOMIC_COUNTER_BARRIER_BIT, 16#00001000). -define(GL_ALL_BARRIER_BITS, 16#FFFFFFFF). -define(GL_MAX_IMAGE_UNITS, 16#8F38). -define(GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS, 16#8F39). @@ -2337,8 +2337,8 @@ -define(GL_FRAMEZOOM_SGIX, 16#818B). -define(GL_FRAMEZOOM_FACTOR_SGIX, 16#818C). -define(GL_MAX_FRAMEZOOM_FACTOR_SGIX, 16#818D). --define(GL_TEXTURE_DEFORMATION_BIT_SGIX, 16#1). --define(GL_GEOMETRY_DEFORMATION_BIT_SGIX, 16#2). +-define(GL_TEXTURE_DEFORMATION_BIT_SGIX, 16#00000001). +-define(GL_GEOMETRY_DEFORMATION_BIT_SGIX, 16#00000002). -define(GL_GEOMETRY_DEFORMATION_SGIX, 16#8194). -define(GL_TEXTURE_DEFORMATION_SGIX, 16#8195). -define(GL_DEFORMATIONS_MASK_SGIX, 16#8196). @@ -2377,24 +2377,24 @@ -define(GL_VERTEX_CONSISTENT_HINT_PGI, 16#1A22B). -define(GL_MATERIAL_SIDE_HINT_PGI, 16#1A22C). -define(GL_MAX_VERTEX_HINT_PGI, 16#1A22D). --define(GL_COLOR3_BIT_PGI, 16#10000). --define(GL_COLOR4_BIT_PGI, 16#20000). --define(GL_EDGEFLAG_BIT_PGI, 16#40000). --define(GL_INDEX_BIT_PGI, 16#80000). --define(GL_MAT_AMBIENT_BIT_PGI, 16#100000). --define(GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI, 16#200000). --define(GL_MAT_DIFFUSE_BIT_PGI, 16#400000). --define(GL_MAT_EMISSION_BIT_PGI, 16#800000). --define(GL_MAT_COLOR_INDEXES_BIT_PGI, 16#1000000). --define(GL_MAT_SHININESS_BIT_PGI, 16#2000000). --define(GL_MAT_SPECULAR_BIT_PGI, 16#4000000). --define(GL_NORMAL_BIT_PGI, 16#8000000). +-define(GL_COLOR3_BIT_PGI, 16#00010000). +-define(GL_COLOR4_BIT_PGI, 16#00020000). +-define(GL_EDGEFLAG_BIT_PGI, 16#00040000). +-define(GL_INDEX_BIT_PGI, 16#00080000). +-define(GL_MAT_AMBIENT_BIT_PGI, 16#00100000). +-define(GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI, 16#00200000). +-define(GL_MAT_DIFFUSE_BIT_PGI, 16#00400000). +-define(GL_MAT_EMISSION_BIT_PGI, 16#00800000). +-define(GL_MAT_COLOR_INDEXES_BIT_PGI, 16#01000000). +-define(GL_MAT_SHININESS_BIT_PGI, 16#02000000). +-define(GL_MAT_SPECULAR_BIT_PGI, 16#04000000). +-define(GL_NORMAL_BIT_PGI, 16#08000000). -define(GL_TEXCOORD1_BIT_PGI, 16#10000000). -define(GL_TEXCOORD2_BIT_PGI, 16#20000000). -define(GL_TEXCOORD3_BIT_PGI, 16#40000000). -define(GL_TEXCOORD4_BIT_PGI, 16#80000000). --define(GL_VERTEX23_BIT_PGI, 16#4). --define(GL_VERTEX4_BIT_PGI, 16#8). +-define(GL_VERTEX23_BIT_PGI, 16#00000004). +-define(GL_VERTEX4_BIT_PGI, 16#00000008). -define(GL_PREFER_DOUBLEBUFFER_HINT_PGI, 16#1A1F8). -define(GL_CONSERVE_MEMORY_HINT_PGI, 16#1A1FD). -define(GL_RECLAIM_MEMORY_HINT_PGI, 16#1A1FE). @@ -2591,9 +2591,9 @@ -define(GL_TEXTURE_CONSTANT_DATA_SUNX, 16#81D6). -define(GL_GLOBAL_ALPHA_SUN, 16#81D9). -define(GL_GLOBAL_ALPHA_FACTOR_SUN, 16#81DA). --define(GL_RESTART_SUN, 16#1). --define(GL_REPLACE_MIDDLE_SUN, 16#2). --define(GL_REPLACE_OLDEST_SUN, 16#3). +-define(GL_RESTART_SUN, 16#0001). +-define(GL_REPLACE_MIDDLE_SUN, 16#0002). +-define(GL_REPLACE_OLDEST_SUN, 16#0003). -define(GL_TRIANGLE_LIST_SUN, 16#81D7). -define(GL_REPLACEMENT_CODE_SUN, 16#81D8). -define(GL_REPLACEMENT_CODE_ARRAY_SUN, 16#85C0). @@ -3112,19 +3112,19 @@ -define(GL_SWIZZLE_STQ_DQ_ATI, 16#8979). -define(GL_SWIZZLE_STRQ_ATI, 16#897A). -define(GL_SWIZZLE_STRQ_DQ_ATI, 16#897B). --define(GL_RED_BIT_ATI, 16#1). --define(GL_GREEN_BIT_ATI, 16#2). --define(GL_BLUE_BIT_ATI, 16#4). --define(GL_2X_BIT_ATI, 16#1). --define(GL_4X_BIT_ATI, 16#2). --define(GL_8X_BIT_ATI, 16#4). --define(GL_HALF_BIT_ATI, 16#8). --define(GL_QUARTER_BIT_ATI, 16#10). --define(GL_EIGHTH_BIT_ATI, 16#20). --define(GL_SATURATE_BIT_ATI, 16#40). --define(GL_COMP_BIT_ATI, 16#2). --define(GL_NEGATE_BIT_ATI, 16#4). --define(GL_BIAS_BIT_ATI, 16#8). +-define(GL_RED_BIT_ATI, 16#00000001). +-define(GL_GREEN_BIT_ATI, 16#00000002). +-define(GL_BLUE_BIT_ATI, 16#00000004). +-define(GL_2X_BIT_ATI, 16#00000001). +-define(GL_4X_BIT_ATI, 16#00000002). +-define(GL_8X_BIT_ATI, 16#00000004). +-define(GL_HALF_BIT_ATI, 16#00000008). +-define(GL_QUARTER_BIT_ATI, 16#00000010). +-define(GL_EIGHTH_BIT_ATI, 16#00000020). +-define(GL_SATURATE_BIT_ATI, 16#00000040). +-define(GL_COMP_BIT_ATI, 16#00000002). +-define(GL_NEGATE_BIT_ATI, 16#00000004). +-define(GL_BIAS_BIT_ATI, 16#00000008). -define(GL_PN_TRIANGLES_ATI, 16#87F0). -define(GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI, 16#87F1). -define(GL_PN_TRIANGLES_POINT_MODE_ATI, 16#87F2). @@ -3409,7 +3409,7 @@ -define(GL_MAX_PROGRAM_IF_DEPTH_NV, 16#88F6). -define(GL_MAX_PROGRAM_LOOP_DEPTH_NV, 16#88F7). -define(GL_MAX_PROGRAM_LOOP_COUNT_NV, 16#88F8). --define(GL_INVALID_FRAMEBUFFER_OPERATION_EXT, 16#506). +-define(GL_INVALID_FRAMEBUFFER_OPERATION_EXT, 16#0506). -define(GL_MAX_RENDERBUFFER_SIZE_EXT, 16#84E8). -define(GL_FRAMEBUFFER_BINDING_EXT, 16#8CA6). -define(GL_RENDERBUFFER_BINDING_EXT, 16#8CA7). @@ -3506,10 +3506,10 @@ -define(GL_MAX_PROGRAM_RESULT_COMPONENTS_NV, 16#8909). -define(GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV, 16#8DA5). -define(GL_MAX_PROGRAM_GENERIC_RESULTS_NV, 16#8DA6). --define(GL_LINES_ADJACENCY_EXT, 16#A). --define(GL_LINE_STRIP_ADJACENCY_EXT, 16#B). --define(GL_TRIANGLES_ADJACENCY_EXT, 16#C). --define(GL_TRIANGLE_STRIP_ADJACENCY_EXT, 16#D). +-define(GL_LINES_ADJACENCY_EXT, 16#000A). +-define(GL_LINE_STRIP_ADJACENCY_EXT, 16#000B). +-define(GL_TRIANGLES_ADJACENCY_EXT, 16#000C). +-define(GL_TRIANGLE_STRIP_ADJACENCY_EXT, 16#000D). -define(GL_GEOMETRY_PROGRAM_NV, 16#8C26). -define(GL_MAX_PROGRAM_OUTPUT_VERTICES_NV, 16#8C27). -define(GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV, 16#8C28). @@ -3891,18 +3891,18 @@ -define(GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT, 16#906C). -define(GL_MAX_IMAGE_SAMPLES_EXT, 16#906D). -define(GL_IMAGE_BINDING_FORMAT_EXT, 16#906E). --define(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT, 16#1). --define(GL_ELEMENT_ARRAY_BARRIER_BIT_EXT, 16#2). --define(GL_UNIFORM_BARRIER_BIT_EXT, 16#4). --define(GL_TEXTURE_FETCH_BARRIER_BIT_EXT, 16#8). --define(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT, 16#20). --define(GL_COMMAND_BARRIER_BIT_EXT, 16#40). --define(GL_PIXEL_BUFFER_BARRIER_BIT_EXT, 16#80). --define(GL_TEXTURE_UPDATE_BARRIER_BIT_EXT, 16#100). --define(GL_BUFFER_UPDATE_BARRIER_BIT_EXT, 16#200). --define(GL_FRAMEBUFFER_BARRIER_BIT_EXT, 16#400). --define(GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT, 16#800). --define(GL_ATOMIC_COUNTER_BARRIER_BIT_EXT, 16#1000). +-define(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT, 16#00000001). +-define(GL_ELEMENT_ARRAY_BARRIER_BIT_EXT, 16#00000002). +-define(GL_UNIFORM_BARRIER_BIT_EXT, 16#00000004). +-define(GL_TEXTURE_FETCH_BARRIER_BIT_EXT, 16#00000008). +-define(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT, 16#00000020). +-define(GL_COMMAND_BARRIER_BIT_EXT, 16#00000040). +-define(GL_PIXEL_BUFFER_BARRIER_BIT_EXT, 16#00000080). +-define(GL_TEXTURE_UPDATE_BARRIER_BIT_EXT, 16#00000100). +-define(GL_BUFFER_UPDATE_BARRIER_BIT_EXT, 16#00000200). +-define(GL_FRAMEBUFFER_BARRIER_BIT_EXT, 16#00000400). +-define(GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT, 16#00000800). +-define(GL_ATOMIC_COUNTER_BARRIER_BIT_EXT, 16#00001000). -define(GL_ALL_BARRIER_BITS_EXT, 16#FFFFFFFF). -define(GL_DOUBLE_VEC2_EXT, 16#8FFC). -define(GL_DOUBLE_VEC3_EXT, 16#8FFD). @@ -3952,7 +3952,7 @@ -define(GL_FLOAT16_VEC2_NV, 16#8FF9). -define(GL_FLOAT16_VEC3_NV, 16#8FFA). -define(GL_FLOAT16_VEC4_NV, 16#8FFB). --define(GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV, 16#10). +-define(GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV, 16#00000010). -define(GL_MAX_PROGRAM_PATCH_ATTRIBS_NV, 16#86D8). -define(GL_TESS_CONTROL_PROGRAM_NV, 16#891E). -define(GL_TESS_EVALUATION_PROGRAM_NV, 16#891F). diff --git a/lib/wx/include/glu.hrl b/lib/wx/include/glu.hrl index a2ab55d054..dc55c5bb96 100644 --- a/lib/wx/include/glu.hrl +++ b/lib/wx/include/glu.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. 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 @@ -174,4 +174,4 @@ -define(GLU_TESS_WINDING_POSITIVE, 100132). -define(GLU_TESS_WINDING_NEGATIVE, 100133). -define(GLU_TESS_WINDING_ABS_GEQ_TWO, 100134). --define(GLU_TESS_MAX_COORD, ?1.0e150). +-define(GLU_TESS_MAX_COORD, 1.0e150). diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl index fba99a1f07..62979908a6 100644 --- a/lib/wx/include/wx.hrl +++ b/lib/wx/include/wx.hrl @@ -316,12 +316,13 @@ -type wxEventType() :: wxAuiManagerEventType() | wxAuiNotebookEventType() | wxCalendarEventType() | wxChildFocusEventType() | wxCloseEventType() | wxColourPickerEventType() | wxCommandEventType() | wxContextMenuEventType() | wxDateEventType() | wxDisplayChangedEventType() | wxEraseEventType() | wxFileDirPickerEventType() | wxFocusEventType() | wxFontPickerEventType() | wxGridEventType() | wxHelpEventType() | wxHtmlLinkEventType() | wxIconizeEventType() | wxIdleEventType() | wxJoystickEventType() | wxKeyEventType() | wxListEventType() | wxMaximizeEventType() | wxMenuEventType() | wxMouseCaptureChangedEventType() | wxMouseEventType() | wxMoveEventType() | wxNavigationKeyEventType() | wxNcPaintEventType() | wxNotebookEventType() | wxPaintEventType() | wxPaletteChangedEventType() | wxQueryNewPaletteEventType() | wxSashEventType() | wxScrollEventType() | wxScrollWinEventType() | wxSetCursorEventType() | wxShowEventType() | wxSizeEventType() | wxSpinEventType() | wxSplitterEventType() | wxStyledTextEventType() | wxSysColourChangedEventType() | wxTaskBarIconEventType() | wxTreeEventType() | wxUpdateUIEventType() | wxWindowCreateEventType() | wxWindowDestroyEventType(). %% Hardcoded Records --record(wxMouseState, {x, y, %% integer() - leftDown, middleDown, rightDown, %% bool() - controlDown, shiftDown, altDown, metaDown, cmdDown %% bool() +-record(wxMouseState, {x :: integer(), y :: integer(), + leftDown :: boolean(), middleDown :: boolean, rightDown :: boolean, + controlDown :: boolean(), shiftDown :: boolean(), + altDown :: boolean(), metaDown :: boolean(), cmdDown :: boolean() }). -record(wxHtmlLinkInfo, { - href, target %% unicode:chardata() + href :: unicode:chardata(), target :: unicode:chardata() }). %% Hardcoded Defines @@ -984,7 +985,7 @@ -define(wxRESET, 16384). -define(wxBACKWARD, 8192). -define(wxFORWARD, 4096). --define(wxICON_MASK, (?16#00000100 bor ?16#00000200 bor ?16#00000400 bor ?16#00000800)). +-define(wxICON_MASK, (16#00000100 bor 16#00000200 bor 16#00000400 bor 16#00000800)). -define(wxICON_ASTERISK, ?wxICON_INFORMATION). -define(wxICON_STOP, ?wxICON_HAND). -define(wxICON_INFORMATION, 2048). diff --git a/lib/wx/src/gen/wxAuiManager.erl b/lib/wx/src/gen/wxAuiManager.erl index 95dd5d64a5..71e851f706 100644 --- a/lib/wx/src/gen/wxAuiManager.erl +++ b/lib/wx/src/gen/wxAuiManager.erl @@ -119,7 +119,7 @@ detachPane(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef}) <<ThisRef:32/?UI,WindowRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxauimanager.html#wxauimanagergetallpanes">external documentation</a>. --spec getAllPanes(This) -> wxAuiPaneInfoArray:wxAuiPaneInfoArray() when +-spec getAllPanes(This) -> wx:wx_object() when This::wxAuiManager(). getAllPanes(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxAuiManager), diff --git a/lib/wx/src/gen/wxBitmapButton.erl b/lib/wx/src/gen/wxBitmapButton.erl index 79e51da801..ddddbbc1dd 100644 --- a/lib/wx/src/gen/wxBitmapButton.erl +++ b/lib/wx/src/gen/wxBitmapButton.erl @@ -101,7 +101,7 @@ new(Parent,Id,Bitmap) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,#wx_ref{type=BitmapT,ref=BitmapRef}, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -129,7 +129,7 @@ create(This,Parent,Id,Bitmap) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,#wx_ref{type=BitmapT,ref=BitmapRef}, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxBitmapButton), diff --git a/lib/wx/src/gen/wxButton.erl b/lib/wx/src/gen/wxButton.erl index ae78f53e6f..a27e5d91c2 100644 --- a/lib/wx/src/gen/wxButton.erl +++ b/lib/wx/src/gen/wxButton.erl @@ -99,7 +99,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -128,7 +128,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxButton), diff --git a/lib/wx/src/gen/wxCheckBox.erl b/lib/wx/src/gen/wxCheckBox.erl index 37bca77cae..6e30f14207 100644 --- a/lib/wx/src/gen/wxCheckBox.erl +++ b/lib/wx/src/gen/wxCheckBox.erl @@ -99,7 +99,7 @@ new(Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -127,7 +127,7 @@ create(This,Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxCheckBox), diff --git a/lib/wx/src/gen/wxCheckListBox.erl b/lib/wx/src/gen/wxCheckListBox.erl index 71199fed3b..083a9e32f4 100644 --- a/lib/wx/src/gen/wxCheckListBox.erl +++ b/lib/wx/src/gen/wxCheckListBox.erl @@ -106,7 +106,7 @@ new(Parent,Id) | {size, {W::integer(), H::integer()}} | {choices, [[unicode:chardata()]]} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), diff --git a/lib/wx/src/gen/wxChoice.erl b/lib/wx/src/gen/wxChoice.erl index b9cac3926c..2a2b2688fe 100644 --- a/lib/wx/src/gen/wxChoice.erl +++ b/lib/wx/src/gen/wxChoice.erl @@ -104,7 +104,7 @@ new(Parent,Id) | {size, {W::integer(), H::integer()}} | {choices, [[unicode:chardata()]]} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -130,7 +130,7 @@ create(This,Parent,Id,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) -spec create(This, Parent, Id, Pos, Size, Choices, [Option]) -> boolean() when This::wxChoice(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]], Option :: {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,{PosX,PosY},{SizeW,SizeH},Choices, Options) when is_integer(Id),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) -> ?CLASS(ThisT,wxChoice), diff --git a/lib/wx/src/gen/wxColourPickerCtrl.erl b/lib/wx/src/gen/wxColourPickerCtrl.erl index 5fd51eba15..1ba771695f 100644 --- a/lib/wx/src/gen/wxColourPickerCtrl.erl +++ b/lib/wx/src/gen/wxColourPickerCtrl.erl @@ -104,7 +104,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -133,7 +133,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxColourPickerCtrl), diff --git a/lib/wx/src/gen/wxComboBox.erl b/lib/wx/src/gen/wxComboBox.erl index b9224d18b9..c67ff82bbc 100644 --- a/lib/wx/src/gen/wxComboBox.erl +++ b/lib/wx/src/gen/wxComboBox.erl @@ -107,7 +107,7 @@ new(Parent,Id) | {size, {W::integer(), H::integer()}} | {choices, [[unicode:chardata()]]} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -134,7 +134,7 @@ create(This,Parent,Id,Value,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) -spec create(This, Parent, Id, Value, Pos, Size, Choices, [Option]) -> boolean() when This::wxComboBox(), Parent::wxWindow:wxWindow(), Id::integer(), Value::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]], Option :: {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Value,{PosX,PosY},{SizeW,SizeH},Choices, Options) when is_integer(Id),is_list(Value),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) -> ?CLASS(ThisT,wxComboBox), diff --git a/lib/wx/src/gen/wxDatePickerCtrl.erl b/lib/wx/src/gen/wxDatePickerCtrl.erl index ad66d1cf03..6ffc2ca3f5 100644 --- a/lib/wx/src/gen/wxDatePickerCtrl.erl +++ b/lib/wx/src/gen/wxDatePickerCtrl.erl @@ -104,7 +104,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), diff --git a/lib/wx/src/gen/wxDirPickerCtrl.erl b/lib/wx/src/gen/wxDirPickerCtrl.erl index 5bc79ca7a1..bbc169ae03 100644 --- a/lib/wx/src/gen/wxDirPickerCtrl.erl +++ b/lib/wx/src/gen/wxDirPickerCtrl.erl @@ -105,7 +105,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -136,7 +136,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxDirPickerCtrl), diff --git a/lib/wx/src/gen/wxFilePickerCtrl.erl b/lib/wx/src/gen/wxFilePickerCtrl.erl index f13c20f6c7..d19c8c00cb 100644 --- a/lib/wx/src/gen/wxFilePickerCtrl.erl +++ b/lib/wx/src/gen/wxFilePickerCtrl.erl @@ -106,7 +106,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -139,7 +139,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxFilePickerCtrl), diff --git a/lib/wx/src/gen/wxFontPickerCtrl.erl b/lib/wx/src/gen/wxFontPickerCtrl.erl index 2659d4e2e3..46c0dbab4d 100644 --- a/lib/wx/src/gen/wxFontPickerCtrl.erl +++ b/lib/wx/src/gen/wxFontPickerCtrl.erl @@ -105,7 +105,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -134,7 +134,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxFontPickerCtrl), diff --git a/lib/wx/src/gen/wxGLCanvas.erl b/lib/wx/src/gen/wxGLCanvas.erl index 3a9cb39020..46168374af 100644 --- a/lib/wx/src/gen/wxGLCanvas.erl +++ b/lib/wx/src/gen/wxGLCanvas.erl @@ -96,7 +96,7 @@ new(Parent) %% | {palette, wxPalette:wxPalette()}.<br /> %% -spec new(Parent, Shared) -> wxGLCanvas() when - Parent::wxWindow:wxWindow(), Shared::wxGLContext:wxGLContext() | wxGLCanvas(); + Parent::wxWindow:wxWindow(), Shared::wx:wx_object() | wxGLCanvas(); (Parent, [Option]) -> wxGLCanvas() when Parent::wxWindow:wxWindow(), Option :: {id, integer()} @@ -128,7 +128,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef}, Options) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxglcanvas.html#wxglcanvaswxglcanvas">external documentation</a>. -spec new(Parent, Shared, [Option]) -> wxGLCanvas() when - Parent::wxWindow:wxWindow(), Shared::wxGLContext:wxGLContext() | wxGLCanvas(), + Parent::wxWindow:wxWindow(), Shared::wx:wx_object() | wxGLCanvas(), Option :: {id, integer()} | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} @@ -159,7 +159,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},#wx_ref{type=SharedT,ref=SharedRef}, Opt <<ParentRef:32/?UI,SharedRef:32/?UI, BinOpt/binary>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxglcanvas.html#wxglcanvasgetcontext">external documentation</a>. --spec getContext(This) -> wxGLContext:wxGLContext() when +-spec getContext(This) -> wx:wx_object() when This::wxGLCanvas(). getContext(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxGLCanvas), diff --git a/lib/wx/src/gen/wxGauge.erl b/lib/wx/src/gen/wxGauge.erl index c35a2574f6..7f892355c7 100644 --- a/lib/wx/src/gen/wxGauge.erl +++ b/lib/wx/src/gen/wxGauge.erl @@ -99,7 +99,7 @@ new(Parent,Id,Range) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Range, Options) when is_integer(Id),is_integer(Range),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -126,7 +126,7 @@ create(This,Parent,Id,Range) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Range, Options) when is_integer(Id),is_integer(Range),is_list(Options) -> ?CLASS(ThisT,wxGauge), diff --git a/lib/wx/src/gen/wxGridBagSizer.erl b/lib/wx/src/gen/wxGridBagSizer.erl index 6a978efc85..e8a9ff6d76 100644 --- a/lib/wx/src/gen/wxGridBagSizer.erl +++ b/lib/wx/src/gen/wxGridBagSizer.erl @@ -98,7 +98,7 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ItemT,ref=ItemRef}) -> %% Option :: {proportion, integer()}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()}.<br /> +%% | {userData, wx:wx_object()}.<br /> %% -spec add(This, Width, Height) -> wxSizerItem:wxSizerItem() when This::wxGridBagSizer(), Width::integer(), Height::integer(); @@ -109,7 +109,7 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ItemT,ref=ItemRef}) -> Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. add(This,Width,Height) when is_record(This, wx_ref),is_integer(Width),is_integer(Height) -> @@ -143,13 +143,13 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef}, Options %% Option :: {proportion, integer()}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()};<br /> +%% | {userData, wx:wx_object()};<br /> %% (This, Window, Pos, [Option]) -> wxSizerItem:wxSizerItem() when<br /> %% This::wxGridBagSizer(), Window::wxWindow:wxWindow() | wxSizer:wxSizer(), Pos::{R::integer(), C::integer()},<br /> %% Option :: {span, {RS::integer(), CS::integer()}}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()}.<br /> +%% | {userData, wx:wx_object()}.<br /> %% -spec add(This, Width, Height, Pos) -> wxSizerItem:wxSizerItem() when This::wxGridBagSizer(), Width::integer(), Height::integer(), Pos::{R::integer(), C::integer()}; @@ -158,13 +158,13 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef}, Options Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}; + | {userData, wx:wx_object()}; (This, Window, Pos, [Option]) -> wxSizerItem:wxSizerItem() when This::wxGridBagSizer(), Window::wxWindow:wxWindow() | wxSizer:wxSizer(), Pos::{R::integer(), C::integer()}, Option :: {span, {RS::integer(), CS::integer()}} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. add(This,Width,Height,Pos={PosR,PosC}) when is_record(This, wx_ref),is_integer(Width),is_integer(Height),is_integer(PosR),is_integer(PosC) -> @@ -204,7 +204,7 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef},{PosR,Po Option :: {span, {RS::integer(), CS::integer()}} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. add(#wx_ref{type=ThisT,ref=ThisRef},Width,Height,{PosR,PosC}, Options) when is_integer(Width),is_integer(Height),is_integer(PosR),is_integer(PosC),is_list(Options) -> ?CLASS(ThisT,wxGridBagSizer), @@ -305,7 +305,7 @@ findItemAtPosition(#wx_ref{type=ThisT,ref=ThisRef},{PosR,PosC}) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxgridbagsizer.html#wxgridbagsizerfinditemwithdata">external documentation</a>. -spec findItemWithData(This, UserData) -> wxGBSizerItem:wxGBSizerItem() when - This::wxGridBagSizer(), UserData::wx:wx(). + This::wxGridBagSizer(), UserData::wx:wx_object(). findItemWithData(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=UserDataT,ref=UserDataRef}) -> ?CLASS(ThisT,wxGridBagSizer), ?CLASS(UserDataT,wx), diff --git a/lib/wx/src/gen/wxIcon.erl b/lib/wx/src/gen/wxIcon.erl index c726f2ed4c..0f31278732 100644 --- a/lib/wx/src/gen/wxIcon.erl +++ b/lib/wx/src/gen/wxIcon.erl @@ -50,13 +50,13 @@ new() -> %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxicon.html#wxiconwxicon">external documentation</a>. %% <br /> Also:<br /> %% new(Loc) -> wxIcon() when<br /> -%% Loc::wx:wx().<br /> +%% Loc::wx:wx_object().<br /> %% %%<br /> Type = ?wxBITMAP_TYPE_INVALID | ?wxBITMAP_TYPE_BMP | ?wxBITMAP_TYPE_BMP_RESOURCE | ?wxBITMAP_TYPE_RESOURCE | ?wxBITMAP_TYPE_ICO | ?wxBITMAP_TYPE_ICO_RESOURCE | ?wxBITMAP_TYPE_CUR | ?wxBITMAP_TYPE_CUR_RESOURCE | ?wxBITMAP_TYPE_XBM | ?wxBITMAP_TYPE_XBM_DATA | ?wxBITMAP_TYPE_XPM | ?wxBITMAP_TYPE_XPM_DATA | ?wxBITMAP_TYPE_TIF | ?wxBITMAP_TYPE_TIF_RESOURCE | ?wxBITMAP_TYPE_GIF | ?wxBITMAP_TYPE_GIF_RESOURCE | ?wxBITMAP_TYPE_PNG | ?wxBITMAP_TYPE_PNG_RESOURCE | ?wxBITMAP_TYPE_JPEG | ?wxBITMAP_TYPE_JPEG_RESOURCE | ?wxBITMAP_TYPE_PNM | ?wxBITMAP_TYPE_PNM_RESOURCE | ?wxBITMAP_TYPE_PCX | ?wxBITMAP_TYPE_PCX_RESOURCE | ?wxBITMAP_TYPE_PICT | ?wxBITMAP_TYPE_PICT_RESOURCE | ?wxBITMAP_TYPE_ICON | ?wxBITMAP_TYPE_ICON_RESOURCE | ?wxBITMAP_TYPE_ANI | ?wxBITMAP_TYPE_IFF | ?wxBITMAP_TYPE_TGA | ?wxBITMAP_TYPE_MACCURSOR | ?wxBITMAP_TYPE_MACCURSOR_RESOURCE | ?wxBITMAP_TYPE_ANY -spec new(Filename) -> wxIcon() when Filename::unicode:chardata(); (Loc) -> wxIcon() when - Loc::wx:wx(). + Loc::wx:wx_object(). new(Filename) when is_list(Filename) -> diff --git a/lib/wx/src/gen/wxListBox.erl b/lib/wx/src/gen/wxListBox.erl index bcaf5fad3b..3b41de9ffc 100644 --- a/lib/wx/src/gen/wxListBox.erl +++ b/lib/wx/src/gen/wxListBox.erl @@ -104,7 +104,7 @@ new(Parent,Id) | {size, {W::integer(), H::integer()}} | {choices, [[unicode:chardata()]]} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -130,7 +130,7 @@ create(This,Parent,Id,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) -spec create(This, Parent, Id, Pos, Size, Choices, [Option]) -> boolean() when This::wxListBox(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]], Option :: {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,{PosX,PosY},{SizeW,SizeH},Choices, Options) when is_integer(Id),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) -> ?CLASS(ThisT,wxListBox), diff --git a/lib/wx/src/gen/wxListCtrl.erl b/lib/wx/src/gen/wxListCtrl.erl index 16e8ae4508..a6288fc02a 100644 --- a/lib/wx/src/gen/wxListCtrl.erl +++ b/lib/wx/src/gen/wxListCtrl.erl @@ -34,10 +34,10 @@ deleteItem/2,destroy/1,editLabel/2,ensureVisible/2,findItem/3,findItem/4, getColumn/3,getColumnCount/1,getColumnWidth/2,getCountPerPage/1,getEditControl/1, getImageList/2,getItem/2,getItemBackgroundColour/2,getItemCount/1, - getItemData/2,getItemFont/2,getItemPosition/3,getItemRect/3,getItemRect/4, + getItemData/2,getItemFont/2,getItemPosition/2,getItemRect/2,getItemRect/3, getItemSpacing/1,getItemState/3,getItemText/2,getItemTextColour/2, getNextItem/2,getNextItem/3,getSelectedItemCount/1,getTextColour/1, - getTopItem/1,getViewRect/1,hitTest/2,insertColumn/3,insertColumn/4, + getTopItem/1,getViewRect/1,hitTest/3,insertColumn/3,insertColumn/4, insertItem/2,insertItem/3,insertItem/4,refreshItem/2,refreshItems/3, scrollList/3,setBackgroundColour/2,setColumn/3,setColumnWidth/3,setImageList/3, setItem/2,setItem/4,setItem/5,setItemBackgroundColour/3,setItemColumnImage/4, @@ -391,34 +391,37 @@ getItemFont(#wx_ref{type=ThisT,ref=ThisRef},Item) <<ThisRef:32/?UI,Item:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlgetitemposition">external documentation</a>. --spec getItemPosition(This, Item, Pos) -> boolean() when - This::wxListCtrl(), Item::integer(), Pos::{X::integer(), Y::integer()}. -getItemPosition(#wx_ref{type=ThisT,ref=ThisRef},Item,{PosX,PosY}) - when is_integer(Item),is_integer(PosX),is_integer(PosY) -> +-spec getItemPosition(This, Item) -> Result when + Result ::{Res ::boolean(), Pos::{X::integer(), Y::integer()}}, + This::wxListCtrl(), Item::integer(). +getItemPosition(#wx_ref{type=ThisT,ref=ThisRef},Item) + when is_integer(Item) -> ?CLASS(ThisT,wxListCtrl), wxe_util:call(?wxListCtrl_GetItemPosition, - <<ThisRef:32/?UI,Item:32/?UI,PosX:32/?UI,PosY:32/?UI>>). + <<ThisRef:32/?UI,Item:32/?UI>>). -%% @equiv getItemRect(This,Item,Rect, []) --spec getItemRect(This, Item, Rect) -> boolean() when - This::wxListCtrl(), Item::integer(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}. +%% @equiv getItemRect(This,Item, []) +-spec getItemRect(This, Item) -> Result when + Result ::{Res ::boolean(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}}, + This::wxListCtrl(), Item::integer(). -getItemRect(This,Item,Rect={RectX,RectY,RectW,RectH}) - when is_record(This, wx_ref),is_integer(Item),is_integer(RectX),is_integer(RectY),is_integer(RectW),is_integer(RectH) -> - getItemRect(This,Item,Rect, []). +getItemRect(This,Item) + when is_record(This, wx_ref),is_integer(Item) -> + getItemRect(This,Item, []). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlgetitemrect">external documentation</a>. --spec getItemRect(This, Item, Rect, [Option]) -> boolean() when - This::wxListCtrl(), Item::integer(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}, +-spec getItemRect(This, Item, [Option]) -> Result when + Result :: {Res ::boolean(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}}, + This::wxListCtrl(), Item::integer(), Option :: {code, integer()}. -getItemRect(#wx_ref{type=ThisT,ref=ThisRef},Item,{RectX,RectY,RectW,RectH}, Options) - when is_integer(Item),is_integer(RectX),is_integer(RectY),is_integer(RectW),is_integer(RectH),is_list(Options) -> +getItemRect(#wx_ref{type=ThisT,ref=ThisRef},Item, Options) + when is_integer(Item),is_list(Options) -> ?CLASS(ThisT,wxListCtrl), MOpts = fun({code, Code}, Acc) -> [<<1:32/?UI,Code:32/?UI>>|Acc]; (BadOpt, _) -> erlang:error({badoption, BadOpt}) end, BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)), wxe_util:call(?wxListCtrl_GetItemRect, - <<ThisRef:32/?UI,Item:32/?UI,RectX:32/?UI,RectY:32/?UI,RectW:32/?UI,RectH:32/?UI, BinOpt/binary>>). + <<ThisRef:32/?UI,Item:32/?UI, BinOpt/binary>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlgetitemspacing">external documentation</a>. -spec getItemSpacing(This) -> {W::integer(), H::integer()} when @@ -511,14 +514,13 @@ getViewRect(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlhittest">external documentation</a>. --spec hitTest(This, Point) -> Result when - Result ::{Res ::integer(), Flags::integer()}, - This::wxListCtrl(), Point::{X::integer(), Y::integer()}. -hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PointX,PointY}) - when is_integer(PointX),is_integer(PointY) -> +-spec hitTest(This, Point, Flags) -> integer() when + This::wxListCtrl(), Point::{X::integer(), Y::integer()}, Flags::integer(). +hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PointX,PointY},Flags) + when is_integer(PointX),is_integer(PointY),is_integer(Flags) -> ?CLASS(ThisT,wxListCtrl), wxe_util:call(?wxListCtrl_HitTest, - <<ThisRef:32/?UI,PointX:32/?UI,PointY:32/?UI>>). + <<ThisRef:32/?UI,PointX:32/?UI,PointY:32/?UI,Flags:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlinsertcolumn">external documentation</a>. %% <br /> Also:<br /> diff --git a/lib/wx/src/gen/wxRadioBox.erl b/lib/wx/src/gen/wxRadioBox.erl index 1b3717de23..7843fde488 100644 --- a/lib/wx/src/gen/wxRadioBox.erl +++ b/lib/wx/src/gen/wxRadioBox.erl @@ -93,7 +93,7 @@ new(Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]], Option :: {majorDim, integer()} | {style, integer()} - | {val, wx:wx()}. + | {val, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title,{PosX,PosY},{SizeW,SizeH},Choices, Options) when is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -121,7 +121,7 @@ create(This,Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) This::wxRadioBox(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]], Option :: {majorDim, integer()} | {style, integer()} - | {val, wx:wx()}. + | {val, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Title,{PosX,PosY},{SizeW,SizeH},Choices, Options) when is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) -> ?CLASS(ThisT,wxRadioBox), diff --git a/lib/wx/src/gen/wxRadioButton.erl b/lib/wx/src/gen/wxRadioButton.erl index a013bd65e4..beb052b873 100644 --- a/lib/wx/src/gen/wxRadioButton.erl +++ b/lib/wx/src/gen/wxRadioButton.erl @@ -97,7 +97,7 @@ new(Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -125,7 +125,7 @@ create(This,Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxRadioButton), diff --git a/lib/wx/src/gen/wxScrollBar.erl b/lib/wx/src/gen/wxScrollBar.erl index 30272312ab..4370bd1635 100644 --- a/lib/wx/src/gen/wxScrollBar.erl +++ b/lib/wx/src/gen/wxScrollBar.erl @@ -98,7 +98,7 @@ new(Parent,Id) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -125,7 +125,7 @@ create(This,Parent,Id) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxScrollBar), diff --git a/lib/wx/src/gen/wxSizer.erl b/lib/wx/src/gen/wxSizer.erl index 104e1bcdc9..7edc01aa2a 100644 --- a/lib/wx/src/gen/wxSizer.erl +++ b/lib/wx/src/gen/wxSizer.erl @@ -56,7 +56,7 @@ add(This,Window) %% Option :: {proportion, integer()}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()};<br /> +%% | {userData, wx:wx_object()};<br /> %% (This, Window, Flags) -> wxSizerItem:wxSizerItem() when<br /> %% This::wxSizer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags().<br /> %% @@ -67,7 +67,7 @@ add(This,Window) Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}; + | {userData, wx:wx_object()}; (This, Window, Flags) -> wxSizerItem:wxSizerItem() when This::wxSizer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags(). @@ -110,7 +110,7 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef},#wx_ref{ Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. add(#wx_ref{type=ThisT,ref=ThisRef},Width,Height, Options) when is_integer(Width),is_integer(Height),is_list(Options) -> ?CLASS(ThisT,wxSizer), @@ -350,7 +350,7 @@ insert(#wx_ref{type=ThisT,ref=ThisRef},Index,#wx_ref{type=ItemT,ref=ItemRef}) %% Option :: {proportion, integer()}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()};<br /> +%% | {userData, wx:wx_object()};<br /> %% (This, Index, Window, Flags) -> wxSizerItem:wxSizerItem() when<br /> %% This::wxSizer(), Index::integer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags().<br /> %% @@ -361,7 +361,7 @@ insert(#wx_ref{type=ThisT,ref=ThisRef},Index,#wx_ref{type=ItemT,ref=ItemRef}) Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}; + | {userData, wx:wx_object()}; (This, Index, Window, Flags) -> wxSizerItem:wxSizerItem() when This::wxSizer(), Index::integer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags(). @@ -405,7 +405,7 @@ insert(#wx_ref{type=ThisT,ref=ThisRef},Index,#wx_ref{type=WindowT,ref=WindowRef} Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. insert(#wx_ref{type=ThisT,ref=ThisRef},Index,Width,Height, Options) when is_integer(Index),is_integer(Width),is_integer(Height),is_list(Options) -> ?CLASS(ThisT,wxSizer), @@ -497,7 +497,7 @@ prepend(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ItemT,ref=ItemRef}) -> %% Option :: {proportion, integer()}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()};<br /> +%% | {userData, wx:wx_object()};<br /> %% (This, Window, Flags) -> wxSizerItem:wxSizerItem() when<br /> %% This::wxSizer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags().<br /> %% @@ -508,7 +508,7 @@ prepend(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ItemT,ref=ItemRef}) -> Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}; + | {userData, wx:wx_object()}; (This, Window, Flags) -> wxSizerItem:wxSizerItem() when This::wxSizer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags(). @@ -551,7 +551,7 @@ prepend(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef},#wx_ Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. prepend(#wx_ref{type=ThisT,ref=ThisRef},Width,Height, Options) when is_integer(Width),is_integer(Height),is_list(Options) -> ?CLASS(ThisT,wxSizer), diff --git a/lib/wx/src/gen/wxSizerItem.erl b/lib/wx/src/gen/wxSizerItem.erl index 22533500da..62655864d1 100644 --- a/lib/wx/src/gen/wxSizerItem.erl +++ b/lib/wx/src/gen/wxSizerItem.erl @@ -72,7 +72,7 @@ new(Width,Height,#wx_ref{type=FlagsT,ref=FlagsRef}) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsizeritem.html#wxsizeritemwxsizeritem">external documentation</a>. -spec new(Window, Proportion, Flag, Border, UserData) -> wxSizerItem() when - Window::wxWindow:wxWindow() | wxSizer:wxSizer(), Proportion::integer(), Flag::integer(), Border::integer(), UserData::wx:wx(). + Window::wxWindow:wxWindow() | wxSizer:wxSizer(), Proportion::integer(), Flag::integer(), Border::integer(), UserData::wx:wx_object(). new(#wx_ref{type=WindowT,ref=WindowRef},Proportion,Flag,Border,#wx_ref{type=UserDataT,ref=UserDataRef}) when is_integer(Proportion),is_integer(Flag),is_integer(Border) -> WindowOP = case ?CLASS_T(WindowT,wxWindow) of @@ -88,7 +88,7 @@ new(#wx_ref{type=WindowT,ref=WindowRef},Proportion,Flag,Border,#wx_ref{type=User %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsizeritem.html#wxsizeritemwxsizeritem">external documentation</a>. -spec new(Width, Height, Proportion, Flag, Border, UserData) -> wxSizerItem() when - Width::integer(), Height::integer(), Proportion::integer(), Flag::integer(), Border::integer(), UserData::wx:wx(). + Width::integer(), Height::integer(), Proportion::integer(), Flag::integer(), Border::integer(), UserData::wx:wx_object(). new(Width,Height,Proportion,Flag,Border,#wx_ref{type=UserDataT,ref=UserDataRef}) when is_integer(Width),is_integer(Height),is_integer(Proportion),is_integer(Flag),is_integer(Border) -> ?CLASS(UserDataT,wx), @@ -200,7 +200,7 @@ getSpacer(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsizeritem.html#wxsizeritemgetuserdata">external documentation</a>. --spec getUserData(This) -> wx:wx() when +-spec getUserData(This) -> wx:wx_object() when This::wxSizerItem(). getUserData(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxSizerItem), diff --git a/lib/wx/src/gen/wxSlider.erl b/lib/wx/src/gen/wxSlider.erl index e951e559ca..459e9b9c35 100644 --- a/lib/wx/src/gen/wxSlider.erl +++ b/lib/wx/src/gen/wxSlider.erl @@ -99,7 +99,7 @@ new(Parent,Id,Value,MinValue,MaxValue) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Value,MinValue,MaxValue, Options) when is_integer(Id),is_integer(Value),is_integer(MinValue),is_integer(MaxValue),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -126,7 +126,7 @@ create(This,Parent,Id,Value,MinValue,MaxValue) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Value,MinValue,MaxValue, Options) when is_integer(Id),is_integer(Value),is_integer(MinValue),is_integer(MaxValue),is_list(Options) -> ?CLASS(ThisT,wxSlider), diff --git a/lib/wx/src/gen/wxStyledTextCtrl.erl b/lib/wx/src/gen/wxStyledTextCtrl.erl index 057e01ddd5..f6dc2176b7 100644 --- a/lib/wx/src/gen/wxStyledTextCtrl.erl +++ b/lib/wx/src/gen/wxStyledTextCtrl.erl @@ -252,7 +252,7 @@ addText(#wx_ref{type=ThisT,ref=ThisRef},Text) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxstyledtextctrl.html#wxstyledtextctrladdstyledtext">external documentation</a>. -spec addStyledText(This, Data) -> ok when - This::wxStyledTextCtrl(), Data::wxMemoryBuffer:wxMemoryBuffer(). + This::wxStyledTextCtrl(), Data::wx:wx_object(). addStyledText(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=DataT,ref=DataRef}) -> ?CLASS(ThisT,wxStyledTextCtrl), ?CLASS(DataT,wxMemoryBuffer), @@ -361,7 +361,7 @@ setSavePoint(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxstyledtextctrl.html#wxstyledtextctrlgetstyledtext">external documentation</a>. --spec getStyledText(This, StartPos, EndPos) -> wxMemoryBuffer:wxMemoryBuffer() when +-spec getStyledText(This, StartPos, EndPos) -> wx:wx_object() when This::wxStyledTextCtrl(), StartPos::integer(), EndPos::integer(). getStyledText(#wx_ref{type=ThisT,ref=ThisRef},StartPos,EndPos) when is_integer(StartPos),is_integer(EndPos) -> diff --git a/lib/wx/src/gen/wxTextCtrl.erl b/lib/wx/src/gen/wxTextCtrl.erl index 21ff062d5b..e1f82c40c3 100644 --- a/lib/wx/src/gen/wxTextCtrl.erl +++ b/lib/wx/src/gen/wxTextCtrl.erl @@ -106,7 +106,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -201,7 +201,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxTextCtrl), diff --git a/lib/wx/src/gen/wxToggleButton.erl b/lib/wx/src/gen/wxToggleButton.erl index 1d770b5ca5..ed2f564952 100644 --- a/lib/wx/src/gen/wxToggleButton.erl +++ b/lib/wx/src/gen/wxToggleButton.erl @@ -97,7 +97,7 @@ new(Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -125,7 +125,7 @@ create(This,Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxToggleButton), diff --git a/lib/wx/src/gen/wxToolBar.erl b/lib/wx/src/gen/wxToolBar.erl index e55becb748..9401e30e20 100644 --- a/lib/wx/src/gen/wxToolBar.erl +++ b/lib/wx/src/gen/wxToolBar.erl @@ -86,7 +86,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}). -type wxToolBar() :: wx:wx_object(). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddcontrol">external documentation</a>. --spec addControl(This, Control) -> wx:wx() when +-spec addControl(This, Control) -> wx:wx_object() when This::wxToolBar(), Control::wxControl:wxControl(). addControl(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ControlT,ref=ControlRef}) -> ?CLASS(ThisT,wxToolBar), @@ -95,7 +95,7 @@ addControl(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ControlT,ref=ControlRef} <<ThisRef:32/?UI,ControlRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddseparator">external documentation</a>. --spec addSeparator(This) -> wx:wx() when +-spec addSeparator(This) -> wx:wx_object() when This::wxToolBar(). addSeparator(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxToolBar), @@ -103,8 +103,8 @@ addSeparator(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddtool">external documentation</a>. --spec addTool(This, Tool) -> wx:wx() when - This::wxToolBar(), Tool::wx:wx(). +-spec addTool(This, Tool) -> wx:wx_object() when + This::wxToolBar(), Tool::wx:wx_object(). addTool(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ToolT,ref=ToolRef}) -> ?CLASS(ThisT,wxToolBar), ?CLASS(ToolT,wx), @@ -112,7 +112,7 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ToolT,ref=ToolRef}) -> <<ThisRef:32/?UI,ToolRef:32/?UI>>). %% @equiv addTool(This,Toolid,Bitmap, []) --spec addTool(This, Toolid, Bitmap) -> wx:wx() when +-spec addTool(This, Toolid, Bitmap) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(). addTool(This,Toolid,Bitmap) @@ -121,19 +121,19 @@ addTool(This,Toolid,Bitmap) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddtool">external documentation</a>. %% <br /> Also:<br /> -%% addTool(This, Toolid, Bitmap, BmpDisabled) -> wx:wx() when<br /> +%% addTool(This, Toolid, Bitmap, BmpDisabled) -> wx:wx_object() when<br /> %% This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap();<br /> -%% (This, Toolid, Bitmap, [Option]) -> wx:wx() when<br /> +%% (This, Toolid, Bitmap, [Option]) -> wx:wx_object() when<br /> %% This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(),<br /> %% Option :: {shortHelpString, unicode:chardata()}<br /> %% | {longHelpString, unicode:chardata()}.<br /> %% %%<br /> Kind = ?wxITEM_SEPARATOR | ?wxITEM_NORMAL | ?wxITEM_CHECK | ?wxITEM_RADIO | ?wxITEM_MAX --spec addTool(This, Toolid, Label, Bitmap) -> wx:wx() when +-spec addTool(This, Toolid, Label, Bitmap) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(); - (This, Toolid, Bitmap, BmpDisabled) -> wx:wx() when + (This, Toolid, Bitmap, BmpDisabled) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(); - (This, Toolid, Bitmap, [Option]) -> wx:wx() when + (This, Toolid, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), Option :: {shortHelpString, unicode:chardata()} | {longHelpString, unicode:chardata()}. @@ -158,28 +158,28 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,#wx_ref{type=BitmapT,ref=BitmapRe %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddtool">external documentation</a>. %% <br /> Also:<br /> -%% addTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx() when<br /> +%% addTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx_object() when<br /> %% This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(),<br /> %% Option :: {shortHelp, unicode:chardata()}<br /> %% | {kind, wx:wx_enum()};<br /> -%% (This, Toolid, Bitmap, BmpDisabled, [Option]) -> wx:wx() when<br /> +%% (This, Toolid, Bitmap, BmpDisabled, [Option]) -> wx:wx_object() when<br /> %% This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(),<br /> %% Option :: {toggle, boolean()}<br /> -%% | {clientData, wx:wx()}<br /> +%% | {clientData, wx:wx_object()}<br /> %% | {shortHelpString, unicode:chardata()}<br /> %% | {longHelpString, unicode:chardata()}.<br /> %% %%<br /> Kind = ?wxITEM_SEPARATOR | ?wxITEM_NORMAL | ?wxITEM_CHECK | ?wxITEM_RADIO | ?wxITEM_MAX --spec addTool(This, Toolid, Label, Bitmap, BmpDisabled) -> wx:wx() when +-spec addTool(This, Toolid, Label, Bitmap, BmpDisabled) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(); - (This, Toolid, Label, Bitmap, [Option]) -> wx:wx() when + (This, Toolid, Label, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), Option :: {shortHelp, unicode:chardata()} | {kind, wx:wx_enum()}; - (This, Toolid, Bitmap, BmpDisabled, [Option]) -> wx:wx() when + (This, Toolid, Bitmap, BmpDisabled, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(), Option :: {toggle, boolean()} - | {clientData, wx:wx()} + | {clientData, wx:wx_object()} | {shortHelpString, unicode:chardata()} | {longHelpString, unicode:chardata()}. @@ -213,22 +213,22 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,#wx_ref{type=BitmapT,ref=BitmapRe %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddtool">external documentation</a>. %% <br /> Also:<br /> -%% addTool(This, Toolid, Label, Bitmap, BmpDisabled, [Option]) -> wx:wx() when<br /> +%% addTool(This, Toolid, Label, Bitmap, BmpDisabled, [Option]) -> wx:wx_object() when<br /> %% This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(),<br /> %% Option :: {kind, wx:wx_enum()}<br /> %% | {shortHelp, unicode:chardata()}<br /> %% | {longHelp, unicode:chardata()}<br /> -%% | {data, wx:wx()}.<br /> +%% | {data, wx:wx_object()}.<br /> %% %%<br /> Kind = ?wxITEM_SEPARATOR | ?wxITEM_NORMAL | ?wxITEM_CHECK | ?wxITEM_RADIO | ?wxITEM_MAX --spec addTool(This, Toolid, Bitmap, BmpDisabled, Toggle, XPos) -> wx:wx() when +-spec addTool(This, Toolid, Bitmap, BmpDisabled, Toggle, XPos) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(), Toggle::boolean(), XPos::integer(); - (This, Toolid, Label, Bitmap, BmpDisabled, [Option]) -> wx:wx() when + (This, Toolid, Label, Bitmap, BmpDisabled, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(), Option :: {kind, wx:wx_enum()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()} - | {data, wx:wx()}. + | {data, wx:wx_object()}. addTool(This,Toolid,Bitmap,BmpDisabled,Toggle,XPos) when is_record(This, wx_ref),is_integer(Toolid),is_record(Bitmap, wx_ref),is_record(BmpDisabled, wx_ref),is_boolean(Toggle),is_integer(XPos) -> @@ -249,10 +249,10 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,ref=Bi <<ThisRef:32/?UI,Toolid:32/?UI,(byte_size(Label_UC)):32/?UI,(Label_UC)/binary, 0:(((8- ((4+byte_size(Label_UC)) band 16#7)) band 16#7))/unit:8,BitmapRef:32/?UI,BmpDisabledRef:32/?UI, BinOpt/binary>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddtool">external documentation</a>. --spec addTool(This, Toolid, Bitmap, BmpDisabled, Toggle, XPos, [Option]) -> wx:wx() when +-spec addTool(This, Toolid, Bitmap, BmpDisabled, Toggle, XPos, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(), Toggle::boolean(), XPos::integer(), Option :: {yPos, integer()} - | {clientData, wx:wx()} + | {clientData, wx:wx_object()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()}. addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,#wx_ref{type=BitmapT,ref=BitmapRef},#wx_ref{type=BmpDisabledT,ref=BmpDisabledRef},Toggle,XPos, Options) @@ -270,7 +270,7 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,#wx_ref{type=BitmapT,ref=BitmapRe <<ThisRef:32/?UI,Toolid:32/?UI,BitmapRef:32/?UI,BmpDisabledRef:32/?UI,(wxe_util:from_bool(Toggle)):32/?UI,XPos:32/?UI, BinOpt/binary>>). %% @equiv addCheckTool(This,Toolid,Label,Bitmap, []) --spec addCheckTool(This, Toolid, Label, Bitmap) -> wx:wx() when +-spec addCheckTool(This, Toolid, Label, Bitmap) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(). addCheckTool(This,Toolid,Label,Bitmap) @@ -278,12 +278,12 @@ addCheckTool(This,Toolid,Label,Bitmap) addCheckTool(This,Toolid,Label,Bitmap, []). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddchecktool">external documentation</a>. --spec addCheckTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx() when +-spec addCheckTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), Option :: {bmpDisabled, wxBitmap:wxBitmap()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()} - | {data, wx:wx()}. + | {data, wx:wx_object()}. addCheckTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef}, Options) when is_integer(Toolid),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxToolBar), @@ -299,7 +299,7 @@ addCheckTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,r <<ThisRef:32/?UI,Toolid:32/?UI,(byte_size(Label_UC)):32/?UI,(Label_UC)/binary, 0:(((8- ((4+byte_size(Label_UC)) band 16#7)) band 16#7))/unit:8,BitmapRef:32/?UI, 0:32,BinOpt/binary>>). %% @equiv addRadioTool(This,Toolid,Label,Bitmap, []) --spec addRadioTool(This, Toolid, Label, Bitmap) -> wx:wx() when +-spec addRadioTool(This, Toolid, Label, Bitmap) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(). addRadioTool(This,Toolid,Label,Bitmap) @@ -307,12 +307,12 @@ addRadioTool(This,Toolid,Label,Bitmap) addRadioTool(This,Toolid,Label,Bitmap, []). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddradiotool">external documentation</a>. --spec addRadioTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx() when +-spec addRadioTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), Option :: {bmpDisabled, wxBitmap:wxBitmap()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()} - | {data, wx:wx()}. + | {data, wx:wx_object()}. addRadioTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef}, Options) when is_integer(Toolid),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxToolBar), @@ -355,7 +355,7 @@ enableTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Enable) <<ThisRef:32/?UI,Toolid:32/?UI,(wxe_util:from_bool(Enable)):32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarfindbyid">external documentation</a>. --spec findById(This, Toolid) -> wx:wx() when +-spec findById(This, Toolid) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(). findById(#wx_ref{type=ThisT,ref=ThisRef},Toolid) when is_integer(Toolid) -> @@ -373,7 +373,7 @@ findControl(#wx_ref{type=ThisT,ref=ThisRef},Toolid) <<ThisRef:32/?UI,Toolid:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarfindtoolforposition">external documentation</a>. --spec findToolForPosition(This, X, Y) -> wx:wx() when +-spec findToolForPosition(This, X, Y) -> wx:wx_object() when This::wxToolBar(), X::integer(), Y::integer(). findToolForPosition(#wx_ref{type=ThisT,ref=ThisRef},X,Y) when is_integer(X),is_integer(Y) -> @@ -467,7 +467,7 @@ getToolState(#wx_ref{type=ThisT,ref=ThisRef},Toolid) <<ThisRef:32/?UI,Toolid:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarinsertcontrol">external documentation</a>. --spec insertControl(This, Pos, Control) -> wx:wx() when +-spec insertControl(This, Pos, Control) -> wx:wx_object() when This::wxToolBar(), Pos::integer(), Control::wxControl:wxControl(). insertControl(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=ControlT,ref=ControlRef}) when is_integer(Pos) -> @@ -477,7 +477,7 @@ insertControl(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=ControlT,ref=Cont <<ThisRef:32/?UI,Pos:32/?UI,ControlRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarinsertseparator">external documentation</a>. --spec insertSeparator(This, Pos) -> wx:wx() when +-spec insertSeparator(This, Pos) -> wx:wx_object() when This::wxToolBar(), Pos::integer(). insertSeparator(#wx_ref{type=ThisT,ref=ThisRef},Pos) when is_integer(Pos) -> @@ -486,8 +486,8 @@ insertSeparator(#wx_ref{type=ThisT,ref=ThisRef},Pos) <<ThisRef:32/?UI,Pos:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarinserttool">external documentation</a>. --spec insertTool(This, Pos, Tool) -> wx:wx() when - This::wxToolBar(), Pos::integer(), Tool::wx:wx(). +-spec insertTool(This, Pos, Tool) -> wx:wx_object() when + This::wxToolBar(), Pos::integer(), Tool::wx:wx_object(). insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=ToolT,ref=ToolRef}) when is_integer(Pos) -> ?CLASS(ThisT,wxToolBar), @@ -496,7 +496,7 @@ insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=ToolT,ref=ToolRef}) <<ThisRef:32/?UI,Pos:32/?UI,ToolRef:32/?UI>>). %% @equiv insertTool(This,Pos,Toolid,Bitmap, []) --spec insertTool(This, Pos, Toolid, Bitmap) -> wx:wx() when +-spec insertTool(This, Pos, Toolid, Bitmap) -> wx:wx_object() when This::wxToolBar(), Pos::integer(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(). insertTool(This,Pos,Toolid,Bitmap) @@ -505,22 +505,22 @@ insertTool(This,Pos,Toolid,Bitmap) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarinserttool">external documentation</a>. %% <br /> Also:<br /> -%% insertTool(This, Pos, Toolid, Bitmap, [Option]) -> wx:wx() when<br /> +%% insertTool(This, Pos, Toolid, Bitmap, [Option]) -> wx:wx_object() when<br /> %% This::wxToolBar(), Pos::integer(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(),<br /> %% Option :: {bmpDisabled, wxBitmap:wxBitmap()}<br /> %% | {toggle, boolean()}<br /> -%% | {clientData, wx:wx()}<br /> +%% | {clientData, wx:wx_object()}<br /> %% | {shortHelp, unicode:chardata()}<br /> %% | {longHelp, unicode:chardata()}.<br /> %% %%<br /> Kind = ?wxITEM_SEPARATOR | ?wxITEM_NORMAL | ?wxITEM_CHECK | ?wxITEM_RADIO | ?wxITEM_MAX --spec insertTool(This, Pos, Toolid, Label, Bitmap) -> wx:wx() when +-spec insertTool(This, Pos, Toolid, Label, Bitmap) -> wx:wx_object() when This::wxToolBar(), Pos::integer(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(); - (This, Pos, Toolid, Bitmap, [Option]) -> wx:wx() when + (This, Pos, Toolid, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Pos::integer(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), Option :: {bmpDisabled, wxBitmap:wxBitmap()} | {toggle, boolean()} - | {clientData, wx:wx()} + | {clientData, wx:wx_object()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()}. @@ -543,13 +543,13 @@ insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,Toolid,#wx_ref{type=BitmapT,ref=B %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarinserttool">external documentation</a>. %%<br /> Kind = ?wxITEM_SEPARATOR | ?wxITEM_NORMAL | ?wxITEM_CHECK | ?wxITEM_RADIO | ?wxITEM_MAX --spec insertTool(This, Pos, Toolid, Label, Bitmap, [Option]) -> wx:wx() when +-spec insertTool(This, Pos, Toolid, Label, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Pos::integer(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), Option :: {bmpDisabled, wxBitmap:wxBitmap()} | {kind, wx:wx_enum()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()} - | {clientData, wx:wx()}. + | {clientData, wx:wx_object()}. insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef}, Options) when is_integer(Pos),is_integer(Toolid),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxToolBar), @@ -574,7 +574,7 @@ realize(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarremovetool">external documentation</a>. --spec removeTool(This, Toolid) -> wx:wx() when +-spec removeTool(This, Toolid) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(). removeTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid) when is_integer(Toolid) -> diff --git a/lib/wx/src/gen/wxTreeCtrl.erl b/lib/wx/src/gen/wxTreeCtrl.erl index fef9916fff..dfa9e691ce 100644 --- a/lib/wx/src/gen/wxTreeCtrl.erl +++ b/lib/wx/src/gen/wxTreeCtrl.erl @@ -118,7 +118,7 @@ new(Parent) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef}, Options) when is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -235,7 +235,7 @@ create(This,Parent) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options) when is_list(Options) -> ?CLASS(ThisT,wxTreeCtrl), diff --git a/lib/wx/src/gen/wxWindow.erl b/lib/wx/src/gen/wxWindow.erl index d31f489726..229633a106 100644 --- a/lib/wx/src/gen/wxWindow.erl +++ b/lib/wx/src/gen/wxWindow.erl @@ -526,7 +526,7 @@ getCursor(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxwindow.html#wxwindowgetdroptarget">external documentation</a>. --spec getDropTarget(This) -> wxDropTarget:wxDropTarget() when +-spec getDropTarget(This) -> wx:wx_object() when This::wxWindow(). getDropTarget(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxWindow), @@ -1384,7 +1384,7 @@ setOwnForegroundColour(#wx_ref{type=ThisT,ref=ThisRef},Colour) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxwindow.html#wxwindowsetdroptarget">external documentation</a>. -spec setDropTarget(This, DropTarget) -> ok when - This::wxWindow(), DropTarget::wxDropTarget:wxDropTarget(). + This::wxWindow(), DropTarget::wx:wx_object(). setDropTarget(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=DropTargetT,ref=DropTargetRef}) -> ?CLASS(ThisT,wxWindow), ?CLASS(DropTargetT,wxDropTarget), diff --git a/lib/wx/src/wx.erl b/lib/wx/src/wx.erl index 32e9f5da3f..7d62305048 100644 --- a/lib/wx/src/wx.erl +++ b/lib/wx/src/wx.erl @@ -71,13 +71,17 @@ retain_memory/1, release_memory/1]). -export([demo/0]). --export_type([wx_colour/0, wx_datetime/0, wx_enum/0, wx_mouseState/0, wx_wxHtmlLinkInfo/0]). + +-export_type([wx_object/0, wx_env/0, wx_memory/0]). +-export_type([wx_colour/0, wx_colour4/0, wx_datetime/0, + wx_enum/0, wx_wxMouseState/0, wx_wxHtmlLinkInfo/0]). + -include("wxe.hrl"). -include("../include/wx.hrl"). --opaque wx_object() :: #wx_ref{}. %% Opaque object reference --opaque wx_env() :: #wx_env{}. %% Opaque process environment --opaque wx_memory() :: binary() | #wx_mem{}. %% Opaque memory reference +-type wx_object() :: #wx_ref{}. %% Opaque object reference +-type wx_env() :: #wx_env{}. %% Opaque process environment +-type wx_memory() :: binary() | #wx_mem{}. %% Opaque memory reference -type wx_colour4() :: {R::byte(),G::byte(),B::byte(), A::byte()}. -type wx_colour() :: {R::byte(),G::byte(),B::byte()} | wx_colour4(). @@ -85,7 +89,7 @@ -type wx_datetime() :: {{Year::integer(),Month::integer(),Day::integer()}, {Hour::integer(),Minute::integer(),Second::integer()}}. %% In Local Timezone --type wx_mouseState() :: #wxMouseState{}. %% See #wxMouseState{} defined in wx.hrl +-type wx_wxMouseState() :: #wxMouseState{}. %% See #wxMouseState{} defined in wx.hrl -type wx_enum() :: integer(). %% Constant defined in wx.hrl -type wx_wxHtmlLinkInfo() :: #wxHtmlLinkInfo{}. @@ -311,14 +315,15 @@ debug(Level) when is_integer(Level) -> end. %% @doc Starts a wxErlang demo if examples directory exists and is compiled --spec demo() -> ok. +-spec demo() -> ok | {error, atom()}. demo() -> Priv = code:priv_dir(wx), Demo = filename:join([filename:dirname(Priv),examples,demo]), Mod = list_to_atom("demo"), %% Fool xref tests case file:set_cwd(Demo) of ok -> - apply(Mod, start, []); + apply(Mod, start, []), + ok; _ -> {error, no_demo_dir} end. diff --git a/lib/xmerl/include/xmerl_xlink.hrl b/lib/xmerl/include/xmerl_xlink.hrl deleted file mode 100644 index 375e244c23..0000000000 --- a/lib/xmerl/include/xmerl_xlink.hrl +++ /dev/null @@ -1,26 +0,0 @@ - - - -%% The following is a brief summary of the element types (columns) on -%% which the global attributes are allowed: -%% -%% simple extended locator arc resource title -%% type X X X X X X -%% href X X -%% role X X X X -%% title X X X X -%% show X X X -%% actuate X X X -%% from X -%% to X -%% --record(xlink, { - type, % simple | extended | locator | arc | resource | title - href, - role - title, - show, - actuate, - from, - to - }). diff --git a/lib/xmerl/src/Makefile b/lib/xmerl/src/Makefile index 7009b50f6c..5b77c9478b 100644 --- a/lib/xmerl/src/Makefile +++ b/lib/xmerl/src/Makefile @@ -94,7 +94,6 @@ MODULES = $(EDOC_MODULES) \ HRL_FILES = \ ../include/xmerl.hrl \ - ../include/xmerl_xlink.hrl \ ../include/xmerl_xpath.hrl \ ../include/xmerl_xsd.hrl diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl index 45e2a928ac..1d795799ce 100644 --- a/lib/xmerl/src/xmerl_sax_parser.erl +++ b/lib/xmerl/src/xmerl_sax_parser.erl @@ -217,7 +217,7 @@ check_encoding_option(E) when E==utf8; E=={utf16,little}; E=={utf16,big}; check_encoding_option(utf16) -> {utf16,big}; check_encoding_option(E) -> - {error, io_lib:format("Charcter set ~p not supported", [E])}. + {error, io_lib:format("Character set ~p not supported", [E])}. %%---------------------------------------------------------------------- %% Function: detect_charset(Xml, State) @@ -279,6 +279,7 @@ convert_encoding(Enc) -> %% Just for 7,8 bit + utf8 case string:to_lower(Enc) of "utf-8" -> utf8; "us-ascii" -> utf8; + "latin1" -> latin1; "iso-8859-1" -> latin1; % Handle all iso-8859 as latin1 "iso-8859-2" -> latin1; "iso-8859-3" -> latin1; diff --git a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc index c25cde0472..e988c49292 100644 --- a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc +++ b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc @@ -39,6 +39,9 @@ %% Internal exports %%---------------------------------------------------------------------- -export([ + cf/3, + cf/4, + cf/5 ]). %%---------------------------------------------------------------------- |