aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/doc/src/erlang.xml7
-rw-r--r--erts/doc/src/escript.xml10
-rw-r--r--erts/emulator/Makefile.in2
-rw-r--r--erts/emulator/beam/beam_emu.c14
-rwxr-xr-xerts/emulator/beam/erl_bif_info.c25
-rw-r--r--erts/emulator/beam/erl_init.c4
-rw-r--r--erts/emulator/internal_doc/CodeLoading.md186
-rw-r--r--erts/emulator/internal_doc/Tracing.md220
-rw-r--r--erts/emulator/sys/unix/sys.c19
-rw-r--r--erts/emulator/test/exception_SUITE.erl11
-rw-r--r--erts/emulator/test/scheduler_SUITE.erl2
-rwxr-xr-xerts/emulator/utils/make_version4
-rw-r--r--erts/epmd/src/epmd_cli.c4
-rw-r--r--erts/epmd/test/epmd_SUITE.erl23
-rw-r--r--erts/etc/common/erlexec.c30
-rw-r--r--erts/etc/unix/etp-commands.in343
-rw-r--r--erts/etc/win32/erlang.icobin1398 -> 99678 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin97872 -> 97916 bytes
-rw-r--r--erts/preloaded/src/erlang.erl1
-rw-r--r--erts/vsn.mk6
20 files changed, 865 insertions, 46 deletions
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 711473afd2..124302a2cb 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -6008,6 +6008,13 @@ ok
<seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>, and
<seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
</item>
+ <tag><marker id="system_info_otp_correction_package"><c>otp_correction_package</c></marker></tag>
+ <item>
+ <p>Returns a string containing the OTP correction package version
+ number that currenly executing VM is part of. Note that other
+ OTP applications in the system may be part of other OTP correction
+ packages.</p>
+ </item>
<tag><marker id="system_info_otp_release"><c>otp_release</c></marker></tag>
<item>
<p>Returns a string containing the OTP release number.</p>
diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml
index 180447cac4..d2b09d4515 100644
--- a/erts/doc/src/escript.xml
+++ b/erts/doc/src/escript.xml
@@ -44,6 +44,7 @@
<p><c>escript</c> runs a script written in Erlang.</p>
<p>Here follows an example.</p>
<pre>
+$ <input>chmod u+x factorial</input>
$ <input>cat factorial</input>
#!/usr/bin/env escript
%% -*- erlang -*-
@@ -66,12 +67,13 @@ usage() ->
fac(0) -> 1;
fac(N) -> N * fac(N-1).
-$ <input>factorial 5</input>
+$ <input>./factorial 5</input>
factorial 5 = 120
-$ <input>factorial</input>
+$ <input>./factorial</input>
usage: factorial integer
-$ <input>factorial five</input>
-usage: factorial integer </pre>
+$ <input>./factorial five</input>
+usage: factorial integer
+ </pre>
<p>The header of the Erlang script in the example differs from
a normal Erlang module. The first line is intended to be the
interpreter line, which invokes <c>escript</c>. However if you
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 5638683f88..b270099566 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -575,7 +575,7 @@ GENERATE += $(TTF_DIR)/erl_alloc_types.h
# version include file
$(TARGET)/erl_version.h: ../vsn.mk
- $(gen_verbose)LANG=C $(PERL) utils/make_version -o $@ $(SYSTEM_VSN) $(VSN)$(SERIALNO) $(TARGET)
+ $(gen_verbose)LANG=C $(PERL) utils/make_version -o $@ $(SYSTEM_VSN) $(SYSTEM_CP_VSN) $(VSN)$(SERIALNO) $(TARGET)
GENERATE += $(TARGET)/erl_version.h
# driver table
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 78ab6fa30f..592cfe273f 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -4326,7 +4326,19 @@ void process_main(void)
flags = Arg(2);
BsGetFieldSize(tmp_arg2, (flags >> 3), ClauseFail(), size);
if (size >= SMALL_BITS) {
- Uint wordsneeded = 1+WSIZE(NBYTES((Uint) size));
+ Uint wordsneeded;
+ /* check bits size before potential gc.
+ * We do not want a gc and then realize we don't need
+ * the allocated space (i.e. if the op fails)
+ *
+ * remember to reacquire the matchbuffer after gc.
+ */
+
+ mb = ms_matchbuffer(tmp_arg1);
+ if (mb->size - mb->offset < size) {
+ ClauseFail();
+ }
+ wordsneeded = 1+WSIZE(NBYTES((Uint) size));
TestHeapPreserve(wordsneeded, Arg(1), tmp_arg1);
}
mb = ms_matchbuffer(tmp_arg1);
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 414ae2f046..e0b654cb22 100755
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -64,8 +64,10 @@ static Export *gather_gc_info_res_trap;
#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
+static char otp_correction_package[] = ERLANG_OTP_CORRECTION_PACKAGE;
/* Keep erts_system_version as a global variable for easy access from a core */
static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE
+ "%s"
" [erts-" ERLANG_VERSION "]"
#if !HEAP_ON_C_STACK && !HALFWORD_HEAP
" [no-c-stack-objects]"
@@ -304,11 +306,28 @@ make_link_list(Process *p, ErtsLink *root, Eterm tail)
int
erts_print_system_version(int to, void *arg, Process *c_p)
{
+ int i, rc = -1;
+ char *rc_str = "";
+ char rc_buf[100];
+ char *ocp = otp_correction_package;
#ifdef ERTS_SMP
Uint total, online, active;
(void) erts_schedulers_state(&total, &online, &active, 0);
#endif
- return erts_print(to, arg, erts_system_version
+ for (i = 0; i < sizeof(otp_correction_package)-4; i++) {
+ if (ocp[i] == '-' && ocp[i+1] == 'r' && ocp[i+2] == 'c')
+ rc = atoi(&ocp[i+3]);
+ }
+ if (rc >= 0) {
+ if (rc == 0)
+ rc_str = " [DEVELOPMENT]";
+ else {
+ erts_snprintf(rc_buf, sizeof(rc_buf), " [RELEASE CANDIDATE %d]", rc);
+ rc_str = rc_buf;
+ }
+ }
+ return erts_print(to, arg, erts_system_version,
+ rc_str
#ifdef ERTS_SMP
, total, online
#endif
@@ -2417,6 +2436,10 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
DECL_AM(unknown);
BIF_RET(AM_unknown);
}
+ } else if (ERTS_IS_ATOM_STR("otp_correction_package", BIF_ARG_1)) {
+ int n = sizeof(ERLANG_OTP_CORRECTION_PACKAGE)-1;
+ hp = HAlloc(BIF_P, 2*n);
+ BIF_RET(buf_to_intlist(&hp, ERLANG_OTP_CORRECTION_PACKAGE, n, NIL));
} else if (ERTS_IS_ATOM_STR("otp_release", BIF_ARG_1)) {
int n = sizeof(ERLANG_OTP_RELEASE)-1;
hp = HAlloc(BIF_P, 2*n);
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 8c4fffa75b..1af80dd04b 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -553,8 +553,8 @@ void erts_usage(void)
erts_fprintf(stderr, " numbers is %d\n",
ERTS_MAX_NO_OF_SCHEDULERS);
erts_fprintf(stderr, "-SP p1:p2 specify schedulers (p1) and schedulers online (p2)\n");
- erts_fprintf(stderr, " as percentages of logical processors configured and logical\n");
- erts_fprintf(stderr, " processors available, respectively\n");
+ erts_fprintf(stderr, " as percentages of logical processors configured and logical\n");
+ erts_fprintf(stderr, " processors available, respectively\n");
erts_fprintf(stderr, "-t size set the maximum number of atoms the "
"emulator can handle\n");
erts_fprintf(stderr, " valid range is [%d-%d]\n",
diff --git a/erts/emulator/internal_doc/CodeLoading.md b/erts/emulator/internal_doc/CodeLoading.md
new file mode 100644
index 0000000000..151b9cd57c
--- /dev/null
+++ b/erts/emulator/internal_doc/CodeLoading.md
@@ -0,0 +1,186 @@
+Non-Blocking Code Loading
+=========================
+
+Introduction
+------------
+
+Before OTP R16 when an Erlang code module was loaded, all other
+execution in the VM were halted while the load operation was carried
+out in single threaded mode. This might not be a big problem for
+initial loading of modules during VM boot, but it can be a severe
+problem for availability when upgrading modules or adding new code on
+a VM with running payload. This problem grows with the number of cores
+as both the time it takes to wait for all schedulers to stop increases
+as well as the potential amount of halted ongoing work.
+
+In OTP R16, modules are loaded without blocking the VM.
+Erlang processes may continue executing undisturbed in parallel during
+the entire load operation. The code loading is carried out by a normal
+Erlang process that is scheduled like all the others. The load
+operation is completed by making the loaded code visible to all
+processes in a consistent way with one single atomic
+instruction. Non-blocking code loading will improve real-time
+characteristics when modules are loaded/upgraded on a running SMP
+system.
+
+
+The Load Phases
+---------------
+
+The loading of a module is divided into two phases; a *prepare phase*
+and a *finishing phase*. The prepare phase contains reading the BEAM
+file format and all the preparations of the loaded code that can
+easily be done without interference with the running code. The
+finishing phase will make the loaded (and prepared) code accessible
+from the running code. Old module versions (replaced or deleted) will
+also be made inaccessible by the finishing phase.
+
+The prepare phase is designed to allow several "loader" processes to
+prepare separate modules in parallel while the finishing phase can
+only be done by one loader process at a time. A second loader process
+trying to enter finishing phase will be suspended until the first
+loader is done. This will only block the process, the scheduler is
+free to schedule other work while the second loader is waiting. (See
+`erts_try_seize_code_write_permission` and
+`erts_release_code_write_permission`).
+
+The ability to prepare several modules in parallel is not currently
+used as almost all code loading is serialized by the code_server
+process. The BIF interface is however prepared for this.
+
+ erlang:prepare_loading(Module, Code) -> LoaderState
+ erlang:finish_loading([LoaderState])
+
+The idea is that `prepare_loading` could be called in parallel for
+different modules and returns a "magic binary" containing the internal
+state of each prepared module. Function `finish_loading` could take a
+list of such states and do the finishing of all of them in one go.
+
+Currenlty we use the legacy BIF `erlang:load_module` which is now
+implemented in Erlang by calling the above two functions in
+sequence. Function `finish_loading` is limited to only accepts a list
+with one module state as we do not yet use the multi module loading
+feature.
+
+
+The Finishing Sequence
+----------------------
+
+During VM execution, code is accessed through a number of data
+structures. These *code access structures* are
+
+* Export table. One entry for every exported function.
+* Module table. One entry for each loaded module.
+* "beam_catches". Identifies jump destinations for catch instructions.
+* "beam_ranges". Map code address to function and line in source file.
+
+The most frequently used of these structures is the export table that
+is accessed in run time for every executed external function call to
+get the address of the callee. For performance reasons, we want to
+access all these structures without any overhead from thread
+synchronization. Earlier this was solved with an emergency break. Stop
+the entire VM to mutate these code access structures, otherwise treat
+them as read-only.
+
+The solution in R16 is instead to *replicate* the code access
+structures. We have one set of active structures read by the running
+code. When new code is loaded the active structures are copied, the
+copy is updated to include the newly loaded module and then a switch
+is made to make the updated copy the new active set. The active set is
+identified by a single global atomic variable
+`the_active_code_index`. The switch can thus be made by a single
+atomic write operation. The running code have to read this atomic
+variable when using the active access structures, which means one
+atomic read operation per external function call for example. The
+performance penalty from this extra atomic read is however very small
+as it can be done without any memory barriers at all (as described
+below). With this solution we also preserve the transactional feature
+of a load operation. Running code will never see the intermediate
+result of a half loaded module.
+
+The finishing phase is carried out in the following sequence by the
+BIF `erlang:finish_loading`:
+
+1. Seize exclusive code write permission (suspend process if needed
+ until we get it).
+
+2. Make a full copy of all the active access structures. This copy is
+ called the staging area and is identified by the global atomic
+ variable `the_staging_code_index`.
+
+3. Update all access structures in the staging area to include the
+ newly prepared module.
+
+4. Schedule a thread progress event. That is a time in the future when
+ all schedulers have yielded and executed a full memory barrier.
+
+5. Suspend the loader process.
+
+6. After thread progress, commit the staging area by assigning
+ `the_staging_code_index` to `the_active_code_index`.
+
+7. Release the code write permission allowing other processes to stage
+ new code.
+
+8. Resume the loader process allowing it to return from
+ `erlang:finish_loading`.
+
+
+### Thread Progress
+
+The waiting for thread progress in 4-6 is necessary in order for
+processes to read `the_active_code_index` atomic during normal
+execution without any expensive memory barriers. When we write a new
+value into `the_active_code_index` in step 6, we know that all
+schedulers will see an updated and consistent view of all the new
+active access structures once they become reachable through
+`the_active_code_index`.
+
+The total lack of memory barrier when reading `the_active_code_index`
+has one interesting consequence however. Different processes may see
+the new code at different point in time depending on when different
+cores happen to refresh their hardware caches. This may sound unsafe
+but it actually does not matter. The only property we must guarantee
+is that the ability to see the new code must spread with process
+communication. After receiving a message that was triggered by new
+code, the receiver must be guaranteed to also see the new code. This
+will be guaranteed as all types of process communication involves
+memory barriers in order for the receiver to be sure to read what the
+sender has written. This implicit memory barrier will then also make
+sure that the receiver reads the new value of `the_active_code_index`
+and thereby also sees the new code. This is true for all kinds of
+inter process communication (TCP, ETS, process name registering,
+tracing, drivers, NIFs, etc) not just Erlang messages.
+
+### Code Index Reuse
+
+To optimize the copy operation in step 2, code access structures are
+reused. In current solution we have three sets of code access
+structures, identified by a code index of 0, 1 and 2. These indexes
+are used in a round robin fashion. Instead of having to initialize a
+completely new copy of all access structures for every load operation
+we just have to update with the changes that have happened since the
+last two code load operations. We could get by with only two code
+indexes (0 and 1), but that would require yet another round of waiting
+for thread progress before step 2 in the `finish_loading` sequence. We
+cannot start reusing a code index as staging area until we know that
+no lingering scheduler thread is still using it as the active code
+index. With three generations of code indexes, the waiting for thread
+progress in step 4-6 will give this guarantee for us. Thread progress
+will wait for all running schedulers to reschedule at least one
+time. No ongoing execution reading code access structures reached from
+an old value of `the_active_code_index` can exist after a second round
+of thread progress.
+
+The design choice between two or three generations of code access
+structures is a trade-off between memory consumption and code loading
+latency.
+
+### A Consistent Code View
+
+Some native BIFs may need to get a consistent snapshot view of the
+active code. To do this it is important to only read
+`the_active_code_index` one time and then use that index value for all
+code accessing during the BIF. If a load operation is executed in
+parallel, reading `the_active_code_index` a second time might result
+in a different value, and thereby a different view of the code.
diff --git a/erts/emulator/internal_doc/Tracing.md b/erts/emulator/internal_doc/Tracing.md
new file mode 100644
index 0000000000..30bc5327a7
--- /dev/null
+++ b/erts/emulator/internal_doc/Tracing.md
@@ -0,0 +1,220 @@
+Non-blocking trace setting
+==========================
+
+Introduction
+------------
+
+Before OTP R16 when trace settings were changed by `erlang:trace_pattern`,
+all other execution in the VM were halted while the trace operation
+was carried out in single threaded mode. Similar to code loading, this
+can impose a severe problem for availability that grows with the
+number of cores.
+
+In OTP R16, trace breakpoints are set in the code without blocking the
+VM. Erlang processes may continue executing undisturbed in parallel
+during the entire operation. The same base technique is used as for
+code loading. A staging area of breakpoints is prepared and then made
+active with a single atomic operation.
+
+
+Redesign of Breakpoint Wheel
+----------------------------
+
+To make it easier to manage breakpoints without single threaded mode a
+redesign of the breakpoint mechanism has been made. The old
+"breakpoint wheel" data structure was a circular double-linked list of
+breakpoints for each instrumented function. It was invented before the
+SMP emulator. To support it in the SMP emulator, is was essentially
+expanded to one breakpoint wheel per scheduler. As more breakpoint
+types have been added, the implementation have become messy and hard
+to understand and maintain.
+
+In the new design the old wheel was dropped and instead replaced by
+one struct (`GenericBp`) to hold the data for all types of breakpoints
+for each instrumented function. A bit-flag field is used to indicate
+what different type of break actions that are enabled.
+
+
+Same Same but Different
+-----------------------
+Even though `trace_pattern` use the same technique as the non-blocking
+code loading with replicated generations of data structures and an
+atomic switch, the implementations are quite separate from each
+other. One initial idea was to use the existing mechanism of code
+loading to do a dummy load operation that would make a copy of the
+affected modules. That copy could then be instrumented with
+breakpoints before making it reachable with the same atomic switch as
+done for code loading. This approach seems straight forward but has a
+number of shortcomings, one being the large memory footprint when many
+modules are instrumented. Another problem is how execution will reach
+the new instrumented code. Normally loaded code can only be reached
+through external functions calls. Trace settings must be activated
+instantaneously without the need of external function calls.
+
+The choosen solution is instead for tracing to use the technique of
+replication applied on the data structures for breakpoints. Two
+generations of breakpoints are kept and indentified by index of 0 and
+1. The global atomic variables `erts_active_bp_index` will determine
+which generation of breakpoints running code will use.
+
+### Atomicy Without Atomic Operations
+
+Not using the code loading generations (or any other code duplication)
+means that `trace_pattern` must at some point write to the active beam
+code in order for running processes to reach the staged breakpoints
+structures. This can be done with one single atomic write operation
+per instrumented function. The beam instruction words are however read
+with normal memory loads and not through the atomic API. The only
+guarantee we need is that the written instruction word is seen as
+atomic. Either fully written or not at all. This is true for word
+aligned write operation on all hardware architectures we use.
+
+
+Adding a new Breakpoint
+-----------------------
+This is a simplified sequence describing what `trace_pattern` goes
+through when adding a new breakpoint.
+
+1. Seize exclusive code write permission (suspend process until we get it).
+
+2. Allocate breakpoint structure `GenericBp` including both generations.
+ Set the active part as disabled with a zeroed flagfield. Save the original
+ instruction word in the breakpoint.
+
+3. Write a pointer to the breakpoint at offset -4 from the first
+ instruction "func_info" header.
+
+4. Set the staging part of the breakpoint as enabled with specified
+ breakpoint data.
+
+5. Wait for thread progress.
+
+6. Write a `op_i_generic_breakpoint` as the first instruction for the function.
+ This instruction will execute the breakpoint that it finds at offset -4.
+
+7. Wait for thread progress.
+
+8. Commit the breadpoint by switching `erts_active_bp_index`.
+
+9. Wait for thread progress.
+
+10. Prepare for next call to `trace_pattern` by updating the new staging part
+ (the old active) of the breakpoint to be identic to the the new active part.
+
+11. Release code write permission and return from `trace_pattern`.
+
+
+The code write permission "lock" seized in step 1 is the same as used
+by code loading. This will ensure that only one process at a time can
+stage new trace settings but it will also prevent concurrent code
+loading and make sure we see a consistent view of the beam code during
+the entire sequence.
+
+Between step 6 and 8, runninng processes might execute the written
+`op_i_generic_breakpoint` instruction. They will get the breakpoint
+structure written in step 3, read `erts_active_bp_index` and execute
+the corresponding part of the breakpoint. Before the switch in step 8
+becomes visible they will however execute the disabled part of the
+breakpoint structure and do nothing other than executing the saved
+original instruction.
+
+
+To Updating and Remove Breakpoints
+----------------------------------
+
+The above sequence did only describe adding a new breakpoint. We do
+basically the same sequence to update the settings of an existing
+breakpoint except step 2,3 and 6 can be skipped as it has already been
+done.
+
+To remove a breakpoint some more steps are needed. The idea is to
+first stage the breakpoint as disabled, do the switch, wait for thread
+progress and then remove the disabled breakpoint by restoring the
+original beam instruction.
+
+Here is a more complete sequence that contains both adding, updating
+and removing breakpoints.
+
+1. Seize exclusive code write permission (suspend process until we get it).
+
+2. Allocate new breakpoint structures with a disabled active part and
+ the original beam instruction. Write a pointer to the breakpoint in
+ "func_info" header at offset -4.
+
+3. Update the staging part of all affected breakpoints. Disable
+ breakpoints that are to be removed.
+
+4. Wait for thread progress.
+
+5. Write a `op_i_generic_breakpoint` as the first instruction for all
+ functions with new breakpoints.
+
+6. Wait for thread progress.
+
+7. Commit all staged breadpoints by switching `erts_active_bp_index`.
+
+8. Wait for thread progress.
+
+
+9. Restore original beam instruction for disabled breakpoints.
+
+10. Wait for thread progress.
+
+11. Prepare for next call to `trace_pattern` by updating the new
+ staging area (the old active) for all enabled breakpoints.
+
+12. Deallocate disabled breakpoint structures.
+
+13. Release code write permission and return from `trace_pattern`.
+
+
+### All that Waiting for Thread Progress
+
+There are four rounds of waiting for thread progress in the above
+sequence. In the code loading sequence we sacrificed memory overhead
+of three generations to avoid a second round of thread progress. The
+latency of `trace_pattern` should not be such a big problem for
+however, as it is normally not called in a rapid sequence.
+
+The waiting in step 4 is to make sure all threads will see an updated
+view of the breakpoint structures once they become reachable through
+the `op_i_generic_breakpoint` instruction written in step 5.
+
+The waiting in step 6 is to make the activation of the new trace
+settings "as atomic as possible". Different cores might see the new
+value of `erts_active_bp_index` at different times as it is read
+without any memory barrier. But this is the best we can do without
+more expensive thread synchronization.
+
+The waiting in step 8 is to make sure we dont't restore the original
+bream instructions for disabled breakpoints until we know that no
+thread is still accessing the old enabled part of a disabled
+breakpoint.
+
+The waiting in step 10 is to make sure no lingering thread is still
+accessing disabled breakpoint structures to be deallocated in step
+12.
+
+
+Global Tracing
+--------------
+
+Call tracing with `global` option only affects external function
+calls. This was earlier handled by inserting a special trace
+instruction in export entries without the use of breakpoints. With the
+new non-blocking tracing we want to avoid special handling for global
+tracing and make use of the staging and atomic switching within the
+breakpoint mechanism. The solution was to create the same type of
+breakpoint structure for a global call trace. The difference to local
+tracing is that we insert the `op_i_generic_breakpoint` instruction
+(with its pointer at offset -4) in the export entry rather than in the
+code.
+
+
+Future work
+-----------
+
+We still go to single threaded mode when new code is loaded for a
+module that is traced, or when loading code when there is a default
+trace pattern set. That is not impossible to fix, but that requires
+much closer cooperation between tracing BIFs and the loader BIFs.
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 61f9f6a59a..59e34eb819 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -547,6 +547,25 @@ erts_sys_pre_init(void)
#endif
#endif /* USE_THREADS */
erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0);
+
+ {
+ /*
+ * Unfortunately we depend on fd 0,1,2 in the old shell code.
+ * So if for some reason we do not have those open when we start
+ * we have to open them here. Not doing this can cause the emulator
+ * to deadlock when reaping the fd_driver ports :(
+ */
+ int fd;
+ /* Make sure fd 0 is open */
+ if ((fd = open("/dev/null", O_RDONLY)) != 0)
+ close(fd);
+ /* Make sure fds 1 and 2 are open */
+ while (fd < 3) {
+ fd = open("/dev/null", O_WRONLY);
+ }
+ close(fd);
+ }
+
}
void
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index 109cec25cb..09a7a87a9a 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -589,6 +589,13 @@ line_numbers(Config) when is_list(Config) ->
[{file,ModFile},{line,_}]}|_]}} =
(catch build_binary2(8, bad_binary)),
+ <<"abc",357:16>> = build_binary3(<<"abc">>),
+ {'EXIT',{badarg,[{?MODULE,build_binary3,1,
+ [{file,"bit_syntax.erl"},{line,72511}]},
+ {?MODULE,line_numbers,1,
+ [{file,ModFile},{line,_}]}|_]}} =
+ (catch build_binary3(no_binary)),
+
{'EXIT',{function_clause,
[{?MODULE,do_call_abs,[y,y],
[{file,"gc_bif.erl"},{line,18}]},
@@ -691,6 +698,10 @@ build_binary2(Size, Bin) -> %Line 72505
id(0), %Line 72506
<<7:Size,Bin/binary>>. %Line 72507
+build_binary3(Bin) -> %Line 72509
+ id(0), %Line 72510
+ <<Bin/binary,357:16>>. %Line 72511
+
-file("gc_bif.erl", 17).
do_call_abs(x, Arg) -> %Line 18
abs(Arg). %Line 19
diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl
index 81539faa09..6a43e2b0e7 100644
--- a/erts/emulator/test/scheduler_SUITE.erl
+++ b/erts/emulator/test/scheduler_SUITE.erl
@@ -1495,7 +1495,7 @@ mcall(Node, Funs) ->
end, Refs).
erl_rel_flag_var() ->
- "ERL_"++erlang:system_info(otp_release)++"_FLAGS".
+ "ERL_OTP"++erlang:system_info(otp_release)++"_FLAGS".
clear_erl_rel_flags() ->
EnvVar = erl_rel_flag_var(),
diff --git a/erts/emulator/utils/make_version b/erts/emulator/utils/make_version
index 7757fa8138..02b68f2b39 100755
--- a/erts/emulator/utils/make_version
+++ b/erts/emulator/utils/make_version
@@ -41,6 +41,9 @@ if ($ARGV[0] eq '-o') {
my $release = shift;
defined $release or die "No release specified";
+my $correction_package = shift;
+defined $correction_package or die "No correction package specified";
+
my $version = shift;
defined $version or die "No version name specified";
@@ -53,6 +56,7 @@ open(FILE, ">$outputfile") or die "Can't create $outputfile: $!";
print FILE <<EOF;
/* This file was created by 'make_version' -- don't modify. */
#define ERLANG_OTP_RELEASE "$release"
+#define ERLANG_OTP_CORRECTION_PACKAGE "$correction_package"
#define ERLANG_VERSION "$version"
#define ERLANG_COMPILE_DATE "$time_str"
#define ERLANG_ARCHITECTURE "$architecture"
diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c
index 8817bde8d7..bd30bc35d9 100644
--- a/erts/epmd/src/epmd_cli.c
+++ b/erts/epmd/src/epmd_cli.c
@@ -118,7 +118,7 @@ void epmd_call(EpmdVars *g,int what)
if (!g->silent) {
rval = erts_snprintf(buf, OUTBUF_SIZE,
"epmd: up and running on port %d with data:\n", j);
- write(1, buf, rval);
+ fwrite(buf, 1, rval, stdout);
}
while(1) {
if ((rval = read(fd,buf,OUTBUF_SIZE)) <= 0) {
@@ -126,7 +126,7 @@ void epmd_call(EpmdVars *g,int what)
epmd_cleanup_exit(g,0);
}
if (!g->silent)
- write(1, buf, rval); /* Potentially UTF-8 encoded */
+ fwrite(buf, 1, rval, stdout); /* Potentially UTF-8 encoded */
}
}
diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl
index cc24a556a3..a752abf33b 100644
--- a/erts/epmd/test/epmd_SUITE.erl
+++ b/erts/epmd/test/epmd_SUITE.erl
@@ -69,6 +69,8 @@
returns_valid_empty_extra/1,
returns_valid_populated_extra_with_nulls/1,
+ names_stdout/1,
+
buffer_overrun_1/1,
buffer_overrun_2/1,
no_nonlocal_register/1,
@@ -118,6 +120,7 @@ all() ->
too_large, alive_req_too_small_1, alive_req_too_small_2,
alive_req_too_large, returns_valid_empty_extra,
returns_valid_populated_extra_with_nulls,
+ names_stdout,
{group, buffer_overrun}, no_nonlocal_register,
no_nonlocal_kill, no_live_killing].
@@ -759,6 +762,24 @@ returns_valid_populated_extra_with_nulls(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+names_stdout(doc) ->
+ ["Test that epmd -names prints registered nodes to stdout"];
+names_stdout(suite) ->
+ [];
+names_stdout(Config) when is_list(Config) ->
+ ?line ok = epmdrun(),
+ ?line {ok,Sock} = register_node("foobar"),
+ ?line ok = epmdrun("-names"),
+ ?line {ok, Data} = receive {_Port, {data, D}} -> {ok, D}
+ after 10000 -> {error, timeout}
+ end,
+ ?line {match,_} = re:run(Data, "^epmd: up and running", [multiline]),
+ ?line {match,_} = re:run(Data, "^name foobar at port", [multiline]),
+ ?line ok = close(Sock),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
buffer_overrun_1(suite) ->
[];
buffer_overrun_1(doc) ->
@@ -968,7 +989,7 @@ epmdrun(Epmd,Args0) ->
O ->
" "++O
end,
- osrun("\"" ++ Epmd ++ "\"" ++ Args ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT)).
+ osrun("\"" ++ Epmd ++ "\"" ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT) ++ Args).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 1d7811d570..c30203c632 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -1972,35 +1972,8 @@ get_file_args(char *filename, argv_buf *abp, argv_buf *xabp)
}
static void
-write_erl_otp_flags(char *bufp)
-{
- /* ERL_OTP<MAJOR-VSN>_FLAGS */
- int ix = 0;
- char *otp_p;
- char otp[] = OTP_SYSTEM_VERSION;
-
- bufp[ix++] = 'E';
- bufp[ix++] = 'R';
- bufp[ix++] = 'L';
- bufp[ix++] = '_';
- bufp[ix++] = 'O';
- bufp[ix++] = 'T';
- bufp[ix++] = 'P';
- for (otp_p = &otp[0]; '0' <= *otp_p && *otp_p <= '9'; otp_p++)
- bufp[ix++] = *otp_p;
- bufp[ix++] = '_';
- bufp[ix++] = 'F';
- bufp[ix++] = 'L';
- bufp[ix++] = 'A';
- bufp[ix++] = 'G';
- bufp[ix++] = 'S';
- bufp[ix] = '\0';
-}
-
-static void
initial_argv_massage(int *argc, char ***argv)
{
- char erl_otp_flags_buf[] = "ERL_OTP" OTP_SYSTEM_VERSION "_FLAGS";
argv_buf ab = {0}, xab = {0};
int ix, vix, ac;
char **av;
@@ -2016,8 +1989,7 @@ initial_argv_massage(int *argc, char ***argv)
vix = 0;
- write_erl_otp_flags(erl_otp_flags_buf);
- av = build_args_from_env(erl_otp_flags_buf);
+ av = build_args_from_env("ERL_OTP" OTP_SYSTEM_VERSION "_FLAGS");
if (av)
avv[vix++].argv = av;
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index 73887931cc..8520d58f47 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -652,7 +652,7 @@ end
define etp-ct-atom-1
# Args: int
#
-# Determines if integer is a atom first character
+# Determines if integer is an atom first character
#
# Non-reentrant
# Returns: $etp_ct_atom
@@ -1278,6 +1278,250 @@ document etpf-stackdump
%---------------------------------------------------------------------------
end
+define etp-heapdump
+# Args: Process*
+#
+# Non-reentrant
+ etp-heapdump-1 ($arg0)->heap ($arg0)->htop
+end
+
+document etp-heapdump
+%---------------------------------------------------------------------------
+% etp-heapdump Process*
+%
+% Take an Process* and print a heapdump for the process heap.
+%---------------------------------------------------------------------------
+end
+
+define etp-heapdump-old
+# Args: Process*
+#
+# Non-reentrant
+ etp-heapdump-1 ($arg0)->old_heap ($arg0)->old_htop
+end
+
+document etp-heapdump
+%---------------------------------------------------------------------------
+% etp-heapdump-old Process*
+%
+% Take an Process* and print a heapdump for the process old heap (gen-heap).
+%---------------------------------------------------------------------------
+end
+
+
+define etp-heapdump-1
+# Args: Eterm* heap, Eterm* htop
+#
+# Non-reentrant
+ set $etp_heapdump_heap = (Eterm*)($arg0)
+ set $etp_heapdump_p = (Eterm*)($arg0)
+ set $etp_heapdump_end = (Eterm*)($arg1)
+ set $etp_heapdump_skips = 0
+ printf "%% heapdump (%u):\n", $etp_heapdump_end-$etp_heapdump_p
+ while $etp_heapdump_p < $etp_heapdump_end
+ set $etp_heapdump_ix = 0
+ printf " %p: ", $etp_heapdump_p
+ while $etp_heapdump_p < $etp_heapdump_end && $etp_heapdump_ix < 8
+ if ($etp_heapdump_skips > 0)
+ printf "| 0x%08x ", ($etp_heapdump_p)
+ set $etp_heapdump_skips--
+ else
+ etp-term-dump $etp_heapdump_p[0]
+ end
+ set $etp_heapdump_p++
+ set $etp_heapdump_ix++
+ end
+ printf "\n"
+ end
+end
+
+
+define etp-term-dump
+# Args: Eterm
+ if (($arg0) & 0x3) == 0
+ etp-term-dump-header ($arg0)
+ else
+ if (($arg0) & 0x3) == 1
+ # Cons pointer
+ set $etp_term_dump_cons_p = ((Eterm*)(($arg0) & ~0x3))
+ if $etp_term_dump_cons_p > $etp_heapdump_heap && $etp_term_dump_cons_p < $etp_heapdump_end
+ printf "| C:0x%08x ", $etp_term_dump_cons_p
+ #printf "| C: --> %5d ", $etp_heapdump_p - $etp_term_dump_cons_p - 1
+ else
+ printf "| C:0x%08x ", $etp_term_dump_cons_p
+ end
+ else
+ if (($arg0) & 0x3) == 2
+ # Box pointer
+ printf "| B:0x%08x ", ($arg0)
+ else
+ if (($arg0) & 0x3) == 3
+ # immediate
+ etp-term-dump-immediate ($arg0)
+ else
+ printf "| U:0x%08x ", ($arg0)
+ end
+ end
+ end
+ end
+end
+
+define etp-term-dump-immediate
+# Args: immediate term
+ if (($arg0) & 0xF) == 0xf
+ # Fixnum
+ etp-ct-printable-1 ((long)((Sint)($arg0)>>4))
+ if $etp_ct_printable
+ if $etp_ct_printable < 0
+ printf "| I: %c (%3ld) ", (long)((Sint)($arg0)>>4), (long)((Sint)($arg0)>>4)
+ else
+ printf "| I: \\%c (%3ld) ", (long)((Sint)($arg0)>>4), (long)((Sint)($arg0)>>4)
+ end
+ else
+ printf "| I:%10ld ", (long)((Sint)($arg0)>>4)
+ end
+ else
+ if (($arg0) & 0xF) == 0x3
+ etp-term-dump-pid ($arg0)
+ else
+ if (($arg0) & 0xF) == 0x7
+ printf "| port:0x%05x ", ($arg0)
+ else
+ # Immediate2 - 0xB
+ if (($arg0) & 0x3f) == 0x0b
+ etp-term-dump-atom ($arg0)
+ else
+ if (($arg0) & 0x3f) == 0x1b
+ printf "| #Catch<%06d> ", ($arg0)>>6
+ else
+ if (($arg0) == $etp_nil)
+ printf "| [] (NIL) "
+ else
+ printf "| I:0x%08x ", ($arg0)
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+define etp-term-dump-atom
+# Args: atom term
+ set $etp_atom_1_ap = (Atom*)erts_atom_table.seg_table[(Eterm)($arg0)>>16][((Eterm)($arg0)>>6)&0x3FF]
+ set $etp_atom_1_i = ($etp_atom_1_ap)->len
+ set $etp_atom_1_p = ($etp_atom_1_ap)->name
+ set $etp_atom_1_quote = 1
+ set $etp_atom_indent = 13
+
+ if ($etp_atom_1_i < 11)
+ if ($etp_atom_1_i > 0)
+ etp-ct-atom-1 (*$etp_atom_1_p)
+ if $etp_ct_atom
+ set $etp_atom_indent = 13
+ else
+ set $etp_atom_indent = 11
+ end
+ end
+ # perform indentation
+ printf "|"
+ while ($etp_atom_1_i < $etp_atom_indent)
+ printf " "
+ set $etp_atom_1_i++
+ end
+ set $etp_atom_1_i = ($etp_atom_1_ap)->len
+ # Check if atom has to be quoted
+ if ($etp_atom_1_i > 0)
+ etp-ct-atom-1 (*$etp_atom_1_p)
+ if $etp_ct_atom
+ # Atom start character
+ set $etp_atom_1_p++
+ set $etp_atom_1_i--
+ set $etp_atom_1_quote = 0
+ else
+ set $etp_atom_1_i = 0
+ end
+ end
+ while $etp_atom_1_i > 0
+ etp-ct-name-1 (*$etp_atom_1_p)
+ if $etp_ct_name
+ # Name character
+ set $etp_atom_1_p++
+ set $etp_atom_1_i--
+ else
+ set $etp_atom_1_quote = 1
+ set $etp_atom_1_i = 0
+ end
+ end
+ # Print the atom
+ if $etp_atom_1_quote
+ printf "'"
+ end
+ set $etp_atom_1_i = ($etp_atom_1_ap)->len
+ set $etp_atom_1_p = ($etp_atom_1_ap)->name
+ while $etp_atom_1_i > 0
+ etp-char-1 (*$etp_atom_1_p) '\''
+ set $etp_atom_1_p++
+ set $etp_atom_1_i--
+ end
+ if $etp_atom_1_quote
+ printf "'"
+ end
+ printf " "
+ else
+ printf "| A:0x%08x ", ($arg0)
+ end
+end
+
+define etp-term-dump-pid
+# Args: Eterm pid
+#
+# Non-reentrant
+#
+ set $etp_pid_1 = (Eterm)($arg0)
+ if ($etp_pid_1 & 0xF) == 0x3
+ if (etp_arch_bits == 64 && etp_halfword == 0)
+ if (etp_big_endian)
+ set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 36) & 0x0fffffff)
+ else
+ set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 4) & 0x0fffffff)
+ end
+ else
+ set $etp_pid_data = (unsigned) (((((Uint32) $etp_pid_1) >> 4) & ~erts_proc.r.o.pix_mask) | ((((Uint32) $etp_pid_1) >> (erts_proc.r.o.pix_cl_shift + 4)) & erts_proc.r.o.pix_cl_mask) | (((((Uint32) $etp_pid_1) >> 4) & erts_proc.r.o.pix_cli_mask) << erts_proc.r.o.pix_cli_shift))
+ end
+ # Internal pid
+ printf "| <0.%04u.%03u> ", $etp_pid_data & 0x7fff, ($etp_pid_data >> 15) & 0x1fff
+ else
+ printf "| #NotPid<%#x> ", ($arg0)
+ end
+end
+
+define etp-term-dump-header
+# Args: Header term
+ if (($arg0) & 0x3f) == 0
+ printf "| H:%4d-tuple ", ($arg0) >> 6
+ else
+ set $etp_heapdump_skips = ($arg0) >> 6
+ if ((($arg0) & 0x3f) == 0x18)
+ printf "| H: float %3d ", ($arg0) >> 6
+ else
+ if ((($arg0) & 0x3f) == 0x28)
+ # sub-binary
+ printf "| H: sub-bin "
+ else
+ if ((($arg0) & 0x3f) == 0x8)
+ # pos-bignum
+ printf "| H:bignum %3u ", ($arg0) >> 6
+ else
+ printf "| header %5d ", ($arg0) >> 6
+ end
+ end
+ end
+ end
+end
+
+
+
define etp-pid2pix-1
# Args: Eterm
#
@@ -1445,7 +1689,7 @@ define etp-process-info
# Args: Process*
#
printf " Pid: "
- etp-1 $arg0->common.id
+ etp-1 ($arg0)->common.id
printf "\n State: "
etp-proc-state $arg0
if $proxy_process != 0
@@ -1523,11 +1767,104 @@ end
document etp-processes
%---------------------------------------------------------------------------
% etp-processes
-%
+%
% Print misc info about all processes
%---------------------------------------------------------------------------
end
+define etp-processes-memory
+ if (!erts_initialized)
+ printf "No processes, since system isn't initialized!\n"
+ else
+ set $proc_ix = 0
+ printf "--- (%ld processes in wheel)\n", erts_proc.r.o.max
+ while $proc_ix < erts_proc.r.o.max
+ set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$proc_ix])
+ if ($proc != ((Process *) 0) && $proc != &erts_invalid_process)
+ etp-process-memory-info $proc
+ end
+ set $proc_ix++
+ end
+ printf "---\n",
+ end
+end
+
+document etp-processes-memory
+%---------------------------------------------------------------------------
+% etp-processes-memory
+%
+% Print memory info about all processes
+%---------------------------------------------------------------------------
+end
+
+define etp-process-memory-info
+# Args: Process*
+#
+ if ((*(((Uint32 *) &(((Process *) $arg0)->state)))) & 0x400000)
+ set $proxy_process = 1
+ else
+ set $proxy_process = 0
+ end
+ printf " "
+ etp-1 $arg0->common.id
+ printf ": (Process *) %p ", $arg0
+ if $proxy_process != 0
+ printf "(Process *) %p ", $arg0
+ printf " *** PROXY process struct *** refer to next: \n"
+ etp-pid2proc-1 $arg0->common.id
+ printf " -"
+ etp-process-memory-info $proc
+ else
+ printf " [Heap: %5ld", $arg0->heap_sz
+ if ($arg0->old_heap)
+ printf " | %5ld", $arg0->old_hend - $arg0->old_heap
+ else
+ printf " | none "
+ end
+ printf "] [Mbuf: %5ld", $arg0->mbuf_sz
+ if (etp_smp_compiled)
+ printf " | %3ld (%3ld | %3ld)", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len
+ else
+ printf " | %3ld", $arg0->msg.len
+ end
+ printf "] "
+ if ($arg0->i)
+ printf " I: "
+ etp-cp-1 $arg0->i
+ printf " "
+ end
+
+ if ($arg0->current)
+ etp-1 $arg0->current[0]
+ printf ":"
+ etp-1 $arg0->current[1]
+ printf "/%d ", $arg0->current[2]
+ end
+
+ if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0
+ if ($arg0->common.u.alive.reg)
+ etp-1 $arg0->common.u.alive.reg->name
+ printf " "
+ end
+ end
+
+ if ($arg0->cp)
+ printf " CP: "
+ etp-cp-1 $arg0->cp
+ printf " "
+ end
+ printf "\n"
+ end
+end
+
+document etp-process-memory-info
+%---------------------------------------------------------------------------
+% etp-process-memory-info Process*
+%
+% Print memory info about process
+%---------------------------------------------------------------------------
+end
+
define etp-port-id2pix-1
# Args: Eterm
#
diff --git a/erts/etc/win32/erlang.ico b/erts/etc/win32/erlang.ico
index cee8b58af9..7b62d31aa9 100644
--- a/erts/etc/win32/erlang.ico
+++ b/erts/etc/win32/erlang.ico
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 73fac27161..3c77d6ae0f 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 0ed677c3d8..f99d5bfdd0 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -2246,6 +2246,7 @@ tuple_to_list(_Tuple) ->
(modified_timing_level) -> integer() | undefined;
(multi_scheduling) -> disabled | blocked | enabled;
(multi_scheduling_blockers) -> [PID :: pid()];
+ (otp_correction_package) -> string();
(otp_release) -> string();
(port_count) -> non_neg_integer();
(port_limit) -> pos_integer();
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 30aa870144..8e77a9a26e 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,11 @@
#
VSN = 6.0
-SYSTEM_VSN = 17.0-rc0
+
+# OTP major version
+SYSTEM_VSN = 17
+# OTP correction package version
+SYSTEM_CP_VSN = 17.0-rc0
# Port number 4365 in 4.2
# Port number 4366 in 4.3