diff options
Diffstat (limited to 'erts')
31 files changed, 288 insertions, 134 deletions
diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index 06a8691c0e..bc01919da1 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -67,7 +67,22 @@ XML_REF3_FILES = \ erts_alloc.xml XML_PART_FILES = \ - part.xml + part.xml internal.xml + +XML_INTERNAL_FILES = \ + CarrierMigration.xml \ + ThreadProgress.xml \ + CodeLoading.xml \ + Tracing.xml \ + DelayedDealloc.xml \ + beam_makeops.xml \ + GarbageCollection.xml \ + PTables.xml \ + PortSignals.xml \ + ProcessManagementOptimizations.xml \ + SuperCarrier.xml \ + CountingInstructions.xml + XML_CHAPTER_FILES = \ introduction.xml \ @@ -97,6 +112,8 @@ XML_FILES = \ $(BOOK_FILES) $(XML_CHAPTER_FILES) \ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF1_FILES) $(XML_APPLICATION_FILES) +XML_GEN_FILES = $(XML_INTERNAL_FILES:%=$(XMLDIR)/%) + # ---------------------------------------------------- HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ @@ -116,6 +133,12 @@ SPECS_FILES = $(XML_REF3_EFILES:%.xml=$(SPECDIR)/specs_%.xml) TOP_SPECS_FILE = specs.xml +XML_FIGURE_DIR = $(XMLDIR)/figures + +INTERNAL_DOC_PNG_FILES = $(wildcard ../../emulator/internal_doc/figures/*.png) +PNG_FILES = $(notdir $(INTERNAL_DOC_PNG_FILES)) +XMLDIR_PNG_FILES = $(PNG_FILES:%=$(XML_FIGURE_DIR)/%) + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- @@ -128,10 +151,15 @@ SPECS_FLAGS = -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE) # ---------------------------------------------------- # Targets # ---------------------------------------------------- +_create_dirs := $(shell mkdir -p $(XML_FIGURE_DIR)) + $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -docs: man pdf html $(INFO_FILE) +$(XML_FIGURE_DIR)/%.png: ../../emulator/internal_doc/figures/%.png + $(INSTALL_DATA) $< $@ + +docs: figures man pdf html $(INFO_FILE) $(TOP_PDF_FILE): $(XML_FILES) @@ -146,6 +174,7 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%) $(INFO_FILE): $(INFO_FILE_SRC) $(ERL_TOP)/make/$(TARGET)/otp.mk sed -e 's;%RELEASE%;$(SYSTEM_VSN);' $(INFO_FILE_SRC) > $(INFO_FILE) +figures: $(XMLDIR_PNG_FILES) debug opt: @@ -164,6 +193,9 @@ $(SPECDIR)/specs_%.xml: $(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ -o$(dir $@) -module $(patsubst $(SPECDIR)/specs_%.xml,%,$@) +$(XMLDIR)/%.xml: ../../emulator/internal_doc/%.md $(ERL_TOP)/make/emd2exml + $(ERL_TOP)/make/emd2exml $< $@ + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- @@ -173,8 +205,11 @@ release_docs_spec: docs $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html/figures" $(INSTALL_DATA) $(HTMLDIR)/* \ "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(XMLDIR)/figures/* \ + "$(RELSYSDIR)/doc/html/figures" $(INSTALL_DATA) $(ERL_TOP)/erts/example/time_compat.erl \ "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(ERL_TOP)/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl \ diff --git a/erts/doc/src/alt_disco.xml b/erts/doc/src/alt_disco.xml index d04221b9b3..148d6f549e 100644 --- a/erts/doc/src/alt_disco.xml +++ b/erts/doc/src/alt_disco.xml @@ -63,7 +63,7 @@ <seealso marker="kernel:erl_epmd">EPMD module</seealso>. However, instead of communicating with EPMD you can connect to any service to find out connection details of other nodes. A discovery module is enabled - by setting <seealso marker="erts:erl#epmd_module">-epmd_module</seealso> + by setting <seealso marker="erts:erl">-epmd_module</seealso> when starting erlang. The discovery module must implement the following callbacks:</p> diff --git a/erts/doc/src/book.xml b/erts/doc/src/book.xml index a0780c91d9..d79da1e4f7 100644 --- a/erts/doc/src/book.xml +++ b/erts/doc/src/book.xml @@ -41,6 +41,9 @@ <applications> <xi:include href="ref_man.xml"/> </applications> + <internals> + <xi:include href="internal.xml"/> + </internals> <releasenotes> <xi:include href="notes.xml"/> </releasenotes> diff --git a/erts/doc/src/communication.xml b/erts/doc/src/communication.xml index 7e18a73aa8..251b52dc65 100644 --- a/erts/doc/src/communication.xml +++ b/erts/doc/src/communication.xml @@ -64,7 +64,7 @@ a synchronous communication operation consists of two asynchronous signals; one request signal and one reply signal. An example of such a synchronous communication is a call to - <seealso marker="erlang:process_info/2"> + <seealso marker="erlang#process_info/2"> <c>erlang:process_info/2</c></seealso> when the first argument is not <c>self()</c>. The caller sends an asynchronous signal requesting information, and then diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index a9aeb1888c..33d0903622 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -290,7 +290,7 @@ Slogan: <reason></pre> <title>Memory Information</title> <p>Under the tag <em>=memory</em> is shown information similar to what can be obtainted on a living node with - <seealso marker="erts:erlang#erlang:memory/0"> + <seealso marker="erts:erlang#memory/0"> <c>erlang:memory()</c></seealso>.</p> </section> diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 58678f2393..3e2d3bb447 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -230,7 +230,7 @@ <item> <p>With these functions, the driver sends data back to the emulator. The data is received as messages by the port owner process, see - <seealso marker="erlang:open_port/2"> + <seealso marker="erlang#open_port/2"> <c>erlang:open_port/2</c></seealso>. The vector function and the function taking a driver binary are faster, as they avoid copying the data buffer. There is also a fast way of sending @@ -1154,27 +1154,27 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> <taglist> <tag><seealso marker="driver_entry#start"> <c>start</c></seealso></tag> - <item>Called from <seealso marker="erlang:open_port/2"> + <item>Called from <seealso marker="erlang#open_port/2"> <c>erlang:open_port/2</c></seealso>.</item> <tag><seealso marker="driver_entry#output"> <c>output</c></seealso></tag> - <item>Called from <seealso marker="erlang:send/2"> + <item>Called from <seealso marker="erlang#send/2"> <c>erlang:send/2</c></seealso> and - <seealso marker="erlang:port_command/2"> + <seealso marker="erlang#port_command/2"> <c>erlang:port_command/2</c></seealso>.</item> <tag><seealso marker="driver_entry#outputv"> <c>outputv</c></seealso></tag> - <item>Called from <seealso marker="erlang:send/2"> + <item>Called from <seealso marker="erlang#send/2"> <c>erlang:send/2</c></seealso> and - <seealso marker="erlang:port_command/2"> + <seealso marker="erlang#port_command/2"> <c>erlang:port_command/2</c></seealso>.</item> <tag><seealso marker="driver_entry#control"> <c>control</c></seealso></tag> - <item>Called from <seealso marker="erlang:port_control/3"> + <item>Called from <seealso marker="erlang#port_control/3"> <c>erlang:port_control/3</c></seealso>.</item> <tag><seealso marker="driver_entry#call"> <c>call</c></seealso></tag> - <item>Called from <seealso marker="erlang:port_call/3"> + <item>Called from <seealso marker="erlang#port_call/3"> <c>erlang:port_call/3</c></seealso>.</item> </taglist> <p>Notice that this function is <em>not</em> thread-safe, not @@ -2305,7 +2305,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> <c>*value_size</c> has been set to the buffer size needed.</p> <warning> <p>This function reads the emulated environment used by - <seealso marker="os:getenv/1"><c>os:getenv/1</c></seealso> and not + <seealso marker="kernel:os#getenv/1"><c>os:getenv/1</c></seealso> and not the environment used by libc's <c>getenv(3)</c> or similar. Drivers that <em>require</em> that these are in sync will need to do so themselves, but keep in mind that they are segregated for a reason; @@ -2656,7 +2656,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </note> <warning> <p>This function modifies the emulated environment used by - <seealso marker="os:putenv/2"><c>os:putenv/2</c></seealso> and not + <seealso marker="kernel:os#putenv/2"><c>os:putenv/2</c></seealso> and not the environment used by libc's <c>putenv(3)</c> or similar. Drivers that <em>require</em> that these are in sync will need to do so themselves, but keep in mind that they are segregated for a reason; @@ -2849,7 +2849,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] <desc> <marker id="erl_drv_set_os_pid"></marker> <p>Sets the <c>os_pid</c> seen when doing - <seealso marker="erlang:port_info/2"> + <seealso marker="erlang#port_info/2"> <c>erlang:port_info/2</c></seealso> on this port.</p> <p><c>port</c> is the port handle of the port (driver instance) to set the pid on. <c>pid</c>is the pid to set.</p> @@ -3204,7 +3204,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] <c>control</c></seealso> driver entry function will return data to the port owner process. (The <c>control</c> function is called from - <seealso marker="erlang:port_control/3"> + <seealso marker="erlang#port_control/3"> <c>erlang:port_control/3</c></seealso>.)</p> <p>Currently there are only two meaningful values for <c>flags</c>: <c>0</c> means that data is returned in a list, diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index 4721747097..6b7a6db943 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -399,7 +399,7 @@ <tcaption>REFERENCE_EXT</tcaption></table> <p> Encodes a reference object (an object generated with - <seealso marker="erlang:make_ref/0">erlang:make_ref/0</seealso>). + <seealso marker="erlang#make_ref/0">erlang:make_ref/0</seealso>). The <c>Node</c> term is an encoded atom, that is, <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso>, <seealso marker="#SMALL_ATOM_UTF8_EXT"><c>SMALL_ATOM_UTF8_EXT</c></seealso>, or @@ -437,7 +437,7 @@ <tcaption>PORT_EXT</tcaption></table> <p> Encodes a port object (obtained from - <seealso marker="erlang:open_port/2"> + <seealso marker="erlang#open_port/2"> <c>erlang:open_port/2</c></seealso>). The <c>ID</c> is a node-specific identifier for a local port. Port operations are not allowed across node boundaries. @@ -467,7 +467,7 @@ <tcaption>PID_EXT</tcaption></table> <p> Encodes a process identifier object (obtained from - <seealso marker="erlang:spawn/3"><c>erlang:spawn/3</c></seealso> or + <seealso marker="erlang#spawn/3"><c>erlang:spawn/3</c></seealso> or friends). The <c>ID</c> and <c>Creation</c> fields works just like in <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>, while the <c>Serial</c> field is used to improve safety. diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index cf1994887a..c0be715678 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -546,8 +546,8 @@ int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail, <p>Many operations communicating with a process executing a dirty NIF can, however, complete while it executes the dirty NIF. For example, retrieving information about it through - <seealso marker="erlang:process_info/1"> - <c>erlang:process_info</c></seealso>, setting its group leader, + <seealso marker="erlang#process_info/1"> + <c>process_info</c></seealso>, setting its group leader, register/unregister its name, and so on.</p> <p>Termination of a process executing a dirty NIF can only be completed up to a certain point while it executes the dirty NIF. diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 0d94f83493..2b444ccf01 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -126,7 +126,7 @@ instance.</p> <p>One can get an approximation of the <c>native</c> time unit by calling - <seealso marker="erlang:convert_time_unit/3"> + <seealso marker="#convert_time_unit/3"> <c>erlang:convert_time_unit(1, second, native)</c></seealso>. The result equals the number of whole <c>native</c> time units per second. If @@ -4663,6 +4663,7 @@ RealSystem = system + MissedSystem</code> <name name="port_info" arity="2" clause_i="6" since="OTP R16B"/> <fsummary>Information about the memory size of a port.</fsummary> <desc> + <marker id="port_info_memory"/> <p><c><anno>Bytes</anno></c> is the total number of bytes allocated for this port by the runtime system. The port itself can have allocated memory that is not @@ -5387,6 +5388,7 @@ RealSystem = system + MissedSystem</code> </item> <tag><c>{memory, <anno>Size</anno>}</c></tag> <item> + <marker id="process_info_memory"/> <p><c><anno>Size</anno></c> is the size in bytes of the process. This includes call stack, heap, and internal structures.</p> </item> @@ -6676,7 +6678,7 @@ lists:map( <tag><c>async</c></tag> <item>Async threads are used by various linked-in drivers (mainly the file drivers) do offload non-CPU intensive work. See - <seealso marker="erts:erl#+async_thread_pool_size">erl +A</seealso> for more details.</item> + <seealso marker="erts:erl#async_thread_pool_size">erl +A</seealso> for more details.</item> <tag><c>aux</c></tag> <item>Takes care of any work that is not specifically assigned to a scheduler.</item> diff --git a/erts/doc/src/internal.xml b/erts/doc/src/internal.xml new file mode 100644 index 0000000000..88609d492a --- /dev/null +++ b/erts/doc/src/internal.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE part SYSTEM "part.dtd"> + +<internal xmlns:xi="http://www.w3.org/2001/XInclude"> + <header> + <copyright> + <year>2018</year><year>2018</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + </legalnotice> + + <title>ERTS Internal Documentation</title> + <prepared>Lukas Larsson</prepared> + <docno></docno> + <date>2018-07-07</date> + <rev>4.5.2</rev> + <file>internal.xml</file> + </header> + <xi:include href="CarrierMigration.xml"/> + <xi:include href="ThreadProgress.xml"/> + <xi:include href="CodeLoading.xml"/> + <xi:include href="Tracing.xml"/> + <xi:include href="DelayedDealloc.xml"/> + <xi:include href="beam_makeops.xml"/> + <xi:include href="CountingInstructions.xml"/> + <xi:include href="GarbageCollection.xml"/> + <xi:include href="PTables.xml"/> + <xi:include href="PortSignals.xml"/> + <xi:include href="ProcessManagementOptimizations.xml"/> + <xi:include href="SuperCarrier.xml"/> +</internal> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 7c5a8aefad..13cd4129ac 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -8363,8 +8363,7 @@ Erlang/OTP has been ported to the realtime operating system OSE. The port supports both smp and non-smp emulator. For details around the port and how to started - see the User's Guide in the <seealso - marker="ose:ose_intro">ose</seealso> application. </p> + see the User's Guide in the <em>ose</em> application.</p> <p> Note that not all parts of Erlang/OTP has been ported. </p> <p> @@ -9470,9 +9469,9 @@ <c>fix_alloc</c> allocator, a different strategy for management of fix blocks will be used.</item> <item>The information returned from <seealso - marker="erlang:system_info_allocator_tuple"><c>erlang:system_info({allocator, + marker="erlang#system_info_allocator_tuple"><c>erlang:system_info({allocator, A})</c></seealso>, and <seealso - marker="erlang:system_info_allocator_sizes"><c>erlang:system_info({allocator_sizes, + marker="erlang#system_info_allocator_sizes"><c>erlang:system_info({allocator_sizes, A})</c></seealso> will be slightly different when this feature has been enabled. An <c>mbcs_pool</c> tuple will be present giving information about abandoned carriers, @@ -10281,8 +10280,7 @@ information about signal ordering guarantees, see the chapter on <seealso marker="erts:communication">communication</seealso> in - the ERTS user's guide. The <seealso - marker="erts:erl#+n">+n</seealso> command line flag of + the ERTS user's guide. The <c>+n</c> command line flag of <seealso marker="erts:erl">erl(1)</seealso> can be helpful when trying to find signaling order bugs in Erlang code that have been exposed by these diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 8bbe6450eb..456b6fdac0 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -775,19 +775,25 @@ void init_dist(void) static ERTS_INLINE ErtsDistOutputBuf * alloc_dist_obuf(Uint size, Uint headers) { - int i; + Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers); ErtsDistOutputBuf *obuf; - Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers) + - sizeof(byte)*size; - Binary *bin = erts_bin_drv_alloc(obuf_size); - obuf = (ErtsDistOutputBuf *) &bin->orig_bytes[size]; + Binary *bin; + byte *extp; + int i; + + bin = erts_bin_drv_alloc(obuf_size + size); erts_refc_add(&bin->intern.refc, headers - 1, 1); + + obuf = (ErtsDistOutputBuf *)&bin->orig_bytes[0]; + extp = (byte *)&bin->orig_bytes[obuf_size]; + for (i = 0; i < headers; i++) { obuf[i].bin = bin; - obuf[i].extp = (byte *)&bin->orig_bytes[0]; + obuf[i].extp = extp; #ifdef DEBUG obuf[i].dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN; - obuf[i].alloc_endp = obuf->extp + size; + obuf[i].ext_startp = extp; + obuf[i].alloc_endp = &extp[size]; ASSERT(bin == ErtsDistOutputBuf2Binary(obuf)); #endif } @@ -1360,7 +1366,7 @@ erts_dist_seq_tree_foreach_delete_yielding(DistSeqNode **root, limit); if (res > 0) { if (ysp != &ys) - erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp); + erts_free(ERTS_ALC_T_SEQ_YIELD_STATE, ysp); *vyspp = NULL; } else { @@ -2341,7 +2347,8 @@ erts_dsig_send(ErtsDSigSendContext *ctx) (ctx->fragments-1) * ERTS_DIST_FRAGMENT_HEADER_SIZE, ctx->fragments); ctx->obuf->ext_start = &ctx->obuf->extp[0]; - ctx->obuf->ext_endp = &ctx->obuf->extp[0] + ctx->max_finalize_prepend + ctx->dhdr_ext_size; + ctx->obuf->ext_endp = &ctx->obuf->extp[0] + ctx->max_finalize_prepend + + ctx->dhdr_ext_size; /* Encode internal version of dist header */ ctx->obuf->extp = erts_encode_ext_dist_header_setup( @@ -2380,8 +2387,8 @@ erts_dsig_send(ErtsDSigSendContext *ctx) case ERTS_DSIG_SEND_PHASE_FIN: { ASSERT(ctx->obuf->extp < ctx->obuf->ext_endp); - ASSERT(((byte*)&ctx->obuf->bin->orig_bytes[0]) <= ctx->obuf->extp - ctx->max_finalize_prepend); - ASSERT(ctx->obuf->ext_endp <= ((byte*)ctx->obuf->bin->orig_bytes) + ctx->data_size + ctx->dhdr_ext_size); + ASSERT(ctx->obuf->ext_startp <= ctx->obuf->extp - ctx->max_finalize_prepend); + ASSERT(ctx->obuf->ext_endp <= (byte*)ctx->obuf->ext_startp + ctx->data_size + ctx->dhdr_ext_size); ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp; @@ -3457,6 +3464,7 @@ dist_ctrl_get_data_1(BIF_ALIST_1) pb->bytes = (byte*) obuf->extp; pb->flags = 0; res = make_binary(pb); + hp += PROC_BIN_SIZE; } else { hp = HAlloc(BIF_P, PROC_BIN_SIZE * 2 + 4 + hsz); pb = (ProcBin *) (char *) hp; diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index a7424bbcb8..39d42d9757 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2579,6 +2579,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) /* Need to be the only thread running... */ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_P->scheduler_data->current_process = NULL; erts_thr_progress_block(); if (BIF_ARG_1 == am_info) @@ -2592,6 +2593,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) erts_thr_progress_unblock(); erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_P->scheduler_data->current_process = BIF_P; ASSERT(dsbufp && dsbufp->str); res = new_binary(BIF_P, (byte *) dsbufp->str, dsbufp->str_len); diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index c434926142..fc3e117463 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -95,6 +95,7 @@ enum dist_entry_state { struct ErtsDistOutputBuf_ { #ifdef DEBUG Uint dbg_pattern; + byte *ext_startp; byte *alloc_endp; #endif ErtsDistOutputBuf *next; diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index 4e9f177e51..f58a606d57 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -1019,6 +1019,8 @@ send_gen_exit_signal(Process *c_p, Eterm from_tag, ref_sz = size_object(ref); hsz += ref_sz; + reason_sz = 0; /* Set to silence gcc warning */ + /* The reason was part of the control message, just use copy it into the xsigd */ if (is_value(reason)) { diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 9e662632b4..2b45d2d353 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -12141,10 +12141,9 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds) reason); switch (code) { case ERTS_DSIG_SEND_CONTINUE: + case ERTS_DSIG_SEND_YIELD: erts_set_gc_state(c_p, 0); ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx); - /* fall-through */ - case ERTS_DSIG_SEND_YIELD: break; case ERTS_DSIG_SEND_OK: break; @@ -12388,11 +12387,10 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds) reason, SEQ_TRACE_TOKEN(c_p)); switch (code) { + case ERTS_DSIG_SEND_YIELD: case ERTS_DSIG_SEND_CONTINUE: erts_set_gc_state(c_p, 0); ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx); - /* fall-through */ - case ERTS_DSIG_SEND_YIELD: break; case ERTS_DSIG_SEND_OK: break; diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 471c1c3938..fa8314c115 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -699,6 +699,7 @@ dist_ext_size(ErtsDistExternal *edep) } else { sz -= sizeof(ErtsAtomTranslationTable); } + ASSERT(sz % 4 == 0); return sz; } @@ -706,8 +707,9 @@ Uint erts_dist_ext_size(ErtsDistExternal *edep) { Uint sz = dist_ext_size(edep); + sz += 4; /* may need to pad to 8-byte-align ErtsDistExternalData */ sz += edep->data[0].frag_id * sizeof(ErtsDistExternalData); - return sz + ERTS_EXTRA_DATA_ALIGN_SZ(sz); + return sz; } Uint @@ -749,6 +751,8 @@ erts_make_dist_ext_copy(ErtsDistExternal *edep, ErtsDistExternal *new_edep) erts_ref_dist_entry(new_edep->dep); ep += dist_ext_sz; + ep += (UWord)ep & 4; /* 8-byte alignment for ErtsDistExternalData */ + ASSERT((UWord)ep % 8 == 0); new_edep->data = (ErtsDistExternalData*)ep; sys_memzero(new_edep->data, sizeof(ErtsDistExternalData) * edep->data->frag_id); diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index 396cd9f802..f2cc9bf98f 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -144,14 +144,6 @@ typedef struct erl_dist_external { ErtsAtomTranslationTable attab; } ErtsDistExternal; -#define ERTS_DIST_EXT_SIZE(EDEP) \ - (sizeof(ErtsDistExternal) \ - - (((EDEP)->flags & ERTS_DIST_EXT_ATOM_TRANS_TAB) \ - ? (ASSERT(0 <= (EDEP)->attab.size \ - && (EDEP)->attab.size <= ERTS_ATOM_CACHE_SIZE), \ - sizeof(Eterm)*(ERTS_ATOM_CACHE_SIZE - (EDEP)->attab.size)) \ - : sizeof(ErtsAtomTranslationTable))) - typedef struct { byte *extp; int exttmp; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 7a125b0f67..10ca74cd60 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1690,9 +1690,14 @@ i_plus S1=c S2=c Fail Dst => move S1 x | i_plus x S2 Fail Dst i_plus xy xyc j? d -i_minus x x j? d -i_minus c x j? d -i_minus s s j? d +# A minus instruction with a constant right operand will be +# converted to an i_increment instruction, except in guards or +# when the negated value of the constant won't fit in a guard. +# Therefore, it very rare. +i_minus S1 S2=c Fail Dst => move S2 x | i_minus S1 x Fail Dst + +i_minus xy xy j? d +i_minus c xy j? d i_times j? s s d diff --git a/erts/emulator/internal_doc/CarrierMigration.md b/erts/emulator/internal_doc/CarrierMigration.md index bb3d8aac28..40f6031ca8 100644 --- a/erts/emulator/internal_doc/CarrierMigration.md +++ b/erts/emulator/internal_doc/CarrierMigration.md @@ -1,6 +1,9 @@ Carrier Migration ================= +Introduction +------------ + The ERTS memory allocators manage memory blocks in two types of raw memory chunks. We call these chunks of raw memory *carriers*. Single-block carriers which only contain one large block, @@ -141,11 +144,11 @@ Since the carrier has been unlinked from the data structure of available free blocks, no more allocations will be made in the carrier. -The allocator instance that created a carrier is called its **owner**. +The allocator instance that created a carrier is called its *owner*. Ownership never changes. The allocator instance that has the responsibility to perform deallocations in a -carrier is called its **employer**. The employer may also perform allocations if +carrier is called its *employer*. The employer may also perform allocations if the carrier is not in the pool. Employment may change when a carrier is fetched from or inserted into the pool. @@ -153,14 +156,14 @@ Deallocations in a carrier, while it remains in the pool, is always performed the owner. That is, all pooled carriers are employed by their owners. Each carrier has an atomic word containing a pointer to the employing allocator -instance and three bit flags; IN_POOL, BUSY and HOMECOMING. +instance and three bit flags; IN\_POOL, BUSY and HOMECOMING. When fetching a carrier from the pool, employment may change and further deallocations in the carrier will be redirected to the new employer using the delayed dealloc functionality. When a foreign allocator instance abandons a carrier back into the pool, it will -also pass it back to its **owner** using the delayed dealloc queue. When doing +also pass it back to its *owner* using the delayed dealloc queue. When doing this it will set the HOMECOMING bit flag to mark it as "enqueued". The owner will later clear the HOMECOMING bit when the carrier is dequeued. This mechanism prevents a carrier from being enqueued again before it has been dequeued. @@ -180,14 +183,14 @@ back to the owner for deallocation using the delayed dealloc functionality. In short: -* The allocator instance that created a carrier **owns** it. -* An empty carrier is always deallocated by its **owner**. -* **Ownership** never changes. -* The allocator instance that uses a carrier **employs** it. -* An **employer** can abandon a carrier into the pool. +* The allocator instance that created a carrier *owns* it. +* An empty carrier is always deallocated by its *owner*. +* *Ownership* never changes. +* The allocator instance that uses a carrier *employs* it. +* An *employer* can abandon a carrier into the pool. * Pooled carriers are not allocated from. -* Pooled carriers are always **employed** by their **owner**. -* **Employment** can only change from **owner** to a foreign allocator +* Pooled carriers are always *employed* by their *owner*. +* *Employment* can only change from *owner* to a foreign allocator when a carrier is fetched from the pool. @@ -229,7 +232,7 @@ carrier. When the cluster gets to the same size as the search limit, all searches will essentially fail. To counter the "bad cluster" problem and also ease the contention, the -search will now always start by first looking at the allocators **own** +search will now always start by first looking at the allocators *own* carriers. That is, carriers that were initially created by the allocator itself and later had been abandoned to the pool. If none of our own abandoned carrier would do, then the search continues into the diff --git a/erts/emulator/internal_doc/CodeLoading.md b/erts/emulator/internal_doc/CodeLoading.md index 151b9cd57c..0b2e3070e7 100644 --- a/erts/emulator/internal_doc/CodeLoading.md +++ b/erts/emulator/internal_doc/CodeLoading.md @@ -45,7 +45,7 @@ free to schedule other work while the second loader is waiting. (See `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 +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 @@ -71,8 +71,8 @@ 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. +* "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 diff --git a/erts/emulator/internal_doc/GarbageCollection.md b/erts/emulator/internal_doc/GarbageCollection.md index 1d9e3f4160..a1627b3233 100644 --- a/erts/emulator/internal_doc/GarbageCollection.md +++ b/erts/emulator/internal_doc/GarbageCollection.md @@ -1,6 +1,6 @@ # Erlang Garbage Collector -Erlang manages dynamic memory with a [tracing garbage collector](https://en.wikipedia.org/wiki/Tracing_garbage_collection). More precisely a per process generational semi-space copying collector using [Cheney's](#cheney) copy collection algorithm together with a global large object space. +Erlang manages dynamic memory with a [tracing garbage collector](https://en.wikipedia.org/wiki/Tracing_garbage_collection). More precisely a per process generational semi-space copying collector using Cheney's copy collection algorithm together with a global large object space. (See C. J. Cheney in [References](#references).) ## Overview @@ -12,12 +12,11 @@ Terms are created on the heap by evaluating expressions. There are two major typ Let's look at an example that returns a tuple with the newly created data. -```erlang -data(Foo) -> - Cons = [42|Foo], - Literal = {text, "hello world!"}, - {tag, Cons, Literal}. -``` + + data(Foo) -> + Cons = [42|Foo], + Literal = {text, "hello world!"}, + {tag, Cons, Literal}. In this example we first create a new cons cell with an integer and a tuple with some text. Then a tuple of size three wrapping the other values with an atom tag is created and returned. @@ -25,7 +24,6 @@ On the heap tuples require a word size for each of its elements as well as for t Compiling this code to beam assembly (`erlc -S`) shows exactly what is happening. -```erlang ... {test_heap,6,1}. {put_list,{integer,42},{x,0},{x,1}}. @@ -34,9 +32,8 @@ Compiling this code to beam assembly (`erlc -S`) shows exactly what is happening {put,{x,1}}. {put,{literal,{text,"hello world!"}}}. return. -``` -Looking at the assembler code we can see three things; The heap requirement in this function turns out to be only six words, as seen by the `{test_heap,6,1}` instruction. All the allocations are combined to a single instruction. The bulk of the data `{text, "hello world!"}` is a *literal*. Literals, sometimes referred to as constants, are not allocated in the function since they are a part of the module and allocated at load time. +Looking at the assembler code we can see three things: The heap requirement in this function turns out to be only six words, as seen by the `{test_heap,6,1}` instruction. All the allocations are combined to a single instruction. The bulk of the data `{text, "hello world!"}` is a *literal*. Literals, sometimes referred to as constants, are not allocated in the function since they are a part of the module and allocated at load time. If there is not enough space available on the heap to satisfy the `test_heap` instructions request for memory, then a garbage collection is initiated. It may happen immediately in the `test_heap` instruction, or it can be delayed until a later time depending on what state the process is in. If the garbage collection is delayed, any memory needed will be allocated in heap fragments. Heap fragments are extra memory blocks that are a part of the young heap, but are not allocated in the contigious area where terms normally reside. See [The young heap](#the-young-heap) for more details. @@ -50,11 +47,9 @@ It follows all the pointers from the root-set to the heap and copies each term w After the header word has been copied a [*move marker*](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.h#L45-L46) is destructively placed in it pointing to the term in the *to space*. Any other term that points to the already moved term will [see this move marker](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L1125) and copy the referring pointer instead. For example, if the have the following Erlang code: -```erlang -foo(Arg) -> - T = {test, Arg}, - {wrapper, T, T, T}. -``` + foo(Arg) -> + T = {test, Arg}, + {wrapper, T, T, T}. Only one copy of T exists on the heap and during the garbage collection only the first time T is encountered will it be copied. @@ -86,15 +81,15 @@ In the next garbage collection, any pointers to the old heap will be ignored and Generational garbage collection aims to increase performance at the expense of memory. This is achieved because only the young, smaller, heap is considered in most garbage collections. -The generational [hypothesis](#ungar) predicts that most terms tend to die young, and for an immutable language such as Erlang, young terms die even faster than in other languages. So for most usage patterns the data in the new heap will die very soon after it is allocated. This is good because it limits the amount of data copied to the old heap and also because the garbage collection algorithm used is proportional to the amount of live data on the heap. +The generational hypothesis predicts that most terms tend to die young (see D. Ungar in [References](#references)), and for an immutable language such as Erlang, young terms die even faster than in other languages. So for most usage patterns the data in the new heap will die very soon after it is allocated. This is good because it limits the amount of data copied to the old heap and also because the garbage collection algorithm used is proportional to the amount of live data on the heap. One critical issue to note here is that any term on the young heap can reference terms on the old heap but *no* term on the old heap may refer to a term on the young heap. This is due to the nature of the copy algorithm. Anything referenced by an old heap term is not included in the reference tree, root-set and its followers, and hence is not copied. If it was, the data would be lost, fire and brimstone would rise to cover the earth. Fortunately, this comes naturally for Erlang because the terms are immutable and thus there can be no pointers modified on the old heap to point to the young heap. -To reclaim data from the old heap, both young and old heaps are included during the collection and copied to a common *to space*. Both the *from space* of the young and old heap are then deallocated and the procedure will start over from the beginning. This type of garbage collection is called a full sweep and is triggered when the size of the area under the high-watermark is larger than the size of the free area of the old heap. It can also be triggered by doing a manual call to [erlang:garbage_collect()](http://erlang.org/doc/man/erlang.html#garbage_collect-0), or by running into the young garbage collection limit set by [spawn_opt(fun(),[{fullsweep_after, N}])](http://erlang.org/doc/man/erlang.html#spawn_opt-4) where N is the number of young garbage collections to do before forcing a garbage collection of both young and old heap. +To reclaim data from the old heap, both young and old heaps are included during the collection and copied to a common *to space*. Both the *from space* of the young and old heap are then deallocated and the procedure will start over from the beginning. This type of garbage collection is called a full sweep and is triggered when the size of the area under the high-watermark is larger than the size of the free area of the old heap. It can also be triggered by doing a manual call to [erlang:garbage_collect()](http://erlang.org/doc/man/erlang.html#garbage_collect-0), or by running into the young garbage collection limit set by [spawn\_opt(fun(),[{fullsweep\_after, N}\])](http://erlang.org/doc/man/erlang.html#spawn_opt-4) where N is the number of young garbage collections to do before forcing a garbage collection of both young and old heap. ## The young heap -The young heap, or the allocation heap, consists of the stack and heap as described in the Overview. However, it also includes any heap fragments that are attached to the heap. All of the heap fragments are considered to be above the high-watermark and part of the young generation. Heap fragments contain terms that either did not fit on the heap, or were created by another process and then attached to the heap. For instance if the bif binary_to_term created a term which does not fit on the current heap without doing a garbage collection, it will create a heap-fragment for the term and then schedule a garbage collection for later. Also if a message is sent to the process, the payload may be placed in a heap-fragment and that fragment is added to young heap when the message is matched in a receive clause. +The young heap, or the allocation heap, consists of the stack and heap as described in the Overview. However, it also includes any heap fragments that are attached to the heap. All of the heap fragments are considered to be above the high-watermark and part of the young generation. Heap fragments contain terms that either did not fit on the heap, or were created by another process and then attached to the heap. For instance if the bif `binary_to_term/1` created a term which does not fit on the current heap without doing a garbage collection, it will create a heap-fragment for the term and then schedule a garbage collection for later. Also if a message is sent to the process, the payload may be placed in a heap-fragment and that fragment is added to young heap when the message is matched in a receive clause. This procedure differs from how it worked prior to Erlang/OTP 19.0. Before 19.0, only a contiguous memory block where the young heap and stack resided was considered to be part of the young heap. Heap fragments and messages were immediately copied into the young heap before they could be inspected by the Erlang program. The behaviour introduced in 19.0 is superior in many ways - most significantly it reduces the number of necessary copy operations and the root set for garbage collection. @@ -118,21 +113,19 @@ The old heap is always one step ahead in the heap growth stages than the young h When garbage collecting a heap (young or old) all literals are left in place and not copied. To figure out if a term should be copied or not when doing a garbage collection the following pseudo code is used: -```c -if (erts_is_literal(ptr) || (on_old_heap(ptr) && !fullsweep)) { - /* literal or non fullsweep - do not copy */ -} else { - copy(ptr); -} -``` + if (erts_is_literal(ptr) || (on_old_heap(ptr) && !fullsweep)) { + /* literal or non fullsweep - do not copy */ + } else { + copy(ptr); + } The [`erts_is_literal`](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/global.h#L1452-L1465) check works differently on different architectures and operating systems. -On 64 bit systems that allow mapping of unreserved virtual memory areas (most operating systems except Windows), an area of size 1 GB (by default) is mapped and then all literals are placed within that area. Then all that has to be done to determine if something is a literal or not is [two quick pointer checks](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L322-L324). This system relies on the fact that a memory page that has not been touched yet does not take any actual space. So even if 1 GB of virtual memory is mapped, only the memory which is actually needed for literals is allocated in ram. The size of the literal area is configurable through the +MIscs erts_alloc option. +On 64 bit systems that allow mapping of unreserved virtual memory areas (most operating systems except Windows), an area of size 1 GB (by default) is mapped and then all literals are placed within that area. Then all that has to be done to determine if something is a literal or not is [two quick pointer checks](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L322-L324). This system relies on the fact that a memory page that has not been touched yet does not take any actual space. So even if 1 GB of virtual memory is mapped, only the memory which is actually needed for literals is allocated in ram. The size of the literal area is configurable through the +MIscs erts\_alloc option. On 32 bit systems, there is not enough virtual memory space to allocate 1 GB for just literals, so instead small 256 KB sized literal regions are created on demand and a card mark bit-array of the entire 32 bit memory space is then used to determine if a term is a literal or not. Since the total memory space is only 32 bits, the card mark bit-array is only 256 words large. On a 64 bit system the same bit-array would have to be 1 tera words large, so this technique is only viable on 32 bit systems. Doing [lookups in the array](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L316-L319) is a little more expensive then just doing the pointer checks that can be done in 64 bit systems, but not extremely so. -On 64 bit windows, on which erts_alloc cannot do unreserved virtual memory mappings, a [special tag](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L59) within the Erlang term object is used to determine if something [is a literal or not](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L248-L252). This is very cheap, however, the tag is only available on 64 bit machines, and it is possible to do a great deal of other nice optimizations with this tag in the future (like for instance a more compact list implementation) so it is not used on operating systems where it is not needed. +On 64 bit windows, on which erts\_alloc cannot do unreserved virtual memory mappings, a [special tag](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L59) within the Erlang term object is used to determine if something [is a literal or not](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L248-L252). This is very cheap, however, the tag is only available on 64 bit machines, and it is possible to do a great deal of other nice optimizations with this tag in the future (like for instance a more compact list implementation) so it is not used on operating systems where it is not needed. This behaviour is different from how it worked prior to Erlang/OTP 19.0. Before 19.0 the literal check was done by checking if the pointer pointed to the young or old heap block. If it did not, then it was considered a literal. This lead to considerable overhead and strange memory usage scenarios, so it was removed in 19.0. @@ -182,6 +175,8 @@ Using `on_heap` will force all messages to be part of on the young heap which wi Which one of these strategies is best depends a lot on what the process is doing and how it interacts with other processes. So, as always, profile the application and see how it behaves with the different options. - <a name="cheney">[1]</a>: C. J. Cheney. A nonrecursive list compacting algorithm. Commun. ACM, 13(11):677–678, Nov. 1970. +## References + +C. J. Cheney. A nonrecursive list compacting algorithm. Commun. ACM, 13(11):677–678, Nov. 1970. - <a name="ungar">[2]</a>: D. Ungar. Generation scavenging: A non-disruptive high performance storage reclamation algorithm. SIGSOFT Softw. Eng. Notes, 9(3):157–167, Apr. 1984. +D. Ungar. Generation scavenging: A non-disruptive high performance storage reclamation algorithm. SIGSOFT Softw. Eng. Notes, 9(3):157–167, Apr. 1984. diff --git a/erts/emulator/internal_doc/PTables.md b/erts/emulator/internal_doc/PTables.md index 6fe0e7665d..ef61963a40 100644 --- a/erts/emulator/internal_doc/PTables.md +++ b/erts/emulator/internal_doc/PTables.md @@ -85,13 +85,13 @@ following: 3. Depending on use, issue appropriate memory barrier. A common barrier used is a barrier with acquire semantics. On - x86/x86_64 this maps to a compiler barrier preventing the compiler + x86/x86\_64 this maps to a compiler barrier preventing the compiler to reorder instructions, but on other hardware often some kind of light weight hardware memory barrier is also needed. When comparing with a locked approach, at least one heavy weight memory barrier will be issued when locking the lock on most, if - not all, hardware architectures (including x86/x86_64), and often + not all, hardware architectures (including x86/x86\_64), and often some kind of light weight memory barrier will be issued when unlocking the lock. diff --git a/erts/emulator/internal_doc/SuperCarrier.md b/erts/emulator/internal_doc/SuperCarrier.md index acf722ea37..f52c6613d5 100644 --- a/erts/emulator/internal_doc/SuperCarrier.md +++ b/erts/emulator/internal_doc/SuperCarrier.md @@ -5,7 +5,7 @@ A super carrier is large memory area, allocated at VM start, which can be used during runtime to allocate normal carriers from. The super carrier feature was introduced in OTP R16B03. It is -enabled with command line option +MMscs <size in Mb> +enabled with command line option +MMscs <size in Mb> and can be configured with other options. Problem @@ -65,7 +65,7 @@ carrier is full. ### Implementation ### -The entire super carrier implementation is kept in erl_mmap.c. The +The entire super carrier implementation is kept in erl\_mmap.c. The name suggest that it can be viewed as our own mmap implementation. A super carrier needs to satisfy two slightly different kinds of @@ -98,8 +98,8 @@ other. ### Data structures ### -The MBC area is called **sa** as in super aligned and the SBC area is -called **sua** as in super un-aligned. +The MBC area is called *sa* as in super aligned and the SBC area is +called *sua* as in super un-aligned. Note that the "super" in super alignment and the "super" in super carrier has nothing to do with each other. We could have choosen @@ -128,7 +128,7 @@ down or up. We need to keep track of all the free segments in order to reuse them for new carrier allocations. One initial idea was to use the same mechanism that is used to keep track of free blocks within MBCs -(alloc_util and the different strategies). However, that would not be +(alloc\_util and the different strategies). However, that would not be as straight forward as one can think and can also waste quite a lot of memory as it uses prepended block headers. The granularity of the super carrier is one memory page (usually 4kb). We want to allocate diff --git a/erts/emulator/internal_doc/Tracing.md b/erts/emulator/internal_doc/Tracing.md index 7f97f64765..196ae0dd4e 100644 --- a/erts/emulator/internal_doc/Tracing.md +++ b/erts/emulator/internal_doc/Tracing.md @@ -37,6 +37,7 @@ 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 @@ -72,6 +73,7 @@ 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. @@ -82,7 +84,7 @@ through when adding a new breakpoint. instruction word in the breakpoint. 3. Write a pointer to the breakpoint at offset -4 from the first - instruction "func_info" header. + instruction "func\_info" header. 4. Set the staging part of the breakpoint as enabled with specified breakpoint data. @@ -139,7 +141,7 @@ and removing breakpoints. 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. + "func\_info" header at offset -4. 3. Update the staging part of all affected breakpoints. Disable breakpoints that are to be removed. diff --git a/erts/emulator/internal_doc/beam_makeops.md b/erts/emulator/internal_doc/beam_makeops.md index 1da8d2ab05..2880099b70 100644 --- a/erts/emulator/internal_doc/beam_makeops.md +++ b/erts/emulator/internal_doc/beam_makeops.md @@ -403,7 +403,7 @@ A line with `//` is also a comment. It is recommended to only use this style of comments in files that define implementations of instructions. -A long line can be broken into shorter lines by a placing a`\` before +A long line can be broken into shorter lines by a placing a `\` before the newline. ### Variable definitions ### @@ -1159,7 +1159,6 @@ implementation of `gen_element()`: return op; } -} ### Defining the implementation ### @@ -1452,7 +1451,7 @@ optionally additional heap space. ##### The NEXT_INSTRUCTION pre-bound variable ##### -The NEXT_INSTRUCTION is a pre-bound variable that is available in +The NEXT\_INSTRUCTION is a pre-bound variable that is available in all instructions. It expands to the address of the next instruction. Here is an example: @@ -1545,7 +1544,7 @@ register, the pointer will no longer be valid. (Y registers are stored on the stack.) In those circumstances, `$REFRESH_GEN_DEST()` must be invoked -to set up the pointer again. **beam\_makeops** will notice +to set up the pointer again. **beam\_makeops** will notice if there is a call to a function that does a garbage collection and `$REFRESH_GEN_DEST()` is not called. diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 80e8030d74..98be50815c 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -842,6 +842,8 @@ driver_select(ErlDrvPort ix, ErlDrvEvent e, int mode, int on) ret = 0; goto done_unknown; } + /* For some reason (don't know why), we do not clean all + events when doing ERL_DRV_USE_NO_CALLBACK. */ else if ((mode&ERL_DRV_USE_NO_CALLBACK) == ERL_DRV_USE) { mode |= (ERL_DRV_READ | ERL_DRV_WRITE); } @@ -2491,6 +2493,10 @@ drvmode2str(int mode) { case ERL_DRV_WRITE|ERL_DRV_USE: return "WRITE|USE"; case ERL_DRV_READ|ERL_DRV_WRITE|ERL_DRV_USE: return "READ|WRITE|USE"; case ERL_DRV_USE: return "USE"; + case ERL_DRV_READ|ERL_DRV_USE_NO_CALLBACK: return "READ|USE_NO_CB"; + case ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK: return "WRITE|USE_NO_CB"; + case ERL_DRV_READ|ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK: return "READ|WRITE|USE_NO_CB"; + case ERL_DRV_USE_NO_CALLBACK: return "USE_NO_CB"; case ERL_DRV_READ: return "READ"; case ERL_DRV_WRITE: return "WRITE"; case ERL_DRV_READ|ERL_DRV_WRITE: return "READ|WRITE"; diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 042a091db1..664d677ebd 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -1006,10 +1006,8 @@ static void clear_fd_data(ErtsSysFdData *fdd) static void nbio_stop_fd(ErlDrvPort prt, ErtsSysFdData *fdd, int use) { - driver_select(prt, abs(fdd->fd), use ? ERL_DRV_USE_NO_CALLBACK : 0|DO_READ|DO_WRITE, 0); clear_fd_data(fdd); SET_BLOCKING(abs(fdd->fd)); - } static void fd_stop(ErlDrvData ev) /* Does not close the fds */ @@ -1026,10 +1024,12 @@ static void fd_stop(ErlDrvData ev) /* Does not close the fds */ if (dd->ifd) { sz += sizeof(ErtsSysFdData); + driver_select(prt, abs(dd->ifd->fd), ERL_DRV_USE_NO_CALLBACK|DO_READ|DO_WRITE, 0); nbio_stop_fd(prt, dd->ifd, 1); } if (dd->ofd && dd->ofd != dd->ifd) { sz += sizeof(ErtsSysFdData); + driver_select(prt, abs(dd->ofd->fd), ERL_DRV_USE_NO_CALLBACK|DO_WRITE, 0); nbio_stop_fd(prt, dd->ofd, 1); } diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 449821e5ad..58194cf167 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -39,6 +39,8 @@ -define(Line,). -export([all/0, suite/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, ping/1, bulk_send_small/1, group_leader/1, optimistic_dflags/1, @@ -119,6 +121,28 @@ groups() -> message_latency_large_exit2]} ]. +init_per_suite(Config) -> + {ok, Apps} = application:ensure_all_started(os_mon), + [{started_apps, Apps} | Config]. + +end_per_suite(Config) -> + Apps = proplists:get_value(started_apps, Config), + [application:stop(App) || App <- lists:reverse(Apps)], + Config. + +init_per_group(message_latency, Config) -> + Free = free_memory(), + if Free < 2048 -> + {skip, "Not enough memory"}; + true -> + Config + end; +init_per_group(_, Config) -> + Config. + +end_per_group(_, Config) -> + Config. + %% Tests pinging a node in different ways. ping(Config) when is_list(Config) -> Times = 1024, @@ -2845,3 +2869,23 @@ uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 -> Uint band 16#ff; uint8(Uint) -> exit({badarg, uint8, [Uint]}). + +free_memory() -> + %% Free memory in MB. + try + SMD = memsup:get_system_memory_data(), + {value, {free_memory, Free}} = lists:keysearch(free_memory, 1, SMD), + TotFree = (Free + + case lists:keysearch(cached_memory, 1, SMD) of + {value, {cached_memory, Cached}} -> Cached; + false -> 0 + end + + case lists:keysearch(buffered_memory, 1, SMD) of + {value, {buffered_memory, Buffed}} -> Buffed; + false -> 0 + end), + TotFree div (1024*1024) + catch + error : undef -> + ct:fail({"os_mon not built"}) + end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index bb0f3498ab..cbed71cedd 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -998,7 +998,9 @@ chkio_test({erts_poll_info, Before}, During = get_check_io_total(erlang:system_info(check_io)), erlang:display(During), - 0 = element(1, erts_debug:get_internal_state(check_io_debug)), + [0 = element(1, erts_debug:get_internal_state(check_io_debug)) || + %% The pollset is not stable when running the fallback testcase + Test /= ?CHKIO_USE_FALLBACK_POLLSET], io:format("During test: ~p~n", [During]), chk_chkio_port(Port), case erlang:port_control(Port, ?CHKIO_STOP, "") of diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index ae3099633a..d278ac86c7 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -93,25 +93,34 @@ wall_clock_zero_diff1(0) -> %% statistics(wall_clock) are compatible, and are within a small number %% of ms of the amount of real time we waited for. wall_clock_update(Config) when is_list(Config) -> - wall_clock_update1(6). + N = 10, + Inc = 200, + TotalTime = wall_clock_update1(N, Inc, 0), + Overhead = TotalTime - N * Inc, + IsDebug = test_server:is_debug(), -wall_clock_update1(N) when N > 0 -> - {T1_wc_time, _} = statistics(wall_clock), - receive after 1000 -> ok end, - {T2_wc_time, Wc_Diff} = statistics(wall_clock), - - Wc_Diff = T2_wc_time - T1_wc_time, - io:format("Wall clock diff = ~p; should be = 1000..1040~n", [Wc_Diff]), - case test_server:is_debug() of - false -> - true = Wc_Diff =< 1040; + %% Check that the average overhead is reasonable. + if + Overhead < N * 100 -> + ok; + IsDebug, Overhead < N * 1000 -> + ok; true -> - true = Wc_Diff =< 2000 %Be more tolerant in debug-compiled emulator. - end, - true = Wc_Diff >= 1000, - wall_clock_update1(N-1); -wall_clock_update1(0) -> - ok. + io:format("There was an overhead of ~p ms during ~p rounds.", + [Overhead,N]), + ct:fail(too_much_overhead) + end. + +wall_clock_update1(N, Inc, Total) when N > 0 -> + {Time1, _} = statistics(wall_clock), + receive after Inc -> ok end, + {Time2, WcDiff} = statistics(wall_clock), + WcDiff = Time2 - Time1, + io:format("Wall clock diff = ~p (expected at least ~p)\n", [WcDiff,Inc]), + true = WcDiff >= Inc, + wall_clock_update1(N-1, Inc, Total + WcDiff); +wall_clock_update1(0, _, Total) -> + Total. %%% Test statistics(runtime). |