diff options
Diffstat (limited to 'erts')
62 files changed, 1870 insertions, 1613 deletions
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 5ca387ffd8..6bc242c246 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,331 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 10.5</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If you set <c>{linger,{true,0}}</c> on a <c>gen_tcp</c> + listen socket, accept a connection on that socket, and + then close the accepted socket, now the linger zero + setting is transferred to the accepted socket. Before + this correction that information was lost and the close + behaviour on the accepted socket incorrect.</p> + <p> + Own Id: OTP-15370 Aux Id: ERIERL-353 </p> + </item> + <item> + <p> + Sending ancillary data implemented in OTP-15747 + accidentally left behind test code that caused all UDP + sends to fail on Windows. This has now been fixed.</p> + <p> + Own Id: OTP-15422 Aux Id: OTP-15747 </p> + </item> + <item> + <p> + In the socket nif, used invalid flags when if-def'ing for + supported TCP flags: TCP_MAXSEG and TCP_NODELAY (the + support function).</p> + <p> + Own Id: OTP-15827</p> + </item> + <item> + <p> + Fixed memory leaks in experimental socket module.</p> + <p> + Own Id: OTP-15830</p> + </item> + <item> + <p> + <c>re:run()</c> now yields when validating utf8 in a + large subject.</p> + <p> + Own Id: OTP-15836 Aux Id: ERL-876 </p> + </item> + <item> + <p> + Fixed bug in <c>seq_trace:set_token(label,Term)</c> which + could cause VM crash if <c>Term</c> was heap allocated + (not an atom, small integer, local pid or port). Bug + exists since OTP 21.0 when terms other than small + integers were first allowed as labels.</p> + <p> + Own Id: OTP-15849 Aux Id: ERL-700 </p> + </item> + <item> + <p> + Extra <c>-mode</c> flags given to <c>erl</c> are ignored + with a warning.</p> + <p> + Own Id: OTP-15852</p> + </item> + <item> + <p> + Don't loop indefinitely when <c>--enable-pgo</c> is given + to configure, but compiler does not support pgo.</p> + <p> + Own Id: OTP-15853 Aux Id: PR-2254 </p> + </item> + <item> + <p> + Fix <c>seq_trace:print/2</c> not to raise <c>badarg</c> + exception if label is not a small integer. Bug exists + since OTP 21.0.</p> + <p> + Own Id: OTP-15859 Aux Id: ERL-700 </p> + </item> + <item> + <p> + Fixed hipe_flush_icache_range for non-Linux OS on ARM.</p> + <p> + Own Id: OTP-15874 Aux Id: ERL-958, PR-2266 </p> + </item> + <item> + <p>The fix in OTP-15871 was too conservative and disabled + the offending load-time optimization in some cases where + it was safe.</p> + <p> + Own Id: OTP-15881</p> + </item> + <item> + <p> + Upgraded the ERTS internal PCRE library from version 8.42 + to version 8.43. See <url + href="http://pcre.org/original/changelog.txt">http://pcre.org/original/changelog.txt</url> + for information about changes made to PCRE. This library + implements major parts of the <seealso + marker="stdlib:re"><c>re</c></seealso> regular + expressions module.</p> + <p> + Own Id: OTP-15889</p> + </item> + <item> + <p> + Fix race condition when closing a socket while using + <c>{active,N}</c> on Windows.</p> + <p> + Own Id: OTP-15901 Aux Id: ERL-960 PR-2272 </p> + </item> + <item> + <p> + Allow more than one <c>-config</c> command line option to + <c>erl</c> on Windows to conform with other OS.</p> + <p> + Own Id: OTP-15918 Aux Id: ERL-912 </p> + </item> + <item> + <p> + Fix so that ERL_FLAGS environment variable does not + interfere with command line arguments. Before this fix + you could write:</p> + <p> + <c>ERL_FLAGS="10" erl +S</c></p> + <p> + and erlang would start as if <c>+S</c> had been given the + argument <c>10</c>.</p> + <p> + Own Id: OTP-15931</p> + </item> + <item> + <p> + The bug with ID ERL-717 has been fixed. The functions + <c>io:columns()</c> and <c>io:rows()</c> only worked + correctly inside interactive erlang shells before this + fix. These functions returned <c>{error,enotsup}</c> + before this fix even if stdout and stdin were connected + to a terminal when they were invoked from an escript or a + program started with e.g., <c>erl -noshell</c>.</p> + <p> + Own Id: OTP-15959 Aux Id: ERL-717 </p> + </item> + <item> + <p> + Do not use named label in <c>ethread.c</c> inline + assemble. This allows erts to be compiled using gcc 9.1.0 + with LTO enabled.</p> + <p> + Own Id: OTP-15971 Aux Id: PR-2333 </p> + </item> + <item> + <p><c>erlang:fun_to_list/1</c> will now escape the module + and function name when necessary.</p> + <p> + Own Id: OTP-15975 Aux Id: ERL-1009 </p> + </item> + <item> + <p><c>process_info(P,binary)</c> would neglect to look + through heap fragments, potentially missing a few + binaries associated with the process.</p> + <p> + Own Id: OTP-15978 Aux Id: ERIERL-366 </p> + </item> + <item> + <p> + HiPE is now automatically disabled on systems with + non-glibc implementation (for instance musl). This is + because musl does not provide the API's for guaranteeing + that signals are delivered on the correct native stack.</p> + <p> + Own Id: OTP-16037</p> + </item> + <item> + <p> + Fixed bug triggered if a process is killed during call to + <c>persistent_term:put</c> or + <c>persistent_term:erase</c>.</p> + <p> + Own Id: OTP-16041</p> + </item> + <item> + <p> + Add units to all memory slogans in the crash dump + documentation.</p> + <p> + Own Id: OTP-16042</p> + </item> + <item> + <p> + Fix a bug in <c>binary_to_term</c> that would crash the + emulator if a term larger than 16GB was to be decoded.</p> + <p> + Own Id: OTP-16058 Aux Id: PR-2382 </p> + </item> + <item> + <p> + Fixed bug related to an exiting process sending EXIT and + DOWN signals to remote linked/monitored processes. Bugs + exists since OTP 22.0.</p> + <p> + Own Id: OTP-16060</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p><c>erlc</c> can now automatically use a compile server + to avoid starting an Erlang system for each file to be + compiled in a multi-file project. See the documentation + for how to enable it.</p> + <p> + Own Id: OTP-15738 Aux Id: PR-2361 </p> + </item> + <item> + <p> + The possibility to send ancillary data, in particular the + TOS field, has been added to <c>gen_udp:send/4,5</c>.</p> + <p> + Own Id: OTP-15747 Aux Id: ERIERL-294 </p> + </item> + <item> + <p> + The net module has been split into 'net' (kernel) and + prim_net (preloaded).</p> + <p> + Own Id: OTP-15765</p> + </item> + <item> + <p> + Socket counters now works as expected and can also be + extracted with the (new) info function.</p> + <p> + Own Id: OTP-15818</p> + </item> + <item> + <p> + <c>re:run()</c> now avoids validating utf8 in the subject + more than once in the same call. This validation could + previously be performed multiple times when the + <c>global</c> option was passed.</p> + <p> + Own Id: OTP-15831 Aux Id: ERL-876 </p> + </item> + <item> + <p> + The un-documented function <c>erlang:dist_get_stat/1</c> + now returns the real value of what the distribution queue + contains instead of a boolean.</p> + <p> + Own Id: OTP-15905 Aux Id: PR-2270 </p> + </item> + <item> + <p> + ETS <c>ordered_set</c> tables with + <c>write_concurrency</c> enabled has got a performance + issue fixed. There were no limits for the values of + internal statistics counters before this fix. This could + result in that the data structure sometimes reacted + slowly to a change in how many parallel processes were + using it.</p> + <p> + Own Id: OTP-15906</p> + </item> + <item> + <p> + Optimize the reception of large distribution messages.</p> + <p> + Own Id: OTP-15926 Aux Id: PR-2291 </p> + </item> + <item> + <p>Binary matching and functions like + <c>split_binary/2</c> will now create heap binaries when + the results are small enough, reducing the chances of + small sub-binaries keeping large binaries alive.</p> + <p> + Own Id: OTP-15977 Aux Id: ERIERL-366 </p> + </item> + <item> + <p>Fixed rare emulator crash in + <c>instrument:allocations/0-1</c>.</p> + <p> + Own Id: OTP-15983</p> + </item> + <item> + <p> + Ports could pass very small binaries as reference counted + off heap binaries to processes. This could cause an + unnecessary large memory usage and an unnecessary load on + the binary allocator. Small binaries are now always + passed as heap binaries to processes.</p> + <p> + Own Id: OTP-16001 Aux Id: ERIERL-366 </p> + </item> + <item> + <p> + <c>unicode:characters_to_binary()</c> could return very + small binaries as reference counted off heap binaries. + This could cause an unnecessary large memory usage and an + unnecessary load on the binary allocator. Small binaries + are now always returned as heap binaries.</p> + <p> + Own Id: OTP-16002 Aux Id: ERIERL-366 </p> + </item> + <item> + <p> + Improved <c>erl_nif</c> documentation regarding + <c>on_load</c> and Erlang stub/fallback functions.</p> + <p> + Own Id: OTP-16028 Aux Id: PR-2362 </p> + </item> + <item> + <p> + New feature <c>ets:info(_, binary)</c> to get information + about all reference counted binaries kept by a table. + This is the same kind of debug information that + <c>process_info(_, binary)</c> returns for a process.</p> + <p> + Own Id: OTP-16035 Aux Id: ERIERL-366 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.4.4</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -640,6 +965,42 @@ </section> +<section><title>Erts 10.3.5.5</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p><c>process_info(P,binary)</c> would neglect to look + through heap fragments, potentially missing a few + binaries associated with the process.</p> + <p> + Own Id: OTP-15978 Aux Id: ERIERL-366 </p> + </item> + <item> + <p> + Fixed bug triggered if a process is killed during call to + <c>persistent_term:put</c> or + <c>persistent_term:erase</c>.</p> + <p> + Own Id: OTP-16041</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Fixed rare emulator crash in + <c>instrument:allocations/0-1</c>.</p> + <p> + Own Id: OTP-15983</p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.3.5.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml index 2f7ff2fc07..708f87edf6 100644 --- a/erts/doc/src/socket.xml +++ b/erts/doc/src/socket.xml @@ -327,7 +327,7 @@ </func> <func> - <name name="accept" arity="2" clause_i="1" anchor="accept_async" since="OTP @OTP-15731@"/> + <name name="accept" arity="2" clause_i="1" anchor="accept_async" since="OTP 22.1"/> <fsummary>Accept a connection on a socket.</fsummary> <desc> <p>Accept a connection on a socket.</p> @@ -362,7 +362,7 @@ </func> <func> - <name name="cancel" arity="2" since="OTP @OTP-15731@"/> + <name name="cancel" arity="2" since="OTP 22.1"/> <fsummary>Cancel an asynchronous request.</fsummary> <desc> <p>Cancel an asynchronous request.</p> @@ -403,7 +403,7 @@ </func> <func> - <name name="connect" arity="3" clause_i="1" anchor="connect_async" since="OTP @OTP-15731@"/> + <name name="connect" arity="3" clause_i="1" anchor="connect_async" since="OTP 22.1"/> <fsummary>Initiate a connection on a socket.</fsummary> <desc> <p>This function connects the socket to the address @@ -471,7 +471,7 @@ </func> <func> - <name name="info" arity="1" since="OTP @OTP-15818@"/> + <name name="info" arity="1" since="OTP 22.1"/> <fsummary>Get miscellaneous socket info.</fsummary> <desc> <p>Get miscellaneous info about the socket.</p> @@ -544,8 +544,8 @@ </func> <func> - <name name="recv" arity="3" clause_i="2" anchor="recv_async" since="OTP @OTP-15731@"/> - <name name="recv" arity="4" clause_i="1" since="OTP @OTP-15731@"/> + <name name="recv" arity="3" clause_i="2" anchor="recv_async" since="OTP 22.1"/> + <name name="recv" arity="4" clause_i="1" since="OTP 22.1"/> <fsummary>Receive a message from a socket.</fsummary> <desc> <p>Receive a message from a socket.</p> @@ -596,9 +596,9 @@ </func> <func> - <name name="recvfrom" arity="3" clause_i="1" anchor="recvfrom_async" since="OTP @OTP-15731@"/> - <name name="recvfrom" arity="3" clause_i="4" since="OTP @OTP-15731@"/> - <name name="recvfrom" arity="4" clause_i="1" since="OTP @OTP-15731@"/> + <name name="recvfrom" arity="3" clause_i="1" anchor="recvfrom_async" since="OTP 22.1"/> + <name name="recvfrom" arity="3" clause_i="4" since="OTP 22.1"/> + <name name="recvfrom" arity="4" clause_i="1" since="OTP 22.1"/> <fsummary>Receive a message from a socket.</fsummary> <desc> <p>Receive a message from a socket.</p> @@ -663,9 +663,9 @@ </func> <func> - <name name="recvmsg" arity="2" clause_i="2" anchor="recvmsg_async" since="OTP @OTP-15731@"/> - <name name="recvmsg" arity="3" clause_i="1" since="OTP @OTP-15731@"/> - <name name="recvmsg" arity="5" clause_i="1" since="OTP @OTP-15731@"/> + <name name="recvmsg" arity="2" clause_i="2" anchor="recvmsg_async" since="OTP 22.1"/> + <name name="recvmsg" arity="3" clause_i="1" since="OTP 22.1"/> + <name name="recvmsg" arity="5" clause_i="1" since="OTP 22.1"/> <fsummary>Receive a message from a socket.</fsummary> <desc> <p>Receive a message from a socket.</p> @@ -716,8 +716,8 @@ </func> <func> - <name name="send" arity="3" clause_i="2" anchor="send_async" since="OTP @OTP-15731@"/> - <name name="send" arity="4" clause_i="1" since="OTP @OTP-15731@"/> + <name name="send" arity="3" clause_i="2" anchor="send_async" since="OTP 22.1"/> + <name name="send" arity="4" clause_i="1" since="OTP 22.1"/> <fsummary>Send a message on a socket.</fsummary> <desc> <p>Send a message on a connected socket.</p> @@ -764,8 +764,8 @@ </func> <func> - <name name="sendmsg" arity="3" clause_i="2" anchor="sendmsg_async" since="OTP @OTP-15731@"/> - <name name="sendmsg" arity="4" clause_i="1" since="OTP @OTP-15731@"/> + <name name="sendmsg" arity="3" clause_i="2" anchor="sendmsg_async" since="OTP 22.1"/> + <name name="sendmsg" arity="4" clause_i="1" since="OTP 22.1"/> <fsummary>Send a message on a socket.</fsummary> <desc> <p>Send a message on a socket. The destination, if needed @@ -806,8 +806,8 @@ </func> <func> - <name name="sendto" arity="4" clause_i="2" anchor="sendto_async" since="OTP @OTP-15731@"/> - <name name="sendto" arity="5" clause_i="1" since="OTP @OTP-15731@"/> + <name name="sendto" arity="4" clause_i="2" anchor="sendto_async" since="OTP 22.1"/> + <name name="sendto" arity="5" clause_i="1" since="OTP 22.1"/> <fsummary>Send a message on a socket.</fsummary> <desc> <p>Send a message on a socket, to the specified destination.</p> @@ -953,4 +953,3 @@ server(Addr, Port) -> </code> </section> </erlref> - diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index b2b8acd1b0..70718575e9 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -599,7 +599,6 @@ endif $(TTF_DIR)/erl_bif_table.c \ $(TTF_DIR)/erl_bif_table.h \ -$(TTF_DIR)/erl_bif_wrap.c \ $(TTF_DIR)/erl_bif_list.h \ $(TTF_DIR)/erl_atom_table.c \ $(TTF_DIR)/erl_atom_table.h \ @@ -885,7 +884,6 @@ RUN_OBJS += \ $(OBJDIR)/erl_bif_persistent.o \ $(OBJDIR)/erl_bif_atomics.o $(OBJDIR)/erl_bif_counters.o \ $(OBJDIR)/erl_bif_trace.o $(OBJDIR)/erl_bif_unique.o \ - $(OBJDIR)/erl_bif_wrap.o $(OBJDIR)/erl_nfunc_sched.o \ $(OBJDIR)/erl_guard_bifs.o $(OBJDIR)/erl_dirty_bif_wrap.o \ $(OBJDIR)/erl_trace.o $(OBJDIR)/copy.o \ $(OBJDIR)/utils.o $(OBJDIR)/bif.o \ @@ -920,7 +918,8 @@ RUN_OBJS += \ $(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o \ $(OBJDIR)/erl_msacc.o $(OBJDIR)/erl_lock_flags.o \ $(OBJDIR)/erl_io_queue.o $(OBJDIR)/erl_db_catree.o \ - $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o + $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o \ + $(OBJDIR)/erl_nfunc_sched.o LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o diff --git a/erts/emulator/beam/arith_instrs.tab b/erts/emulator/beam/arith_instrs.tab index f14b376419..29f761286f 100644 --- a/erts/emulator/beam/arith_instrs.tab +++ b/erts/emulator/beam/arith_instrs.tab @@ -447,10 +447,10 @@ shift.execute(Fail, Dst) { reg[1] = Op2; SWAPOUT; if (IsOpCode(I[0], i_bsl_ssjd)) { - I = handle_error(c_p, I, reg, &bif_export[BIF_bsl_2]->info.mfa); + I = handle_error(c_p, I, reg, &bif_trap_export[BIF_bsl_2].info.mfa); } else { ASSERT(IsOpCode(I[0], i_bsr_ssjd)); - I = handle_error(c_p, I, reg, &bif_export[BIF_bsr_2]->info.mfa); + I = handle_error(c_p, I, reg, &bif_trap_export[BIF_bsr_2].info.mfa); } goto post_error_handling; } diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 93ba56dccd..4c67c2029f 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -404,6 +404,7 @@ atom min_bin_vheap_size atom minor atom minor_version atom Minus='-' +atom MinusMinus='--' atom module atom module_info atom monitored_by @@ -494,6 +495,7 @@ atom packet atom packet_size atom parallelism atom Plus='+' +atom PlusPlus='++' atom pause atom pending atom pending_driver diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 04b2ed64b7..66cc1855e9 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -166,7 +166,7 @@ BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3) BIF_ERROR(BIF_P, BADARG); if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD3(bif_export[BIF_code_make_stub_module_3], + ERTS_BIF_YIELD3(&bif_trap_export[BIF_code_make_stub_module_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); } @@ -301,7 +301,7 @@ finish_loading_1(BIF_ALIST_1) int do_commit = 0; if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD1(bif_export[BIF_finish_loading_1], BIF_P, BIF_ARG_1); + ERTS_BIF_YIELD1(&bif_trap_export[BIF_finish_loading_1], BIF_P, BIF_ARG_1); } /* @@ -659,7 +659,7 @@ BIF_RETTYPE delete_module_1(BIF_ALIST_1) } if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD1(bif_export[BIF_delete_module_1], BIF_P, BIF_ARG_1); + ERTS_BIF_YIELD1(&bif_trap_export[BIF_delete_module_1], BIF_P, BIF_ARG_1); } { @@ -785,7 +785,7 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) } if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD2(bif_export[BIF_finish_after_on_load_2], + ERTS_BIF_YIELD2(&bif_trap_export[BIF_finish_after_on_load_2], BIF_P, BIF_ARG_1, BIF_ARG_2); } @@ -835,21 +835,25 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) */ num_exps = export_list_size(code_ix); for (i = 0; i < num_exps; i++) { - Export *ep = export_list(i,code_ix); - if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) { - continue; - } - if (ep->beam[1] != 0) { - ep->addressv[code_ix] = (void *) ep->beam[1]; - ep->beam[1] = 0; - } else { - if (ep->addressv[code_ix] == ep->beam && - BeamIsOpCode(ep->beam[0], op_apply_bif)) { - continue; - } - ep->addressv[code_ix] = ep->beam; - ep->beam[0] = BeamOpCodeAddr(op_call_error_handler); - } + Export *ep = export_list(i, code_ix); + + if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) { + continue; + } + + DBG_CHECK_EXPORT(ep, code_ix); + + if (ep->trampoline.not_loaded.deferred != 0) { + ep->addressv[code_ix] = (void*)ep->trampoline.not_loaded.deferred; + ep->trampoline.not_loaded.deferred = 0; + } else { + if (ep->bif_number != -1) { + continue; + } + + ep->addressv[code_ix] = ep->trampoline.raw; + ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler); + } } modp->curr.code_hdr->on_load_function_ptr = NULL; @@ -872,10 +876,11 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) { continue; } - if (BeamIsOpCode(ep->beam[0], op_apply_bif)) { + if (ep->bif_number != -1) { continue; } - ep->beam[1] = 0; + + ep->trampoline.not_loaded.deferred = 0; } } erts_release_code_write_permission(); @@ -1133,7 +1138,7 @@ check_process_code(Process* rp, Module* modp, int *redsp, int fcalls) *redsp += 1; - if (erts_check_nif_export_in_area(rp, mod_start, mod_size)) + if (erts_check_nfunc_in_area(rp, mod_start, mod_size)) return am_true; *redsp += (STACK_START(rp) - rp->stop) / 32; @@ -1672,7 +1677,7 @@ BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2) BIF_ERROR(BIF_P, BADARG); if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_purge_module_2], + ERTS_BIF_YIELD2(&bif_trap_export[BIF_erts_internal_purge_module_2], BIF_P, BIF_ARG_1, BIF_ARG_2); } @@ -1884,25 +1889,28 @@ delete_code(Module* modp) for (i = 0; i < num_exps; i++) { Export *ep = export_list(i, code_ix); if (ep != NULL && (ep->info.mfa.module == module)) { - if (ep->addressv[code_ix] == ep->beam) { - if (BeamIsOpCode(ep->beam[0], op_apply_bif)) { - continue; - } - else if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) { + if (ep->addressv[code_ix] == ep->trampoline.raw) { + if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) { ERTS_LC_ASSERT(erts_thr_progress_is_blocking()); ASSERT(modp->curr.num_traced_exports > 0); DBG_TRACE_MFA_P(&ep->info.mfa, "export trace cleared, code_ix=%d", code_ix); - erts_clear_export_break(modp, &ep->info); + erts_clear_export_break(modp, ep); } else { - ASSERT(BeamIsOpCode(ep->beam[0], op_call_error_handler) || + ASSERT(BeamIsOpCode(ep->trampoline.op, op_call_error_handler) || !erts_initialized); } } - ep->addressv[code_ix] = ep->beam; - ep->beam[0] = BeamOpCodeAddr(op_call_error_handler); - ep->beam[1] = 0; + + if (ep->bif_number != -1 && ep->is_bif_traced) { + /* Code unloading kills both global and local call tracing. */ + ep->is_bif_traced = 0; + } + + ep->addressv[code_ix] = ep->trampoline.raw; + ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler); + ep->trampoline.not_loaded.deferred = 0; DBG_TRACE_MFA_P(&ep->info.mfa, "export invalidation, code_ix=%d", code_ix); } diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 10940072ae..1bb20f6ae3 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -207,9 +207,6 @@ erts_bp_match_functions(BpFunctions* f, ErtsCodeMFA *mfa, int specified) if (erts_is_function_native(ci)) { continue; } - if (is_nil(ci->mfa.module)) { /* Ignore BIF stub */ - continue; - } switch (specified) { case 3: if (ci->mfa.arity != mfa->arity) @@ -244,8 +241,10 @@ erts_bp_match_export(BpFunctions* f, ErtsCodeMFA *mfa, int specified) f->matching = (BpFunction *) Alloc(num_exps*sizeof(BpFunction)); ne = 0; for (i = 0; i < num_exps; i++) { - Export* ep = export_list(i, code_ix); - BeamInstr* pc; + BeamInstr *func; + Export* ep; + + ep = export_list(i, code_ix); switch (specified) { case 3: @@ -263,19 +262,20 @@ erts_bp_match_export(BpFunctions* f, ErtsCodeMFA *mfa, int specified) ASSERT(0); } - pc = ep->beam; - if (ep->addressv[code_ix] == pc) { - if (BeamIsOpCode(*pc, op_apply_bif) || - BeamIsOpCode(*pc, op_call_error_handler)) { - continue; - } - ASSERT(BeamIsOpCode(*pc, op_i_generic_breakpoint)); - } else if (erts_is_function_native(erts_code_to_codeinfo(ep->addressv[code_ix]))) { - continue; - } + func = ep->addressv[code_ix]; + + if (func == ep->trampoline.raw) { + if (BeamIsOpCode(*func, op_call_error_handler)) { + continue; + } + ASSERT(BeamIsOpCode(*func, op_i_generic_breakpoint)); + } else if (erts_is_function_native(erts_code_to_codeinfo(func))) { + continue; + } f->matching[ne].ci = &ep->info; f->matching[ne].mod = erts_get_module(ep->info.mfa.module, code_ix); + ne++; } @@ -305,18 +305,6 @@ erts_consolidate_bp_data(BpFunctions* f, int local) } } -void -erts_consolidate_bif_bp_data(void) -{ - int i; - - ERTS_LC_ASSERT(erts_has_code_write_permission()); - for (i = 0; i < BIF_SIZE; i++) { - Export *ep = bif_export[i]; - consolidate_bp_data(0, &ep->info, 0); - } -} - static void consolidate_bp_data(Module* modp, ErtsCodeInfo *ci, int local) { @@ -495,7 +483,7 @@ erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, ErtsTracer tracer) } void -erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local) +erts_set_export_trace(ErtsCodeInfo *ci, Binary *match_spec, int local) { Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE; @@ -503,25 +491,6 @@ erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local) } void -erts_set_mtrace_bif(ErtsCodeInfo *ci, Binary *match_spec, ErtsTracer tracer) -{ - set_function_break(ci, match_spec, ERTS_BPF_META_TRACE, 0, tracer); -} - -void -erts_set_time_trace_bif(ErtsCodeInfo *ci, enum erts_break_op count_op) -{ - set_function_break(ci, NULL, - ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE, - count_op, erts_tracer_nil); -} - -void -erts_clear_time_trace_bif(ErtsCodeInfo *ci) { - clear_function_break(ci, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE); -} - -void erts_set_debug_break(BpFunctions* f) { set_break(f, NULL, ERTS_BPF_DEBUG, 0, erts_tracer_nil); } @@ -547,7 +516,7 @@ erts_clear_trace_break(BpFunctions* f) } void -erts_clear_call_trace_bif(ErtsCodeInfo *ci, int local) +erts_clear_export_trace(ErtsCodeInfo *ci, int local) { GenericBp* g = ci->u.gen_bp; @@ -566,12 +535,6 @@ erts_clear_mtrace_break(BpFunctions* f) } void -erts_clear_mtrace_bif(ErtsCodeInfo *ci) -{ - clear_function_break(ci, ERTS_BPF_META_TRACE); -} - -void erts_clear_debug_break(BpFunctions* f) { ERTS_LC_ASSERT(erts_thr_progress_is_blocking()); @@ -630,13 +593,22 @@ erts_clear_module_break(Module *modp) { } void -erts_clear_export_break(Module* modp, ErtsCodeInfo *ci) +erts_clear_export_break(Module* modp, Export *ep) { + ErtsCodeInfo *ci; + ERTS_LC_ASSERT(erts_thr_progress_is_blocking()); + ci = &ep->info; + + ASSERT(erts_codeinfo_to_code(ci) == ep->trampoline.raw); + + ASSERT(BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)); + ep->trampoline.op = 0; + clear_function_break(ci, ERTS_BPF_ALL); erts_commit_staged_bp(); - *erts_codeinfo_to_code(ci) = (BeamInstr) 0; + consolidate_bp_data(modp, ci, 0); ASSERT(ci->u.gen_bp == NULL); } @@ -762,229 +734,6 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg) } } -/* - * Entry point called by the trace wrap functions in erl_bif_wrap.c - * - * The trace wrap functions are themselves called through the export - * entries instead of the original BIF functions. - */ -Eterm -erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) -{ - Eterm result; - Eterm (*func)(Process*, Eterm*, BeamInstr*); - Export* ep = bif_export[bif_index]; - Uint32 flags = 0, flags_meta = 0; - ErtsTracer meta_tracer = erts_tracer_nil; - int applying = (I == ep->beam); /* Yup, the apply code for a bif - * is actually in the - * export entry */ - BeamInstr* cp = (BeamInstr *) p->stop[0]; - GenericBp* g; - GenericBpData* bp = NULL; - Uint bp_flags = 0; - int return_to_trace = 0; - - ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); - - g = ep->info.u.gen_bp; - if (g) { - bp = &g->data[erts_active_bp_ix()]; - bp_flags = bp->flags; - } - - /* - * Make continuation pointer OK, it is not during direct BIF calls, - * but it is correct during apply of bif. - */ - if (!applying) { - p->stop[0] = (Eterm) I; - } else { - fixup_cp_before_trace(p, &return_to_trace); - } - if (bp_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE) && - IS_TRACED_FL(p, F_TRACE_CALLS)) { - int local = !!(bp_flags & ERTS_BPF_LOCAL_TRACE); - flags = erts_call_trace(p, &ep->info, bp->local_ms, args, - local, &ERTS_TRACER(p)); - } - if (bp_flags & ERTS_BPF_META_TRACE) { - ErtsTracer old_tracer; - - meta_tracer = erts_atomic_read_nob(&bp->meta_tracer->tracer); - old_tracer = meta_tracer; - flags_meta = erts_call_trace(p, &ep->info, bp->meta_ms, args, - 0, &meta_tracer); - - if (!ERTS_TRACER_COMPARE(old_tracer, meta_tracer)) { - ErtsTracer new_tracer = erts_tracer_nil; - erts_tracer_update(&new_tracer, meta_tracer); - if (old_tracer == erts_atomic_cmpxchg_acqb( - &bp->meta_tracer->tracer, - (erts_aint_t)new_tracer, - (erts_aint_t)old_tracer)) { - ERTS_TRACER_CLEAR(&old_tracer); - } else { - ERTS_TRACER_CLEAR(&new_tracer); - } - } - } - if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE && - IS_TRACED_FL(p, F_TRACE_CALLS)) { - erts_trace_time_call(p, &ep->info, bp->time); - } - - /* Restore original continuation pointer (if changed). */ - p->stop[0] = (Eterm) cp; - - func = bif_table[bif_index].f; - - result = func(p, args, I); - - if (erts_nif_export_check_save_trace(p, result, - applying, ep, - flags, - flags_meta, I, - meta_tracer)) { - /* - * erts_bif_trace_epilogue() will be called - * later when appropriate via the NIF export - * scheduling functionality... - */ - return result; - } - - return erts_bif_trace_epilogue(p, result, applying, ep, - flags, flags_meta, I, - meta_tracer); -} - -Eterm -erts_bif_trace_epilogue(Process *p, Eterm result, int applying, - Export* ep, Uint32 flags, - Uint32 flags_meta, BeamInstr* I, - ErtsTracer meta_tracer) -{ - BeamInstr *cp = NULL; - - if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) { - BeamInstr i_return_trace = beam_return_trace[0]; - BeamInstr i_return_to_trace = beam_return_to_trace[0]; - BeamInstr i_return_time_trace = beam_return_time_trace[0]; - Eterm *cpp; - - /* Maybe advance cp to skip trace stack frames */ - cpp = p->stop; - while (is_not_CP(*cpp)) { - cpp++; - } - for (cp = cp_val(*cpp++); ;) { - if (*cp == i_return_trace) { - /* Skip stack frame variables */ - while (is_not_CP(*cpp)) cpp++; - cpp += 2; /* Skip return_trace parameters */ - } else if (*cp == i_return_time_trace) { - /* Skip stack frame variables */ - while (is_not_CP(*cpp)) cpp++; - cpp += 1; /* Skip return_time_trace parameters */ - } else if (*cp == i_return_to_trace) { - /* A return_to trace message is going to be generated - * by normal means, so we do not have to. - */ - cp = NULL; - break; - } else { - break; - } - cp = cp_val(*cpp++); - } - } - - /* Try to get these in the order - * they usually appear in normal code... */ - if (is_non_value(result)) { - Uint reason = p->freason; - if (reason != TRAP) { - Eterm class; - Eterm value = p->fvalue; - /* Expand error value like in handle_error() */ - if (reason & EXF_ARGLIST) { - Eterm *tp; - ASSERT(is_tuple(value)); - tp = tuple_val(value); - value = tp[1]; - } - if ((reason & EXF_THROWN) && (p->catches <= 0)) { - Eterm *hp = HAlloc(p, 3); - value = TUPLE2(hp, am_nocatch, value); - reason = EXC_ERROR; - } - /* Note: expand_error_value() could theoretically - * allocate on the heap, but not for any error - * returned by a BIF, and it would do no harm, - * just be annoying. - */ - value = expand_error_value(p, reason, value); - class = exception_tag[GET_EXC_CLASS(reason)]; - - if (flags_meta & MATCH_SET_EXCEPTION_TRACE) { - erts_trace_exception(p, &ep->info.mfa, class, value, - &meta_tracer); - } - if (flags & MATCH_SET_EXCEPTION_TRACE) { - erts_trace_exception(p, &ep->info.mfa, class, value, - &ERTS_TRACER(p)); - } - if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) { - /* can only happen if(local)*/ - Eterm *ptr = p->stop; - ASSERT(!applying || is_CP(*ptr)); - ASSERT(ptr <= STACK_START(p)); - /* Search the nearest stack frame for a catch */ - while (++ptr < STACK_START(p)) { - if (is_CP(*ptr)) break; - if (is_catch(*ptr)) { - if (applying) { - /* Apply of BIF, cp is in calling function */ - if (cp) erts_trace_return_to(p, cp); - } else { - /* Direct bif call, I points into - * calling function */ - erts_trace_return_to(p, I); - } - } - } - } - if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) { - erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); - ERTS_TRACE_FLAGS(p) |= F_EXCEPTION_TRACE; - erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR); - } - } - } else { - if (flags_meta & MATCH_SET_RX_TRACE) { - erts_trace_return(p, &ep->info.mfa, result, &meta_tracer); - } - /* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */ - if (flags & MATCH_SET_RX_TRACE) { - erts_trace_return(p, &ep->info.mfa, result, &ERTS_TRACER(p)); - } - if (flags & MATCH_SET_RETURN_TO_TRACE && - IS_TRACED_FL(p, F_TRACE_RETURN_TO)) { - /* can only happen if(local)*/ - if (applying) { - /* Apply of BIF, cp is in calling function */ - if (cp) erts_trace_return_to(p, cp); - } else { - /* Direct bif call, I points into calling function */ - erts_trace_return_to(p, I); - } - } - } - ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); - return result; -} - static ErtsTracer do_call_trace(Process* c_p, ErtsCodeInfo* info, Eterm* reg, int local, Binary* ms, ErtsTracer tracer) diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index a64765822b..54e84e7e4f 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -119,20 +119,16 @@ void erts_bp_free_matched_functions(BpFunctions* f); void erts_install_breakpoints(BpFunctions* f); void erts_uninstall_breakpoints(BpFunctions* f); void erts_consolidate_bp_data(BpFunctions* f, int local); -void erts_consolidate_bif_bp_data(void); void erts_set_trace_break(BpFunctions *f, Binary *match_spec); void erts_clear_trace_break(BpFunctions *f); -void erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local); -void erts_clear_call_trace_bif(ErtsCodeInfo *ci, int local); +void erts_set_export_trace(ErtsCodeInfo *ci, Binary *match_spec, int local); +void erts_clear_export_trace(ErtsCodeInfo *ci, int local); void erts_set_mtrace_break(BpFunctions *f, Binary *match_spec, ErtsTracer tracer); void erts_clear_mtrace_break(BpFunctions *f); -void erts_set_mtrace_bif(ErtsCodeInfo *ci, Binary *match_spec, - ErtsTracer tracer); -void erts_clear_mtrace_bif(ErtsCodeInfo *ci); void erts_set_debug_break(BpFunctions *f); void erts_clear_debug_break(BpFunctions *f); @@ -142,7 +138,7 @@ void erts_clear_count_break(BpFunctions *f); void erts_clear_all_breaks(BpFunctions* f); int erts_clear_module_break(Module *modp); -void erts_clear_export_break(Module *modp, ErtsCodeInfo* ci); +void erts_clear_export_break(Module *modp, Export *ep); BeamInstr erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *ci, Eterm* reg); BeamInstr erts_trace_break(Process *p, ErtsCodeInfo *ci, Eterm *args, @@ -151,8 +147,6 @@ BeamInstr erts_trace_break(Process *p, ErtsCodeInfo *ci, Eterm *args, int erts_is_trace_break(ErtsCodeInfo *ci, Binary **match_spec_ret, int local); int erts_is_mtrace_break(ErtsCodeInfo *ci, Binary **match_spec_ret, ErtsTracer *tracer_ret); -int erts_is_mtrace_bif(ErtsCodeInfo *ci, Binary **match_spec_ret, - ErtsTracer *tracer_ret); int erts_is_native_break(ErtsCodeInfo *ci); int erts_is_count_break(ErtsCodeInfo *ci, Uint *count_ret); int erts_is_time_break(Process *p, ErtsCodeInfo *ci, Eterm *call_time); @@ -163,10 +157,6 @@ void erts_schedule_time_break(Process *p, Uint out); void erts_set_time_break(BpFunctions *f, enum erts_break_op); void erts_clear_time_break(BpFunctions *f); -int erts_is_time_trace_bif(Process *p, ErtsCodeInfo *ci, Eterm *call_time); -void erts_set_time_trace_bif(ErtsCodeInfo *ci, enum erts_break_op); -void erts_clear_time_trace_bif(ErtsCodeInfo *ci); - ErtsCodeInfo *erts_find_local_func(ErtsCodeMFA *mfa); #if ERTS_GLB_INLINE_INCL_FUNC_DEF diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 4d52435139..255ce48306 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -158,7 +158,7 @@ erts_debug_breakpoint_2(BIF_ALIST_2) } if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD2(bif_export[BIF_erts_debug_breakpoint_2], + ERTS_BIF_YIELD2(&bif_trap_export[BIF_erts_debug_breakpoint_2], BIF_P, BIF_ARG_1, BIF_ARG_2); } erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN); @@ -332,7 +332,7 @@ erts_debug_disassemble_1(BIF_ALIST_1) "unknown " HEXF "\n", instr); code_ptr++; } - if (i == op_call_nif) { + if (i == op_call_nif_WWW) { /* * The rest of the code will not be executed. Don't disassemble any * more code in this function. diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 9f8b56a5d5..a7d28f705d 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -111,10 +111,9 @@ do { \ #define CHECK_ALIGNED(Dst) ASSERT((((Uint)&Dst) & (sizeof(Uint)-1)) == 0) -#define GET_BIF_MODULE(p) (p->info.mfa.module) -#define GET_BIF_FUNCTION(p) (p->info.mfa.function) -#define GET_BIF_ARITY(p) (p->info.mfa.arity) -#define GET_BIF_ADDRESS(p) ((BifFunction) (p->beam[1])) +#define GET_EXPORT_MODULE(p) ((p)->info.mfa.module) +#define GET_EXPORT_FUNCTION(p) ((p)->info.mfa.function) +#define GET_EXPORT_ARITY(p) ((p)->info.mfa.arity) #define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm)))) @@ -250,72 +249,6 @@ void** beam_ops; #define Q(N) (N*sizeof(Eterm *)) #define l(N) (freg[N].fd) -/* - * Check that we haven't used the reductions and jump to function pointed to by - * the I register. If we are out of reductions, do a context switch. - */ - -#define DispatchMacro() \ - do { \ - BeamInstr dis_next; \ - dis_next = *I; \ - CHECK_ARGS(I); \ - if (FCALLS > 0 || FCALLS > neg_o_reds) { \ - FCALLS--; \ - Goto(dis_next); \ - } else { \ - goto context_switch; \ - } \ - } while (0) \ - -#define DispatchMacroFun() \ - do { \ - BeamInstr dis_next; \ - dis_next = *I; \ - CHECK_ARGS(I); \ - if (FCALLS > 0 || FCALLS > neg_o_reds) { \ - FCALLS--; \ - Goto(dis_next); \ - } else { \ - goto context_switch_fun; \ - } \ - } while (0) - -#define DispatchMacrox() \ - do { \ - if (FCALLS > 0) { \ - BeamInstr dis_next; \ - SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \ - dis_next = *I; \ - FCALLS--; \ - CHECK_ARGS(I); \ - Goto(dis_next); \ - } else if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) \ - && FCALLS > neg_o_reds) { \ - goto save_calls1; \ - } else { \ - SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \ - CHECK_ARGS(I); \ - goto context_switch; \ - } \ - } while (0) - -#ifdef DEBUG -/* - * To simplify breakpoint setting, put the code in one place only and jump to it. - */ -# define Dispatch() goto do_dispatch -# define Dispatchx() goto do_dispatchx -# define Dispatchfun() goto do_dispatchfun -#else -/* - * Inline for speed. - */ -# define Dispatch() DispatchMacro() -# define Dispatchx() DispatchMacrox() -# define Dispatchfun() DispatchMacroFun() -#endif - #define Arg(N) I[(N)+1] #define GetSource(raw, dst) \ @@ -348,19 +281,6 @@ do { \ } \ } while(0) -#define DispatchReturn \ -do { \ - if (FCALLS > 0 || FCALLS > neg_o_reds) { \ - FCALLS--; \ - Goto(*I); \ - } \ - else { \ - c_p->current = NULL; \ - c_p->arity = 1; \ - goto context_switch3; \ - } \ -} while (0) - #ifdef DEBUG /* Better static type testing by the C compiler */ # define BEAM_IS_TUPLE(Src) is_tuple(Src) @@ -517,10 +437,10 @@ init_emulator(void) } \ } while(0) -#define DTRACE_RETURN_FROM_PC(p) \ +#define DTRACE_RETURN_FROM_PC(p, i) \ do { \ ErtsCodeMFA* cmfa; \ - if (DTRACE_ENABLED(function_return) && (cmfa = find_function_from_pc(cp_val((p)->stop[0])))) { \ + if (DTRACE_ENABLED(function_return) && (cmfa = find_function_from_pc(i))) { \ DTRACE_RETURN((p), cmfa); \ } \ } while(0) @@ -530,7 +450,7 @@ init_emulator(void) #define DTRACE_GLOBAL_CALL(p, mfa) do {} while (0) #define DTRACE_GLOBAL_CALL_FROM_EXPORT(p, e) do {} while (0) #define DTRACE_RETURN(p, mfa) do {} while (0) -#define DTRACE_RETURN_FROM_PC(p) do {} while (0) +#define DTRACE_RETURN_FROM_PC(p, i) do {} while (0) #define DTRACE_BIF_ENTRY(p, mfa) do {} while (0) #define DTRACE_BIF_RETURN(p, mfa) do {} while (0) #define DTRACE_NIF_ENTRY(p, mfa) do {} while (0) @@ -768,27 +688,9 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) #endif #include "beam_hot.h" - -#ifdef DEBUG - /* - * Set a breakpoint here to get control just after a call instruction. - * I points to the first instruction in the called function. - * - * In gdb, use 'call dis(I-5, 1)' to show the name of the function. - */ - do_dispatch: - DispatchMacro(); - - do_dispatchx: - DispatchMacrox(); - - do_dispatchfun: - DispatchMacroFun(); - -#endif - /* - * Jumped to from the Dispatch() macro when the reductions are used up. + * The labels are jumped to from the $DISPATCH() macros when the reductions + * are used up. * * Since the I register points just beyond the FuncBegin instruction, we * can get the module, function, and arity for the function being @@ -982,18 +884,46 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) } #endif return; /* Never executed */ +} - save_calls1: - { - BeamInstr dis_next; +/* + * Enter all BIFs into the export table. + * + * Note that they will all call the error_handler until their modules have been + * loaded, which may prevent the system from booting if BIFs from non-preloaded + * modules are apply/3'd while loading code. Ordinary BIF calls will work fine + * however since they won't go through export entries. + */ +static void install_bifs(void) { + int i; + + for (i = 0; i < BIF_SIZE; i++) { + BifEntry *entry; + Export *ep; + int j; + + entry = &bif_table[i]; + + ep = erts_export_put(entry->module, entry->name, entry->arity); - save_calls(c_p, (Export *) Arg(0)); + ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI); + ep->info.mfa.module = entry->module; + ep->info.mfa.function = entry->name; + ep->info.mfa.arity = entry->arity; + ep->bif_number = i; - SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); + memset(&ep->trampoline, 0, sizeof(ep->trampoline)); + ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler); - dis_next = *I; - FCALLS--; - Goto(dis_next); + for (j = 0; j < ERTS_NUM_CODE_IX; j++) { + ep->addressv[j] = ep->trampoline.raw; + } + + /* Set up a hidden export entry so we can trap to this BIF without + * it being seen when tracing. */ + erts_init_trap_export(&bif_trap_export[i], + entry->module, entry->name, entry->arity, + entry->f); } } @@ -1004,43 +934,30 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) static void init_emulator_finish(void) { - int i; - Export* ep; - #if defined(ARCH_64) && defined(CODE_MODEL_SMALL) - for (i = 0; i < NUMBER_OF_OPCODES; i++) { - BeamInstr instr = BeamOpCodeAddr(i); - if (instr >= (1ull << 32)) { - erts_exit(ERTS_ERROR_EXIT, - "This run-time was supposed be compiled with all code below 2Gb,\n" - "but the instruction '%s' is located at %016lx.\n", - opc[i].name, instr); - } - } + int i; + + for (i = 0; i < NUMBER_OF_OPCODES; i++) { + BeamInstr instr = BeamOpCodeAddr(i); + if (instr >= (1ull << 32)) { + erts_exit(ERTS_ERROR_EXIT, + "This run-time was supposed be compiled with all code below 2Gb,\n" + "but the instruction '%s' is located at %016lx.\n", + opc[i].name, instr); + } + } #endif - beam_apply[0] = BeamOpCodeAddr(op_i_apply); - beam_apply[1] = BeamOpCodeAddr(op_normal_exit); - beam_exit[0] = BeamOpCodeAddr(op_error_action_code); - beam_continue_exit[0] = BeamOpCodeAddr(op_continue_exit); - beam_return_to_trace[0] = BeamOpCodeAddr(op_i_return_to_trace); - beam_return_trace[0] = BeamOpCodeAddr(op_return_trace); - beam_exception_trace[0] = BeamOpCodeAddr(op_return_trace); /* UGLY */ - beam_return_time_trace[0] = BeamOpCodeAddr(op_i_return_time_trace); + beam_apply[0] = BeamOpCodeAddr(op_i_apply); + beam_apply[1] = BeamOpCodeAddr(op_normal_exit); + beam_exit[0] = BeamOpCodeAddr(op_error_action_code); + beam_continue_exit[0] = BeamOpCodeAddr(op_continue_exit); + beam_return_to_trace[0] = BeamOpCodeAddr(op_i_return_to_trace); + beam_return_trace[0] = BeamOpCodeAddr(op_return_trace); + beam_exception_trace[0] = BeamOpCodeAddr(op_return_trace); /* UGLY */ + beam_return_time_trace[0] = BeamOpCodeAddr(op_i_return_time_trace); - /* - * Enter all BIFs into the export table. - */ - for (i = 0; i < BIF_SIZE; i++) { - ep = erts_export_put(bif_table[i].module, - bif_table[i].name, - bif_table[i].arity); - bif_export[i] = ep; - ep->beam[0] = BeamOpCodeAddr(op_apply_bif); - ep->beam[1] = (BeamInstr) bif_table[i].f; - /* XXX: set func info for bifs */ - ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI); - } + install_bifs(); } /* @@ -1253,7 +1170,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) * I[2]: Pointer to erl_module_nif * I[3]: Function pointer to dirty NIF * - * This layout is determined by the NifExport struct + * This layout is determined by the ErtsNativeFunc struct */ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF); @@ -1267,11 +1184,11 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) ERTS_UNREQ_PROC_MAIN_LOCK(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); - if (BeamIsOpCode(*I, op_apply_bif)) { + if (BeamIsOpCode(*I, op_call_bif_W)) { exiting = erts_call_dirty_bif(esdp, c_p, I, reg); } else { - ASSERT(BeamIsOpCode(*I, op_call_nif)); + ASSERT(BeamIsOpCode(*I, op_call_nif_WWW)); exiting = erts_call_dirty_nif(esdp, c_p, I, reg); } @@ -1299,7 +1216,7 @@ ubif2mfa(void* uf) int i; for (i = 0; erts_u_bifs[i].bif; i++) { if (erts_u_bifs[i].bif == uf) - return &bif_export[erts_u_bifs[i].exp_ix]->info.mfa; + return &bif_trap_export[erts_u_bifs[i].exp_ix].info.mfa; } erts_exit(ERTS_ERROR_EXIT, "bad u bif: %p\n", uf); return NULL; @@ -1340,6 +1257,33 @@ Eterm error_atom[NUMBER_EXIT_CODES] = { am_badkey, /* 19 */ }; +/* Returns the return address at E[0] in printable form, skipping tracing in + * the same manner as gather_stacktrace. + * + * This is needed to generate correct stacktraces when throwing errors from + * instructions that return like an ordinary function, such as call_nif. */ +BeamInstr *erts_printable_return_address(Process* p, Eterm *E) { + Eterm *ptr = E; + + ASSERT(is_CP(*ptr)); + + while (ptr < STACK_START(p)) { + BeamInstr *cp = cp_val(*ptr); + + if (cp == beam_exception_trace || cp == beam_return_trace) { + ptr += 3; + } else if (cp == beam_return_time_trace) { + ptr += 2; + } else if (cp == beam_return_to_trace) { + ptr += 1; + } else { + return cp; + } + } + + ERTS_ASSERT(!"No continuation pointer on stack"); +} + /* * To fully understand the error handling, one must keep in mind that * when an exception is thrown, the search for a handler can jump back @@ -1369,14 +1313,14 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa) ASSERT(c_p->freason != TRAP); /* Should have been handled earlier. */ - if (c_p->freason & EXF_RESTORE_NIF) - erts_nif_export_restore_error(c_p, &pc, reg, &bif_mfa); + if (c_p->freason & EXF_RESTORE_NFUNC) + erts_nfunc_restore_error(c_p, &pc, reg, &bif_mfa); #ifdef DEBUG if (bif_mfa) { - /* Verify that bif_mfa does not point into our nif export */ - NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p); - ASSERT(!nep || !ErtsInArea(bif_mfa, (char *)nep, sizeof(NifExport))); + /* Verify that bif_mfa does not point into our native function wrapper */ + ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p); + ASSERT(!nep || !ErtsInArea(bif_mfa, (char *)nep, sizeof(ErtsNativeFunc))); } #endif @@ -1633,19 +1577,17 @@ expand_error_value(Process* c_p, Uint freason, Eterm Value) { static void -gather_stacktrace(Process* p, Eterm *ptr, struct StackTrace* s, int depth) +gather_stacktrace(Process* p, struct StackTrace* s, int depth) { BeamInstr *prev; - BeamInstr i_return_trace; - BeamInstr i_return_to_trace; + Eterm *ptr; if (depth == 0) { return; } - prev = s->depth ? s->trace[s->depth-1] : s->pc; - i_return_trace = beam_return_trace[0]; - i_return_to_trace = beam_return_to_trace[0]; + prev = s->depth ? s->trace[s->depth - 1] : s->pc; + ptr = p->stop; /* * Traverse the stack backwards and add all unique continuation @@ -1658,16 +1600,15 @@ gather_stacktrace(Process* p, Eterm *ptr, struct StackTrace* s, int depth) while (ptr < STACK_START(p) && depth > 0) { if (is_CP(*ptr)) { - if (*cp_val(*ptr) == i_return_trace) { - /* Skip stack frame variables */ - do ++ptr; while (is_not_CP(*ptr)); - /* Skip return_trace parameters */ + BeamInstr *cp = cp_val(*ptr); + + if (cp == beam_exception_trace || cp == beam_return_trace) { + ptr += 3; + } else if (cp == beam_return_time_trace) { ptr += 2; - } else if (*cp_val(*ptr) == i_return_to_trace) { - /* Skip stack frame variables */ - do ++ptr; while (is_not_CP(*ptr)); + } else if (cp == beam_return_to_trace) { + ptr += 1; } else { - BeamInstr *cp = cp_val(*ptr); if (cp != prev) { /* Record non-duplicates only */ prev = cp; @@ -1720,7 +1661,6 @@ static void save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa, Eterm args) { struct StackTrace* s; - Eterm *stack_start; int sz; int depth = erts_backtrace_depth; /* max depth (never negative) */ @@ -1739,33 +1679,6 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, s->depth = 0; /* - * If we crash on an instruction that returns to a return/exception trace - * instruction, we must set the stacktrace 'pc' to the actual return - * address or we'll lose the top stackframe when gathering the stack - * trace. - */ - stack_start = STACK_TOP(c_p); - if (stack_start < STACK_START(c_p) && is_CP(*stack_start)) { - BeamInstr *cp = cp_val(*stack_start); - - if (cp == pc) { - if (pc == beam_exception_trace || pc == beam_return_trace) { - ASSERT(&stack_start[3] <= STACK_START(c_p)); - /* Fake having failed on the first instruction in the function - * pointed to by the tag. */ - pc = cp_val(stack_start[1]); - stack_start += 3; - } else if (pc == beam_return_to_trace) { - ASSERT(&stack_start[2] <= STACK_START(c_p)); - pc = cp_val(stack_start[1]); - /* Skip both the trace tag and the new 'pc' to avoid - * duplicated entries. */ - stack_start += 2; - } - } - } - - /* * If the failure was in a BIF other than 'error/1', 'error/2', * 'exit/1' or 'throw/1', save BIF-MFA and save the argument * registers by consing up an arglist. @@ -1827,13 +1740,13 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, } /* Save the actual stack trace */ - gather_stacktrace(c_p, stack_start, s, depth); + gather_stacktrace(c_p, s, depth); } void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth) { - gather_stacktrace(p, STACK_TOP(p), s, depth); + gather_stacktrace(p, s, depth); } /* @@ -2092,83 +2005,66 @@ apply_bif_error_adjustment(Process *p, Export *ep, Eterm *reg, Uint arity, BeamInstr *I, Uint stack_offset) { + int apply_only; + Uint need; + + need = stack_offset /* bytes */ / sizeof(Eterm); + apply_only = stack_offset == 0; + /* * I is only set when the apply is a tail call, i.e., * from the instructions i_apply_only, i_apply_last_P, * and apply_last_IP. */ - if (I - && BeamIsOpCode(ep->beam[0], op_apply_bif) - && (ep == bif_export[BIF_error_1] - || ep == bif_export[BIF_error_2] - || ep == bif_export[BIF_exit_1] - || ep == bif_export[BIF_throw_1])) { - /* - * We are about to tail apply one of the BIFs - * erlang:error/1, erlang:error/2, erlang:exit/1, - * or erlang:throw/1. Error handling of these BIFs is - * special! - * - * We need the topmost continuation pointer to point into the - * calling function when handling the error after the BIF has - * been applied. This in order to get the topmost stackframe - * correct. - * - * Note that these BIFs will unconditionally cause an - * exception to be raised. That is, our modifications of the - * stack will be corrected by the error handling code. - */ - int apply_only = stack_offset == 0; - BeamInstr *cpp; - Eterm *E; + if (!(I && (ep->bif_number == BIF_error_1 || + ep->bif_number == BIF_error_2 || + ep->bif_number == BIF_exit_1 || + ep->bif_number == BIF_throw_1))) { + return; + } - E = p->stop; + /* + * We are about to tail apply one of the BIFs erlang:error/1, + * erlang:error/2, erlang:exit/1, or erlang:throw/1. Error handling of + * these BIFs is special! + * + * We need the topmost continuation pointer to point into the calling + * function when handling the error after the BIF has been applied. This in + * order to get the topmost stackframe correct. + * + * Note that these BIFs will unconditionally cause an exception to be + * raised. That is, our modifications of the stack will be corrected by the + * error handling code. + */ + if (need == 0) { + need = 1; /* i_apply_only */ + } - while (is_not_CP(*E)) { - E++; - } - cpp = cp_val(E[0]); + if (p->stop - p->htop < need) { + erts_garbage_collect(p, (int) need, reg, arity+1); + } + if (apply_only) { /* - * If we find an exception/return-to trace continuation - * pointer as the topmost continuation pointer, we do not - * need to do anything since the information will already - * be available for generation of the stacktrace. + * Called from the i_apply_only instruction. + * + * Push the continuation pointer for the current function to the stack. */ - - if (cpp != beam_exception_trace - && cpp != beam_return_trace - && cpp != beam_return_to_trace) { - Uint need = stack_offset /* bytes */ / sizeof(Eterm); - if (need == 0) - need = 1; /* i_apply_only */ - if (p->stop - p->htop < need) - erts_garbage_collect(p, (int) need, reg, arity+1); - if (apply_only) { - /* - * Called from the i_apply_only instruction. - * - * Push the continuation pointer for the current - * function to the stack. - */ - p->stop -= need; - p->stop[0] = make_cp(I); - } else { - /* - * Called from an i_apply_last_* instruction. - * - * The calling instruction will deallocate a stack - * frame of size 'stack_offset'. - * - * Push the continuation pointer for the current - * function to the stack, and then add a dummy - * stackframe for the i_apply_last* instruction - * to discard. - */ - p->stop[0] = make_cp(I); - p->stop -= need; - } - } + p->stop -= need; + p->stop[0] = make_cp(I); + } else { + /* + * Called from an i_apply_last_* instruction. + * + * The calling instruction will deallocate a stack frame of size + * 'stack_offset'. + * + * Push the continuation pointer for the current function to the stack, + * and then add a dummy stackframe for the i_apply_last* instruction + * to discard. + */ + p->stop[0] = make_cp(I); + p->stop -= need; } } @@ -2412,7 +2308,7 @@ erts_hibernate(Process* c_p, Eterm* reg) ASSERT(!ERTS_PROC_IS_EXITING(c_p)); } erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS); - c_p->current = &bif_export[BIF_hibernate_3]->info.mfa; + c_p->current = &bif_trap_export[BIF_hibernate_3].info.mfa; c_p->flags |= F_HIBERNATE_SCHED; /* Needed also when woken! */ return 1; } @@ -3220,10 +3116,10 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity) e.info.mfa.arity = arity; if ((ep = export_get(&e)) == NULL) { - return 0; + return 0; } - return ep->addressv[erts_active_code_ix()] == ep->beam && - BeamIsOpCode(ep->beam[0], op_apply_bif); + + return ep->bif_number != -1; } diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 3d5683f19f..13d366ef8b 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -141,7 +141,7 @@ typedef struct { * eventually patch with a pointer into * the export entry. */ - BifFunction bf; /* Pointer to BIF function if BIF; + Export *bif; /* Pointer to export entry if BIF; * NULL otherwise. */ } ImportEntry; @@ -845,17 +845,23 @@ erts_finish_loading(Binary* magic, Process* c_p, if (ep == NULL || ep->info.mfa.module != module) { continue; } - if (ep->addressv[code_ix] == ep->beam) { - if (BeamIsOpCode(ep->beam[0], op_apply_bif)) { - continue; - } else if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) { + + DBG_CHECK_EXPORT(ep, code_ix); + + if (ep->addressv[code_ix] == ep->trampoline.raw) { + if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) { ERTS_LC_ASSERT(erts_thr_progress_is_blocking()); ASSERT(mod_tab_p->curr.num_traced_exports > 0); - erts_clear_export_break(mod_tab_p, &ep->info); - ep->addressv[code_ix] = (BeamInstr *) ep->beam[1]; - ep->beam[1] = 0; + + erts_clear_export_break(mod_tab_p, ep); + + ep->addressv[code_ix] = + (BeamInstr*)ep->trampoline.breakpoint.address; + ep->trampoline.breakpoint.address = 0; + + ASSERT(ep->addressv[code_ix] != ep->trampoline.raw); } - ASSERT(ep->beam[1] == 0); + ASSERT(ep->trampoline.breakpoint.address == 0); } } ASSERT(mod_tab_p->curr.num_breakpoints == 0); @@ -1471,15 +1477,14 @@ load_import_table(LoaderState* stp) } stp->import[i].arity = arity; stp->import[i].patches = 0; - stp->import[i].bf = NULL; + stp->import[i].bif = NULL; /* - * If the export entry refers to a BIF, get the pointer to - * the BIF function. + * If the export entry refers to a BIF, save a pointer to the BIF entry. */ if ((e = erts_active_export_entry(mod, func, arity)) != NULL) { - if (BeamIsOpCode(e->beam[0], op_apply_bif)) { - stp->import[i].bf = (BifFunction) e->beam[1]; + if (e->bif_number != -1) { + stp->import[i].bif = e; if (func == am_load_nif && mod == am_erlang && arity == 2) { stp->may_load_nif = 1; } @@ -1530,33 +1535,6 @@ read_export_table(LoaderState* stp) LoadError2(stp, "export table entry %u: label %u not resolved", i, n); } stp->export[i].address = address = stp->codev + value; - - /* - * Find out if there is a BIF with the same name. - */ - - if (!is_bif(stp->module, func, arity)) { - continue; - } - - /* - * This is a stub for a BIF. - * - * It should not be exported, and the information in its - * func_info instruction should be invalidated so that it - * can be filtered out by module_info(functions) and by - * any other functions that walk through all local functions. - */ - - if (stp->labels[n].num_patches > 0) { - LoadError3(stp, "there are local calls to the stub for " - "the BIF %T:%T/%d", - stp->module, func, arity); - } - stp->export[i].address = NULL; - address[-1] = 0; - address[-2] = NIL; - address[-3] = NIL; } return 1; @@ -1564,25 +1542,16 @@ read_export_table(LoaderState* stp) return 0; } - static int is_bif(Eterm mod, Eterm func, unsigned arity) { - Export* e = erts_active_export_entry(mod, func, arity); - if (e == NULL) { - return 0; - } - if (! BeamIsOpCode(e->beam[0], op_apply_bif)) { - return 0; - } - if (mod == am_erlang && func == am_apply && arity == 3) { - /* - * erlang:apply/3 is a special case -- it is implemented - * as an instruction and it is OK to redefine it. - */ - return 0; + Export *e = erts_active_export_entry(mod, func, arity); + + if (e != NULL) { + return e->bif_number != -1; } - return 1; + + return 0; } static int @@ -2534,10 +2503,14 @@ load_code(LoaderState* stp) if (i >= stp->num_imports) { LoadError1(stp, "invalid import table index %d", i); } - if (stp->import[i].bf == NULL) { + if (stp->import[i].bif == NULL) { LoadError1(stp, "not a BIF: import table index %d", i); } - code[ci++] = (BeamInstr) stp->import[i].bf; + { + int bif_index = stp->import[i].bif->bif_number; + BifEntry *bif_entry = &bif_table[bif_index]; + code[ci++] = (BeamInstr) bif_entry->f; + } break; case 'P': /* Byte offset into tuple or stack */ case 'Q': /* Like 'P', but packable */ @@ -2845,18 +2818,43 @@ load_code(LoaderState* stp) switch (stp->specific_op) { case op_i_func_info_IaaI: { + int padding_required; Sint offset; + if (function_number >= stp->num_functions) { LoadError1(stp, "too many functions in module (header said %u)", stp->num_functions); } - if (stp->may_load_nif) { + /* Native function calls may be larger than their stubs, so + * we'll need to make sure any potentially-native function stub + * is padded with enough room. + * + * Note that the padding is applied for the previous function, + * not the current one, so we check whether the old F/A is + * a BIF. */ + padding_required = last_func_start && (stp->may_load_nif || + is_bif(stp->module, stp->function, stp->arity)); + + /* + * Save context for error messages. + */ + stp->function = code[ci-2]; + stp->arity = code[ci-1]; + + /* + * Save current offset of into the line instruction array. + */ + if (stp->func_line) { + stp->func_line[function_number] = stp->current_li; + } + + if (padding_required) { const int finfo_ix = ci - FUNC_INFO_SZ; - if (finfo_ix - last_func_start < BEAM_NIF_MIN_FUNC_SZ && last_func_start) { + if (finfo_ix - last_func_start < BEAM_NATIVE_MIN_FUNC_SZ) { /* Must make room for call_nif op */ - int pad = BEAM_NIF_MIN_FUNC_SZ - (finfo_ix - last_func_start); - ASSERT(pad > 0 && pad < BEAM_NIF_MIN_FUNC_SZ); + int pad = BEAM_NATIVE_MIN_FUNC_SZ - (finfo_ix - last_func_start); + ASSERT(pad > 0 && pad < BEAM_NATIVE_MIN_FUNC_SZ); CodeNeed(pad); sys_memmove(&code[finfo_ix+pad], &code[finfo_ix], FUNC_INFO_SZ*sizeof(BeamInstr)); @@ -2867,20 +2865,6 @@ load_code(LoaderState* stp) } last_func_start = ci; - /* - * Save current offset of into the line instruction array. - */ - - if (stp->func_line) { - stp->func_line[function_number] = stp->current_li; - } - - /* - * Save context for error messages. - */ - stp->function = code[ci-2]; - stp->arity = code[ci-1]; - /* When this assert is triggered, it is normally a sign that the size of the ops.tab i_func_info instruction is not the same as FUNC_INFO_SZ */ @@ -2910,7 +2894,6 @@ load_code(LoaderState* stp) case op_i_bs_match_string_yfWW: new_string_patch(stp, ci-1); break; - case op_catch_yf: /* code[ci-3] &&lb_catch_yf * code[ci-2] y-register offset in E @@ -3184,6 +3167,25 @@ is_killed_by_make_fun(LoaderState* stp, GenOpArg Reg, GenOpArg idx) } } +/* Test whether Bif is "heavy" and should always go through its export entry */ +static int +is_heavy_bif(LoaderState* stp, GenOpArg Bif) +{ + Export *ep; + + if (Bif.type != TAG_u || Bif.val >= stp->num_imports) { + return 0; + } + + ep = stp->import[Bif.val].bif; + + if (ep) { + return bif_table[ep->bif_number].kind == BIF_KIND_HEAVY; + } + + return 0; +} + /* * Generate an instruction for element/2. */ @@ -5211,26 +5213,51 @@ final_touch(LoaderState* stp, struct erl_module_instance* inst_p) */ for (i = 0; i < stp->num_exps; i++) { - Export* ep; - BeamInstr* address = stp->export[i].address; + Export* ep; + BeamInstr* address = stp->export[i].address; - if (address == NULL) { - /* Skip stub for a BIF */ - continue; - } - ep = erts_export_put(stp->module, stp->export[i].function, - stp->export[i].arity); - if (on_load) { - /* - * on_load: Don't make any of the exported functions - * callable yet. Keep any function in the current - * code callable. - */ - ep->beam[1] = (BeamInstr) address; - } - else + ep = erts_export_put(stp->module, + stp->export[i].function, + stp->export[i].arity); + + /* Fill in BIF stubs with a proper call to said BIF. */ + if (ep->bif_number != -1) { + erts_write_bif_wrapper(ep, address); + } + + if (on_load) { + /* + * on_load: Don't make any of the exported functions + * callable yet. Keep any function in the current + * code callable. + */ + ep->trampoline.not_loaded.deferred = (BeamInstr) address; + } else { ep->addressv[erts_staging_code_ix()] = address; + } + } + +#ifdef DEBUG + /* Ensure that we've loaded stubs for all BIFs in this module. */ + for (i = 0; i < BIF_SIZE; i++) { + BifEntry *entry = &bif_table[i]; + + if (stp->module == entry->module) { + Export *ep = erts_export_put(entry->module, + entry->name, + entry->arity); + BeamInstr *addr = ep->addressv[erts_staging_code_ix()]; + + if (!ErtsInArea(addr, stp->codev, stp->ci * sizeof(BeamInstr))) { + erts_exit(ERTS_ABORT_EXIT, + "Module %T doesn't export BIF %T/%i\n", + entry->module, + entry->name, + entry->arity); + } + } } +#endif /* * Import functions and patch all callers. @@ -5403,15 +5430,16 @@ transform_engine(LoaderState* st) i = instr->a[ap].val; ASSERT(i < st->num_imports); - if (i >= st->num_imports || st->import[i].bf == NULL) + if (i >= st->num_imports || st->import[i].bif == NULL) goto restart; - if (bif_number != -1 && - bif_export[bif_number]->beam[1] != (BeamInstr) st->import[i].bf) { - goto restart; - } + if (bif_number != -1) { + Export *bif = st->import[i].bif; + if (bif->bif_number != bif_number) { + goto restart; + } + } } break; - #endif #if defined(TOP_is_not_bif) case TOP_is_not_bif: @@ -5441,7 +5469,7 @@ transform_engine(LoaderState* st) * they are special. */ if (i < st->num_imports) { - if (st->import[i].bf != NULL || + if (st->import[i].bif != NULL || (st->import[i].module == am_erlang && st->import[i].function == am_apply && (st->import[i].arity == 2 || st->import[i].arity == 3))) { @@ -6286,12 +6314,12 @@ exported_from_module(Process* p, /* Process whose heap to use. */ if (ep->info.mfa.module == mod) { Eterm tuple; - - if (ep->addressv[code_ix] == ep->beam && - BeamIsOpCode(ep->beam[0], op_call_error_handler)) { - /* There is a call to the function, but it does not exist. */ - continue; - } + + if (ep->addressv[code_ix] == ep->trampoline.raw && + BeamIsOpCode(ep->trampoline.op, op_call_error_handler)) { + /* There is a call to the function, but it does not exist. */ + continue; + } if (hp == hend) { int need = 10 * 5; diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index 156c3c45e2..e7127c5b08 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -106,7 +106,7 @@ typedef struct beam_code_header { }BeamCodeHeader; -# define BEAM_NIF_MIN_FUNC_SZ 4 +# define BEAM_NATIVE_MIN_FUNC_SZ 4 void erts_release_literal_area(struct ErtsLiteralArea_* literal_area); int erts_is_module_native(BeamCodeHeader* code); diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 7afbbfd894..14f6c44cb8 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2160,7 +2160,7 @@ BIF_RETTYPE send_3(BIF_ALIST_3) break; case SEND_YIELD: if (suspend) { - ERTS_BIF_PREP_YIELD3(retval, bif_export[BIF_send_3], p, to, msg, opts); + ERTS_BIF_PREP_YIELD3(retval, &bif_trap_export[BIF_send_3], p, to, msg, opts); } else { ERTS_BIF_PREP_RET(retval, am_nosuspend); } @@ -2277,7 +2277,7 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg) ERTS_BIF_PREP_RET(retval, msg); break; case SEND_YIELD: - ERTS_BIF_PREP_YIELD2(retval, bif_export[BIF_send_2], p, to, msg); + ERTS_BIF_PREP_YIELD2(retval, &bif_trap_export[BIF_send_2], p, to, msg); break; case SEND_YIELD_RETURN: yield_return: @@ -2584,7 +2584,7 @@ BIF_RETTYPE iolist_size_1(BIF_ALIST_1) } else { ERTS_BIF_ERROR_TRAPPED1(BIF_P, BADARG, - bif_export[BIF_iolist_size_1], + &bif_trap_export[BIF_iolist_size_1], input_list); } @@ -2604,7 +2604,7 @@ BIF_RETTYPE iolist_size_1(BIF_ALIST_1) ESTACK_SAVE(s, &context->stack); erts_set_gc_state(BIF_P, 0); BUMP_ALL_REDS(BIF_P); - BIF_TRAP1(bif_export[BIF_iolist_size_1], BIF_P, state_mref); + BIF_TRAP1(&bif_trap_export[BIF_iolist_size_1], BIF_P, state_mref); } /**********************************************************************/ @@ -3942,7 +3942,7 @@ BIF_RETTYPE halt_2(BIF_ALIST_2) ("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2)); if (flush) { erts_halt(pos_int_code); - ERTS_BIF_YIELD2(bif_export[BIF_halt_2], BIF_P, am_undefined, am_undefined); + ERTS_BIF_YIELD2(&bif_trap_export[BIF_halt_2], BIF_P, am_undefined, am_undefined); } else { erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); @@ -4527,7 +4527,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) BIF_RET(am_enabled); case ERTS_SCHDLR_SSPND_YIELD_RESTART: ERTS_VBUMP_ALL_REDS(BIF_P); - BIF_TRAP2(bif_export[BIF_system_flag_2], + BIF_TRAP2(&bif_trap_export[BIF_system_flag_2], BIF_P, BIF_ARG_1, BIF_ARG_2); case ERTS_SCHDLR_SSPND_YIELD_DONE: ERTS_BIF_YIELD_RETURN_X(BIF_P, am_enabled, @@ -4552,7 +4552,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) BIF_RET(make_small(old_no)); case ERTS_SCHDLR_SSPND_YIELD_RESTART: ERTS_VBUMP_ALL_REDS(BIF_P); - BIF_TRAP2(bif_export[BIF_system_flag_2], + BIF_TRAP2(&bif_trap_export[BIF_system_flag_2], BIF_P, BIF_ARG_1, BIF_ARG_2); case ERTS_SCHDLR_SSPND_YIELD_DONE: ERTS_BIF_YIELD_RETURN_X(BIF_P, make_small(old_no), @@ -4716,7 +4716,7 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) BIF_RET(make_small(old_no)); case ERTS_SCHDLR_SSPND_YIELD_RESTART: ERTS_VBUMP_ALL_REDS(BIF_P); - BIF_TRAP2(bif_export[BIF_system_flag_2], + BIF_TRAP2(&bif_trap_export[BIF_system_flag_2], BIF_P, BIF_ARG_1, BIF_ARG_2); case ERTS_SCHDLR_SSPND_YIELD_DONE: ERTS_BIF_YIELD_RETURN_X(BIF_P, make_small(old_no), @@ -4871,7 +4871,7 @@ BIF_RETTYPE phash2_1(BIF_ALIST_1) if (trap_state == THE_NON_VALUE) { BIF_RET(make_small(hash & ((1L << 27) - 1))); } else { - BIF_TRAP1(bif_export[BIF_phash2_1], BIF_P, trap_state); + BIF_TRAP1(&bif_trap_export[BIF_phash2_1], BIF_P, trap_state); } } @@ -4894,7 +4894,7 @@ BIF_RETTYPE phash2_2(BIF_ALIST_2) } hash = trapping_make_hash2(BIF_ARG_1, &trap_state, BIF_P); if (trap_state != THE_NON_VALUE) { - BIF_TRAP2(bif_export[BIF_phash2_2], BIF_P, trap_state, BIF_ARG_2); + BIF_TRAP2(&bif_trap_export[BIF_phash2_2], BIF_P, trap_state, BIF_ARG_2); } if (range) { final_hash = hash % range; /* [0..range-1] */ @@ -4971,15 +4971,32 @@ void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a, Eterm (*bif)(BIF_ALIST)) { int i; + sys_memset((void *) ep, 0, sizeof(Export)); + for (i=0; i<ERTS_NUM_CODE_IX; i++) { - ep->addressv[i] = ep->beam; + ep->addressv[i] = ep->trampoline.raw; } + + ep->bif_number = -1; + + ep->info.op = op_i_func_info_IaaI; ep->info.mfa.module = m; ep->info.mfa.function = f; ep->info.mfa.arity = a; - ep->beam[0] = BeamOpCodeAddr(op_apply_bif); - ep->beam[1] = (BeamInstr) bif; + + ep->trampoline.op = BeamOpCodeAddr(op_call_bif_W); + ep->trampoline.raw[1] = (BeamInstr)bif; +} + +/* + * Writes a BIF call wrapper to the given address. + */ +void erts_write_bif_wrapper(Export *export, BeamInstr *address) { + BifEntry *entry = &bif_table[export->bif_number]; + + address[0] = BeamOpCodeAddr(op_call_bif_W); + address[1] = (BeamInstr)entry->f; } void erts_init_bif(void) @@ -5031,7 +5048,7 @@ void erts_init_bif(void) } /* - * Scheduling of BIFs via NifExport... + * Scheduling of BIFs via ErtsNativeFunc... */ #define ERTS_WANT_NFUNC_SCHED_INTERNALS__ #include "erl_nfunc_sched.h" @@ -5046,8 +5063,8 @@ schedule(Process *c_p, Process *dirty_shadow_proc, int argc, Eterm *argv) { ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p)); - (void) erts_nif_export_schedule(c_p, dirty_shadow_proc, - mfa, pc, BeamOpCodeAddr(op_apply_bif), + (void) erts_nfunc_schedule(c_p, dirty_shadow_proc, + mfa, pc, BeamOpCodeAddr(op_call_bif_W), dfunc, ifunc, module, function, argc, argv); @@ -5056,23 +5073,23 @@ schedule(Process *c_p, Process *dirty_shadow_proc, static BIF_RETTYPE dirty_bif_result(BIF_ALIST_1) { - NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(BIF_P); - erts_nif_export_restore(BIF_P, nep, BIF_ARG_1); + ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(BIF_P); + erts_nfunc_restore(BIF_P, nep, BIF_ARG_1); BIF_RET(BIF_ARG_1); } static BIF_RETTYPE dirty_bif_trap(BIF_ALIST) { - NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(BIF_P); + ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(BIF_P); /* * Arity and argument registers already set * correct by call to dirty_bif_trap()... */ - ASSERT(BIF_P->arity == nep->exp.info.mfa.arity); + ASSERT(BIF_P->arity == nep->trampoline.info.mfa.arity); - erts_nif_export_restore(BIF_P, nep, THE_NON_VALUE); + erts_nfunc_restore(BIF_P, nep, THE_NON_VALUE); BIF_P->i = (BeamInstr *) nep->func; BIF_P->freason = TRAP; @@ -5087,8 +5104,8 @@ static BIF_RETTYPE dirty_bif_exception(BIF_ALIST_2) freason = signed_val(BIF_ARG_1); - /* Restore orig info for error and clear nif export in handle_error() */ - freason |= EXF_RESTORE_NIF; + /* Restore orig info for error and clear nif wrapper in handle_error() */ + freason |= EXF_RESTORE_NFUNC; BIF_P->fvalue = BIF_ARG_2; @@ -5126,6 +5143,7 @@ erts_schedule_bif(Process *proc, if (!ERTS_PROC_IS_EXITING(c_p)) { Export *exp; BifFunction dbif, ibif; + BeamInstr call_instr; BeamInstr *pc; /* @@ -5160,27 +5178,40 @@ erts_schedule_bif(Process *proc, if (i == NULL) { ERTS_INTERNAL_ERROR("Missing instruction pointer"); } + + if (BeamIsOpCode(*i, op_i_generic_breakpoint)) { + ErtsCodeInfo *ci; + GenericBp *bp; + + ci = erts_code_to_codeinfo(i); + bp = ci->u.gen_bp; + + call_instr = bp->orig_instr; + } else { + call_instr = *i; + } + #ifdef HIPE - else if (proc->flags & F_HIPE_MODE) { + if (proc->flags & F_HIPE_MODE) { /* Pointer to bif export in i */ exp = (Export *) i; pc = cp_val(c_p->stop[0]); mfa = &exp->info.mfa; - } + } else /* !! This is part of the if clause below !! */ #endif - else if (BeamIsOpCode(*i, op_call_bif_e)) { - /* Pointer to bif export in i+1 */ - exp = (Export *) i[1]; + if (BeamIsOpCode(call_instr, op_call_light_bif_be)) { + /* Pointer to bif export in i+2 */ + exp = (Export *) i[2]; pc = i; mfa = &exp->info.mfa; } - else if (BeamIsOpCode(*i, op_call_bif_only_e)) { - /* Pointer to bif export in i+1 */ - exp = (Export *) i[1]; + else if (BeamIsOpCode(call_instr, op_call_light_bif_only_be)) { + /* Pointer to bif export in i+2 */ + exp = (Export *) i[2]; pc = i; mfa = &exp->info.mfa; } - else if (BeamIsOpCode(*i, op_apply_bif)) { + else if (BeamIsOpCode(call_instr, op_call_bif_W)) { pc = cp_val(c_p->stop[0]); mfa = erts_code_to_codemfa(i); } @@ -5209,7 +5240,7 @@ erts_schedule_bif(Process *proc, static BIF_RETTYPE call_bif(Process *c_p, Eterm *reg, BeamInstr *I) { - NifExport *nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I); + ErtsNativeFunc *nep = ERTS_I_BEAM_OP_TO_NFUNC(I); ErtsBifFunc bif = (ErtsBifFunc) nep->func; BIF_RETTYPE ret; @@ -5222,12 +5253,12 @@ call_bif(Process *c_p, Eterm *reg, BeamInstr *I) ret = (*bif)(c_p, reg, I); if (is_value(ret)) - erts_nif_export_restore(c_p, nep, ret); + erts_nfunc_restore(c_p, nep, ret); else if (c_p->freason != TRAP) - c_p->freason |= EXF_RESTORE_NIF; /* restore in handle_error() */ + c_p->freason |= EXF_RESTORE_NFUNC; /* restore in handle_error() */ else if (nep->func == ERTS_SCHED_BIF_TRAP_MARKER) { /* BIF did an ordinary trap... */ - erts_nif_export_restore(c_p, nep, ret); + erts_nfunc_restore(c_p, nep, ret); } /* else: * BIF rescheduled itself using erts_schedule_bif(). @@ -5244,7 +5275,7 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm * int exiting; Process *dirty_shadow_proc; ErtsBifFunc bf; - NifExport *nep; + ErtsNativeFunc *nep; #ifdef DEBUG Eterm *c_p_htop; erts_aint32_t state; @@ -5257,8 +5288,8 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm * #endif - nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I); - ASSERT(nep == ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p)); + nep = ERTS_I_BEAM_OP_TO_NFUNC(I); + ASSERT(nep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p)); nep->func = ERTS_SCHED_BIF_TRAP_MARKER; diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index c9f5177bd3..19dabc0514 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -25,13 +25,14 @@ # # <bif-decl> ::= "bif" <bif> <C-name>* | # "ubif" <bif> <C-name>* | -# "gcbif" <bif> <C-name>* +# "hbif" <bif> <C-name>* # <bif> ::= <module> ":" <name> "/" <arity> # -# ubif: Use for operators and guard BIFs that never build anything -# on the heap (such as tuple_size/1) and operators. +# ubif: Use for operators and guard BIFs. # -# gcbif: Use for guard BIFs that may build on the heap (such as abs/1). +# hbif: Use for BIFs that perform garbage collection or need up-to-date +# information on where they were called from. These must be called +# through the export entry. # # bif: Use for all other BIFs. # @@ -60,7 +61,7 @@ bif erlang:display_string/1 bif erlang:display_nl/0 ubif erlang:element/2 bif erlang:erase/0 -bif erlang:erase/1 +hbif erlang:erase/1 bif erlang:exit/1 bif erlang:exit/2 bif erlang:exit_signal/2 @@ -70,7 +71,7 @@ ubif erlang:float/1 bif erlang:float_to_list/1 bif erlang:float_to_list/2 bif erlang:fun_info/2 -bif erts_internal:garbage_collect/1 +hbif erts_internal:garbage_collect/1 bif erlang:get/0 bif erlang:get/1 bif erlang:get_keys/1 @@ -127,10 +128,10 @@ bif erlang:ports/0 bif erlang:pre_loaded/0 bif erlang:process_flag/2 bif erts_internal:process_flag/3 -bif erlang:process_info/1 -bif erlang:process_info/2 +hbif erlang:process_info/1 +hbif erlang:process_info/2 bif erlang:processes/0 -bif erlang:put/2 +hbif erlang:put/2 bif erlang:register/2 bif erlang:registered/0 ubif erlang:round/1 @@ -174,7 +175,7 @@ bif erts_internal:port_connect/2 bif erts_internal:request_system_task/3 bif erts_internal:request_system_task/4 -bif erts_internal:check_process_code/1 +hbif erts_internal:check_process_code/1 bif erts_internal:map_to_tuple_keys/1 bif erts_internal:term_type/1 @@ -466,7 +467,7 @@ bif code:is_module_native/1 # New Bifs in R9C. # -bif erlang:hibernate/3 +hbif erlang:hibernate/3 bif error_logger:warning_map/0 # diff --git a/erts/emulator/beam/bif_instrs.tab b/erts/emulator/beam/bif_instrs.tab index f1877882a1..de5305bde4 100644 --- a/erts/emulator/beam/bif_instrs.tab +++ b/erts/emulator/beam/bif_instrs.tab @@ -212,26 +212,32 @@ i_length.execute(Fail, Live, Dst) { // Call a BIF, store the result in x(0) and transfer control to the // next instruction. // -call_bif(Exp) { +call_light_bif(Bif, Exp) { + Export *export; ErtsBifFunc bf; + Eterm result; ErlHeapFragment *live_hf_end; - Export *export = (Export*) $Exp; + + bf = (ErtsBifFunc) $Bif; + export = (Export*) $Exp; if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) { /* * If we have run out of reductions, do a context * switch before calling the BIF. */ - c_p->arity = GET_BIF_ARITY(export); + c_p->arity = GET_EXPORT_ARITY(export); c_p->current = &export->info.mfa; goto context_switch3; } - ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(export), - GET_BIF_ADDRESS(export)); + if (ERTS_UNLIKELY(export->is_bif_traced)) { + $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION); + $DISPATCH_EXPORT(export); + } - bf = GET_BIF_ADDRESS(export); + ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_EXPORT_MODULE(export), bf); PRE_BIF_SWAPOUT(c_p); ERTS_DBG_CHK_REDS(c_p, FCALLS); @@ -243,21 +249,26 @@ call_bif(Exp) { ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); live_hf_end = c_p->mbuf; ERTS_CHK_MBUF_SZ(c_p); + result = (*bf)(c_p, reg, I); + + /* Only heavy BIFs may GC. */ + ASSERT(E == c_p->stop); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_HOLE_CHECK(c_p); ERTS_REQ_PROC_MAIN_LOCK(c_p); if (ERTS_IS_GC_DESIRED(c_p)) { - Uint arity = GET_BIF_ARITY(export); + Uint arity = GET_EXPORT_ARITY(export); result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result, reg, arity); E = c_p->stop; } - PROCESS_MAIN_CHK_LOCKS(c_p); HTOP = HEAP_TOP(c_p); FCALLS = c_p->fcalls; + PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_DBG_CHK_REDS(c_p, FCALLS); /* @@ -282,8 +293,7 @@ call_bif(Exp) { */ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION); SET_I(c_p->i); - SWAPIN; - Dispatch(); + $DISPATCH(); } /* @@ -299,27 +309,36 @@ call_bif(Exp) { // Call a BIF tail-recursively, storing the result in x(0) and doing // a return to the continuation poiner. // - -call_bif_only(Exp) { +call_light_bif_only(Bif, Exp) { + ErlHeapFragment *live_hf_end; ErtsBifFunc bf; + Export *export; Eterm result; - ErlHeapFragment *live_hf_end; - Export *export = (Export*) $Exp; + + bf = (ErtsBifFunc) $Bif; + export = (Export*) $Exp; if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) { /* * If we have run out of reductions, do a context * switch before calling the BIF. */ - c_p->arity = GET_BIF_ARITY(export); + c_p->arity = GET_EXPORT_ARITY(export); c_p->current = &export->info.mfa; goto context_switch3; } - ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(export), - GET_BIF_ADDRESS(export)); + if (ERTS_UNLIKELY(export->is_bif_traced)) { + /* Set up a dummy stack frame so we can perform a normal call. Loader + * transformations ensure that the next instruction after this is + * 'deallocate_return 0'. */ + $AH(0, 0, GET_EXPORT_ARITY(export)); + + $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION); + $DISPATCH_EXPORT(export); + } - bf = GET_BIF_ADDRESS(export); + ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_EXPORT_MODULE(export), bf); PRE_BIF_SWAPOUT(c_p); ERTS_DBG_CHK_REDS(c_p, FCALLS); @@ -331,21 +350,26 @@ call_bif_only(Exp) { ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); live_hf_end = c_p->mbuf; ERTS_CHK_MBUF_SZ(c_p); + result = (*bf)(c_p, reg, I); + + /* Only heavy BIFs may GC. */ + ASSERT(E == c_p->stop); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_HOLE_CHECK(c_p); ERTS_REQ_PROC_MAIN_LOCK(c_p); if (ERTS_IS_GC_DESIRED(c_p)) { - Uint arity = GET_BIF_ARITY(export); + Uint arity = GET_EXPORT_ARITY(export); result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result, reg, arity); E = c_p->stop; } - PROCESS_MAIN_CHK_LOCKS(c_p); HTOP = HEAP_TOP(c_p); FCALLS = c_p->fcalls; + PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_DBG_CHK_REDS(c_p, FCALLS); /* @@ -370,8 +394,7 @@ call_bif_only(Exp) { * to the continuation pointer on the stack will be done. */ SET_I(c_p->i); - SWAPIN; - Dispatch(); + $DISPATCH(); } /* @@ -416,14 +439,14 @@ send() { $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION); SET_I(c_p->i); SWAPIN; - Dispatch(); + $DISPATCH(); } else { goto find_func_info; } } call_nif := nif_bif.call_nif.epilogue; -apply_bif := nif_bif.apply_bif.epilogue; +call_bif := nif_bif.call_bif.epilogue; nif_bif.head() { Eterm nif_bif_result; @@ -433,7 +456,7 @@ nif_bif.head() { ErtsCodeMFA *codemfa; } -nif_bif.call_nif() { +nif_bif.call_nif(Func, NifMod, DirtyFunc) { /* * call_nif is always first instruction in function: * @@ -443,11 +466,14 @@ nif_bif.call_nif() { * I[0]: &&call_nif * I[1]: Function pointer to NIF function * I[2]: Pointer to erl_module_nif - * I[3]: Function pointer to dirty NIF + * I[3]: Function pointer to dirty NIF. This is not used in this + * instruction, but dirty schedulers look at it. * - * This layout is determined by the NifExport struct + * This layout is determined by the ErtsNativeFunc struct */ + (void)$DirtyFunc; + ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF); codemfa = erts_code_to_codemfa(I); @@ -465,12 +491,12 @@ nif_bif.call_nif() { ASSERT(!ERTS_PROC_IS_EXITING(c_p)); { typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]); - NifF* fp = vbf = (NifF*) I[1]; + NifF* fp = vbf = (NifF*) $Func; struct enif_environment_t env; ASSERT(c_p->scheduler_data); live_hf_end = c_p->mbuf; ERTS_CHK_MBUF_SZ(c_p); - erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL); + erts_pre_nif(&env, c_p, (struct erl_module_nif*)$NifMod, NULL); ASSERT((c_p->scheduler_data)->current_nif == NULL); (c_p->scheduler_data)->current_nif = &env; @@ -495,15 +521,15 @@ nif_bif.call_nif() { DTRACE_NIF_RETURN(c_p, codemfa); } -nif_bif.apply_bif() { +nif_bif.call_bif(Func) { /* - * At this point, I points to the code[0] in the export entry for - * the BIF: + * At this point, I points to the code[0] in the native function wrapper + * for the BIF: * * code[-3]: Module * code[-2]: Function * code[-1]: Arity - * code[0]: &&apply_bif + * code[0]: &&call_bif * code[1]: Function pointer to BIF function */ @@ -515,21 +541,19 @@ nif_bif.apply_bif() { codemfa = erts_code_to_codemfa(I); - ERTS_MSACC_SET_BIF_STATE_CACHED_X(codemfa->module, (BifFunction)Arg(0)); - + ERTS_MSACC_SET_BIF_STATE_CACHED_X(codemfa->module, (BifFunction)$Func); /* In case we apply process_info/1,2 or load_nif/1 */ c_p->current = codemfa; $SET_CP_I_ABS(I); /* In case we apply check_process_code/2. */ c_p->arity = 0; /* To allow garbage collection on ourselves - * (check_process_code/2). - */ + * (check_process_code/2, put/2, etc). */ DTRACE_BIF_ENTRY(c_p, codemfa); SWAPOUT; ERTS_DBG_CHK_REDS(c_p, FCALLS - 1); c_p->fcalls = FCALLS - 1; - vbf = (BifFunction) Arg(0); + vbf = (BifFunction)$Func; PROCESS_MAIN_CHK_LOCKS(c_p); bif_nif_arity = codemfa->arity; ASSERT(bif_nif_arity <= 4); @@ -557,6 +581,7 @@ nif_bif.apply_bif() { } nif_bif.epilogue() { + //| -no_next ERTS_REQ_PROC_MAIN_LOCK(c_p); ERTS_HOLE_CHECK(c_p); if (ERTS_IS_GC_DESIRED(c_p)) { @@ -578,12 +603,42 @@ nif_bif.epilogue() { c_p->flags &= ~F_HIBERNATE_SCHED; goto do_schedule; } - Dispatch(); + $DISPATCH(); } { - BeamInstr *cp = cp_val(*E); + BeamInstr *cp = erts_printable_return_address(c_p, E); ASSERT(VALID_INSTR(*cp)); I = handle_error(c_p, cp, reg, c_p->current); } goto post_error_handling; } + +i_load_nif() { + //| -no_next + if (erts_try_seize_code_write_permission(c_p)) { + Eterm result; + + PRE_BIF_SWAPOUT(c_p); + result = erts_load_nif(c_p, I, r(0), r(1)); + erts_release_code_write_permission(); + ERTS_REQ_PROC_MAIN_LOCK(c_p); + SWAPIN; + + if (ERTS_LIKELY(is_value(result))) { + r(0) = result; + $NEXT0(); + } else { + static ErtsCodeMFA mfa = {am_erlang, am_load_nif, 2}; + c_p->freason = BADARG; + I = handle_error(c_p, I, reg, &mfa); + goto post_error_handling; + } + } else { + /* Yield and try again */ + $SET_CP_I_ABS(I); + SWAPOUT; + c_p->current = NULL; + c_p->arity = 2; + goto do_schedule; + } +} diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index 4ddf59092a..0ae4bc1e60 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -567,7 +567,7 @@ BIF_RETTYPE binary_to_list_1(BIF_ALIST_1) if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) { if (reds_left <= L2B_B2L_RESCHED_REDS) { /* Yield and do it with full context reds... */ - ERTS_BIF_YIELD1(bif_export[BIF_binary_to_list_1], + ERTS_BIF_YIELD1(&bif_trap_export[BIF_binary_to_list_1], BIF_P, BIF_ARG_1); } /* Allow a bit more reductions... */ @@ -621,7 +621,7 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3) if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) { if (reds_left <= L2B_B2L_RESCHED_REDS) { /* Yield and do it with full context reds... */ - ERTS_BIF_YIELD3(bif_export[BIF_binary_to_list_3], + ERTS_BIF_YIELD3(&bif_trap_export[BIF_binary_to_list_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); } /* Allow a bit more reductions... */ @@ -668,7 +668,7 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1) if (size < L2B_B2L_MIN_EXEC_REDS*ERTS_B2L_BYTES_PER_REDUCTION) { if (reds_left <= L2B_B2L_RESCHED_REDS) { /* Yield and do it with full context reds... */ - ERTS_BIF_YIELD1(bif_export[BIF_bitstring_to_list_1], + ERTS_BIF_YIELD1(&bif_trap_export[BIF_bitstring_to_list_1], BIF_P, BIF_ARG_1); } /* Allow a bit more reductions... */ @@ -1041,7 +1041,7 @@ HIPE_WRAPPER_BIF_DISABLE_GC(list_to_binary, 1) BIF_RETTYPE list_to_binary_1(BIF_ALIST_1) { - return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_list_to_binary_1]); + return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, &bif_trap_export[BIF_list_to_binary_1]); } HIPE_WRAPPER_BIF_DISABLE_GC(iolist_to_binary, 1) @@ -1054,7 +1054,7 @@ BIF_RETTYPE iolist_to_binary_1(BIF_ALIST_1) } BIF_ERROR(BIF_P, BADARG); } - return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_iolist_to_binary_1]); + return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, &bif_trap_export[BIF_iolist_to_binary_1]); } static int bitstr_list_len(ErtsIOListState *); @@ -1081,7 +1081,7 @@ BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1) else { ErtsL2BState state = ERTS_L2B_STATE_INITER(BIF_P, BIF_ARG_1, - bif_export[BIF_list_to_bitstring_1], + &bif_trap_export[BIF_list_to_bitstring_1], bitstr_list_len, list_to_bitstr_buf_yielding); int orig_reds_left = ERTS_BIF_REDS_LEFT(BIF_P); diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index dafe805a6f..3fdd20eecd 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -3437,7 +3437,7 @@ dist_ctrl_get_data_1(BIF_ALIST_1) erts_de_runlock(dep); if (obufsize) erts_atomic_add_nob(&dep->qsize, (erts_aint_t) -obufsize); - ERTS_BIF_YIELD1(bif_export[BIF_dist_ctrl_get_data_1], + ERTS_BIF_YIELD1(&bif_trap_export[BIF_dist_ctrl_get_data_1], BIF_P, BIF_ARG_1); } diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index b9f0334172..1bbc7d7f1e 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -653,8 +653,6 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) = erts_timer_type_size(ERTS_ALC_T_HL_PTIMER); fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_BIF_TIMER)] = erts_timer_type_size(ERTS_ALC_T_BIF_TIMER); - fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NIF_EXP_TRACE)] - = sizeof(NifExportTrace); fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MREF_NSCHED_ENT)] = sizeof(ErtsNSchedMagicRefTableEntry); fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MINDIRECTION)] @@ -2392,10 +2390,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg) &size.processes_used, fi, ERTS_ALC_T_BIF_TIMER); - add_fix_values(&size.processes, - &size.processes_used, - fi, - ERTS_ALC_T_NIF_EXP_TRACE); } if (want.atom || want.atom_used) { diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 349977ebe7..3e643a6223 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -331,8 +331,7 @@ type DB_HEIR_DATA STANDARD ETS db_heir_data type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data -type NIF_TRAP_EXPORT STANDARD PROCESSES nif_trap_export_entry -type NIF_EXP_TRACE FIXED_SIZE PROCESSES nif_export_trace +type NFUNC_TRAP_WRAPPER STANDARD PROCESSES nfunc_trap_wrapper type EXPORT LONG_LIVED CODE export_entry type MONITOR FIXED_SIZE PROCESSES monitor type MONITOR_SUSPEND STANDARD PROCESSES monitor_suspend diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index b8e56390c1..03b8e0e632 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -2413,7 +2413,7 @@ HIPE_WRAPPER_BIF_DISABLE_GC(binary_list_to_bin, 1) BIF_RETTYPE binary_list_to_bin_1(BIF_ALIST_1) { - return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_binary_list_to_bin_1]); + return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, &bif_trap_export[BIF_binary_list_to_bin_1]); } typedef struct { diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c index cce8472ccb..4cab9bec1d 100644 --- a/erts/emulator/beam/erl_bif_chksum.c +++ b/erts/emulator/beam/erl_bif_chksum.c @@ -327,7 +327,7 @@ crc32_1(BIF_ALIST_1) res_sum = erts_make_integer(chksum,BIF_P); if (rest != NIL) { BUMP_ALL_REDS(BIF_P); - BIF_TRAP2(bif_export[BIF_crc32_2], BIF_P, res_sum, rest); + BIF_TRAP2(&bif_trap_export[BIF_crc32_2], BIF_P, res_sum, rest); } BIF_RET(res_sum); } @@ -354,7 +354,7 @@ crc32_2(BIF_ALIST_2) res_sum = erts_make_integer(chksum,BIF_P); if (rest != NIL) { BUMP_ALL_REDS(BIF_P); - BIF_TRAP2(bif_export[BIF_crc32_2], BIF_P, res_sum, rest); + BIF_TRAP2(&bif_trap_export[BIF_crc32_2], BIF_P, res_sum, rest); } BIF_RET(res_sum); } @@ -407,7 +407,7 @@ adler32_1(BIF_ALIST_1) res_sum = erts_make_integer(chksum,BIF_P); if (rest != NIL) { BUMP_ALL_REDS(BIF_P); - BIF_TRAP2(bif_export[BIF_adler32_2], BIF_P, res_sum, rest); + BIF_TRAP2(&bif_trap_export[BIF_adler32_2], BIF_P, res_sum, rest); } BIF_RET(res_sum); } @@ -434,7 +434,7 @@ adler32_2(BIF_ALIST_2) res_sum = erts_make_integer(chksum,BIF_P); if (rest != NIL) { BUMP_ALL_REDS(BIF_P); - BIF_TRAP2(bif_export[BIF_adler32_2], BIF_P, res_sum, rest); + BIF_TRAP2(&bif_trap_export[BIF_adler32_2], BIF_P, res_sum, rest); } BIF_RET(res_sum); } @@ -575,7 +575,7 @@ md5_update_2(BIF_ALIST_2) bin = new_binary(BIF_P, (byte *) &context, sizeof(MD5_CTX)); if (rest != NIL) { BUMP_ALL_REDS(BIF_P); - BIF_TRAP2(bif_export[BIF_md5_update_2], BIF_P, bin, rest); + BIF_TRAP2(&bif_trap_export[BIF_md5_update_2], BIF_P, bin, rest); } BUMP_REDS(BIF_P,res); BIF_RET(bin); diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c index 09757e473b..053797bc89 100644 --- a/erts/emulator/beam/erl_bif_guard.c +++ b/erts/emulator/beam/erl_bif_guard.c @@ -239,7 +239,7 @@ static BIF_RETTYPE erlang_length_trap(BIF_ALIST_3) * Signal an error. The original argument was tucked away in BIF_ARG_3. */ ERTS_BIF_ERROR_TRAPPED1(BIF_P, BIF_P->freason, - bif_export[BIF_length_1], BIF_ARG_3); + &bif_trap_export[BIF_length_1], BIF_ARG_3); } } } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b51148eb9e..43be78715b 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -158,8 +158,9 @@ static Eterm os_version_tuple; static Eterm current_function(Process* p, ErtsHeapFactory *hfact, Process* rp, int full_info, Uint reserve_size, int flags); -static Eterm current_stacktrace(ErtsHeapFactory *hfact, Process* rp, - Uint reserve_size); +static Eterm +current_stacktrace(Process* p, ErtsHeapFactory *hfact, Process* rp, + Uint reserve_size, int flags); Eterm erts_bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh, Eterm tail) @@ -1249,9 +1250,9 @@ exited: yield: if (pi2) - ERTS_BIF_PREP_YIELD2(ret, bif_export[BIF_process_info_2], c_p, pid, opt); + ERTS_BIF_PREP_YIELD2(ret, &bif_trap_export[BIF_process_info_2], c_p, pid, opt); else - ERTS_BIF_PREP_YIELD1(ret, bif_export[BIF_process_info_1], c_p, pid); + ERTS_BIF_PREP_YIELD1(ret, &bif_trap_export[BIF_process_info_1], c_p, pid); goto done; send_signal: { @@ -1384,7 +1385,7 @@ process_info_aux(Process *c_p, break; case ERTS_PI_IX_CURRENT_STACKTRACE: - res = current_stacktrace(hfact, rp, reserve_size); + res = current_stacktrace(c_p, hfact, rp, reserve_size, flags); break; case ERTS_PI_IX_INITIAL_CALL: @@ -2022,21 +2023,23 @@ current_function(Process *c_p, ErtsHeapFactory *hfact, Process* rp, } if (c_p == rp && !(flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER)) { - FunctionInfo fi2; - BeamInstr* continuation_ptr; + BeamInstr* return_address; + FunctionInfo caller_fi; - /* - * The current function is erlang:process_info/{1,2}, - * which is not the answer that the application want. - * We will use the continuation pointer stored at the - * top of the stack instead. - */ - continuation_ptr = (BeamInstr *) rp->stop[0]; - erts_lookup_function_info(&fi2, continuation_ptr, full_info); - if (fi2.mfa) { - fi = fi2; - rp->current = fi2.mfa; - } + /* + * The current function is erlang:process_info/{1,2}, and we've + * historically returned the *calling* function in that case. We + * therefore use the continuation pointer stored at the top of the + * stack instead, which is safe since process_info is a "heavy" BIF + * that is only called through its export entry. + */ + return_address = erts_printable_return_address(rp, STACK_TOP(rp)); + + erts_lookup_function_info(&caller_fi, return_address, full_info); + if (caller_fi.mfa) { + fi = caller_fi; + rp->current = caller_fi.mfa; + } } /* @@ -2057,8 +2060,8 @@ current_function(Process *c_p, ErtsHeapFactory *hfact, Process* rp, } static Eterm -current_stacktrace(ErtsHeapFactory *hfact, Process* rp, - Uint reserve_size) +current_stacktrace(Process *p, ErtsHeapFactory *hfact, Process* rp, + Uint reserve_size, int flags) { Uint sz; struct StackTrace* s; @@ -2075,9 +2078,14 @@ current_stacktrace(ErtsHeapFactory *hfact, Process* rp, sz = offsetof(struct StackTrace, trace) + sizeof(BeamInstr *)*depth; s = (struct StackTrace *) erts_alloc(ERTS_ALC_T_TMP, sz); s->depth = 0; - if (depth > 0 && rp->i) { - s->trace[s->depth++] = rp->i; - depth--; + s->pc = NULL; + + /* We skip current pc when requesting our own stack trace since it will + * inevitably point to process_info/1,2 */ + if ((p != rp || (flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER)) && + depth > 0 && rp->i) { + s->trace[s->depth++] = rp->i; + depth--; } erts_save_stacktrace(rp, s, depth); @@ -4588,7 +4596,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) if (!flag && BIF_ARG_2 != am_false) { erts_atomic_set_nob(&hipe_test_reschedule_flag, 1); erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); - ERTS_BIF_YIELD2(bif_export[BIF_erts_debug_set_internal_state_2], + ERTS_BIF_YIELD2(&bif_trap_export[BIF_erts_debug_set_internal_state_2], BIF_P, BIF_ARG_1, BIF_ARG_2); } erts_atomic_set_nob(&hipe_test_reschedule_flag, !flag); diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index fa2edfef1e..40512a117d 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -32,8 +32,7 @@ #include "bif.h" #include "erl_binary.h" - -static Eterm keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List); +static Eterm keyfind(Export* Bif, Process* p, Eterm Key, Eterm Pos, Eterm List); /* erlang:'++'/2 * @@ -308,12 +307,12 @@ static Eterm append(Export *bif_entry, BIF_ALIST_2) { Eterm ebif_plusplus_2(BIF_ALIST_2) { - return append(bif_export[BIF_ebif_plusplus_2], BIF_CALL_ARGS); + return append(&bif_trap_export[BIF_ebif_plusplus_2], BIF_CALL_ARGS); } BIF_RETTYPE append_2(BIF_ALIST_2) { - return append(bif_export[BIF_append_2], BIF_CALL_ARGS); + return append(&bif_trap_export[BIF_append_2], BIF_CALL_ARGS); } /* erlang:'--'/2 @@ -1039,11 +1038,11 @@ static Eterm subtract(Export *bif_entry, BIF_ALIST_2) { } BIF_RETTYPE ebif_minusminus_2(BIF_ALIST_2) { - return subtract(bif_export[BIF_ebif_minusminus_2], BIF_CALL_ARGS); + return subtract(&bif_trap_export[BIF_ebif_minusminus_2], BIF_CALL_ARGS); } BIF_RETTYPE subtract_2(BIF_ALIST_2) { - return subtract(bif_export[BIF_subtract_2], BIF_CALL_ARGS); + return subtract(&bif_trap_export[BIF_subtract_2], BIF_CALL_ARGS); } @@ -1068,7 +1067,7 @@ BIF_RETTYPE lists_member_2(BIF_ALIST_2) while (is_list(list)) { if (--max_iter < 0) { BUMP_ALL_REDS(BIF_P); - BIF_TRAP2(bif_export[BIF_lists_member_2], BIF_P, term, list); + BIF_TRAP2(&bif_trap_export[BIF_lists_member_2], BIF_P, term, list); } item = CAR(list_val(list)); if ((item == term) || (non_immed_key && eq(item, term))) { @@ -1130,7 +1129,7 @@ static BIF_RETTYPE lists_reverse_alloc(Process *c_p, } ASSERT(is_list(tail) && cells_left == 0); - BIF_TRAP2(bif_export[BIF_lists_reverse_2], c_p, list, tail); + BIF_TRAP2(&bif_trap_export[BIF_lists_reverse_2], c_p, list, tail); } static BIF_RETTYPE lists_reverse_onheap(Process *c_p, @@ -1179,7 +1178,7 @@ static BIF_RETTYPE lists_reverse_onheap(Process *c_p, } BUMP_ALL_REDS(c_p); - BIF_TRAP2(bif_export[BIF_lists_reverse_2], c_p, list, tail); + BIF_TRAP2(&bif_trap_export[BIF_lists_reverse_2], c_p, list, tail); } BIF_ERROR(c_p, BADARG); @@ -1209,7 +1208,7 @@ lists_keymember_3(BIF_ALIST_3) { Eterm res; - res = keyfind(BIF_lists_keymember_3, BIF_P, + res = keyfind(&bif_trap_export[BIF_lists_keymember_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); if (is_value(res) && is_tuple(res)) { return am_true; @@ -1223,7 +1222,7 @@ lists_keysearch_3(BIF_ALIST_3) { Eterm res; - res = keyfind(BIF_lists_keysearch_3, BIF_P, + res = keyfind(&bif_trap_export[BIF_lists_keysearch_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); if (is_non_value(res) || is_not_tuple(res)) { return res; @@ -1236,12 +1235,12 @@ lists_keysearch_3(BIF_ALIST_3) BIF_RETTYPE lists_keyfind_3(BIF_ALIST_3) { - return keyfind(BIF_lists_keyfind_3, BIF_P, + return keyfind(&bif_trap_export[BIF_lists_keyfind_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); } static Eterm -keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List) +keyfind(Export *Bif, Process* p, Eterm Key, Eterm Pos, Eterm List) { int max_iter = 10 * CONTEXT_REDS; Sint pos; @@ -1257,7 +1256,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List) while (is_list(List)) { if (--max_iter < 0) { BUMP_ALL_REDS(p); - BIF_TRAP3(bif_export[Bif], p, Key, Pos, List); + BIF_TRAP3(Bif, p, Key, Pos, List); } term = CAR(list_val(List)); List = CDR(list_val(List)); @@ -1282,7 +1281,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List) while (is_list(List)) { if (--max_iter < 0) { BUMP_ALL_REDS(p); - BIF_TRAP3(bif_export[Bif], p, Key, Pos, List); + BIF_TRAP3(Bif, p, Key, Pos, List); } term = CAR(list_val(List)); List = CDR(list_val(List)); @@ -1300,7 +1299,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List) while (is_list(List)) { if (--max_iter < 0) { BUMP_ALL_REDS(p); - BIF_TRAP3(bif_export[Bif], p, Key, Pos, List); + BIF_TRAP3(Bif, p, Key, Pos, List); } term = CAR(list_val(List)); List = CDR(list_val(List)); diff --git a/erts/emulator/beam/erl_bif_persistent.c b/erts/emulator/beam/erl_bif_persistent.c index 9dc5c66a0a..d27f35c8ae 100644 --- a/erts/emulator/beam/erl_bif_persistent.c +++ b/erts/emulator/beam/erl_bif_persistent.c @@ -284,7 +284,7 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2) long iterations_until_trap; long max_iterations; #define PUT_TRAP_CODE \ - BIF_TRAP2(bif_export[BIF_persistent_term_put_2], BIF_P, state_mref, BIF_ARG_2) + BIF_TRAP2(&bif_trap_export[BIF_persistent_term_put_2], BIF_P, state_mref, BIF_ARG_2) #define TRAPPING_COPY_TABLE_PUT(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME) \ TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME, PUT_TRAP_CODE) @@ -329,7 +329,7 @@ BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2) if (!try_seize_update_permission(BIF_P)) { - ERTS_BIF_YIELD2(bif_export[BIF_persistent_term_put_2], + ERTS_BIF_YIELD2(&bif_trap_export[BIF_persistent_term_put_2], BIF_P, BIF_ARG_1, BIF_ARG_2); } ctx->hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table); @@ -507,7 +507,7 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1) ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(BIF_P); #endif #define ERASE_TRAP_CODE \ - BIF_TRAP1(bif_export[BIF_persistent_term_erase_1], BIF_P, state_mref); + BIF_TRAP1(&bif_trap_export[BIF_persistent_term_erase_1], BIF_P, state_mref); #define TRAPPING_COPY_TABLE_ERASE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME) \ TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME, ERASE_TRAP_CODE) if (is_internal_magic_ref(BIF_ARG_1) && @@ -542,7 +542,7 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1) ctx->tmp_table = NULL; } if (!try_seize_update_permission(BIF_P)) { - ERTS_BIF_YIELD1(bif_export[BIF_persistent_term_erase_1], + ERTS_BIF_YIELD1(&bif_trap_export[BIF_persistent_term_erase_1], BIF_P, BIF_ARG_1); } @@ -614,7 +614,7 @@ BIF_RETTYPE erts_internal_erase_persistent_terms_0(BIF_ALIST_0) HashTable* new_table; if (!try_seize_update_permission(BIF_P)) { - ERTS_BIF_YIELD0(bif_export[BIF_erts_internal_erase_persistent_terms_0], + ERTS_BIF_YIELD0(&bif_trap_export[BIF_erts_internal_erase_persistent_terms_0], BIF_P); } old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index dd1e884705..17e09f835b 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -211,7 +211,7 @@ BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3) ERTS_BIF_PREP_RET(res, am_false); else { erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, prt); - ERTS_BIF_PREP_YIELD3(res, bif_export[BIF_erts_internal_port_command_3], + ERTS_BIF_PREP_YIELD3(res, &bif_trap_export[BIF_erts_internal_port_command_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); } break; diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 80ba7d1b3c..46f4ed7ab0 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -80,9 +80,6 @@ static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_on_load(Process* p, Eterm key); static Eterm trace_info_event(Process* p, Eterm event, Eterm key); - -static void reset_bif_trace(void); -static void setup_bif_trace(void); static void install_exp_breakpoints(BpFunctions* f); static void uninstall_exp_breakpoints(BpFunctions* f); static void clean_export_entries(BpFunctions* f); @@ -133,7 +130,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) ErtsTracer meta_tracer = erts_tracer_nil; if (!erts_try_seize_code_write_permission(p)) { - ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_trace_pattern_3], p, MFA, Pattern, flaglist); + ERTS_BIF_YIELD3(&bif_trap_export[BIF_erts_internal_trace_pattern_3], p, MFA, Pattern, flaglist); } finish_bp.current = -1; @@ -543,7 +540,7 @@ Eterm erts_internal_trace_3(BIF_ALIST_3) } if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_trace_3], + ERTS_BIF_YIELD3(&bif_trap_export[BIF_erts_internal_trace_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); } @@ -793,7 +790,7 @@ Eterm trace_info_2(BIF_ALIST_2) Eterm res; if (!erts_try_seize_code_write_permission(p)) { - ERTS_BIF_YIELD2(bif_export[BIF_trace_info_2], p, What, Key); + ERTS_BIF_YIELD2(&bif_trap_export[BIF_trace_info_2], p, What, Key); } if (What == am_on_load) { @@ -1047,14 +1044,13 @@ static int function_is_traced(Process *p, e.info.mfa.function = mfa[1]; e.info.mfa.arity = mfa[2]; if ((ep = export_get(&e)) != NULL) { - pc = ep->beam; + pc = ep->trampoline.raw; if (ep->addressv[erts_active_code_ix()] == pc && ! BeamIsOpCode(*pc, op_call_error_handler)) { int r = 0; - ASSERT(BeamIsOpCode(*pc, op_apply_bif) || - BeamIsOpCode(*pc, op_i_generic_breakpoint)); + ASSERT(BeamIsOpCode(*pc, op_i_generic_breakpoint)); if (erts_is_trace_break(&ep->info, ms, 0)) { return FUNC_TRACE_GLOBAL_TRACE; @@ -1426,18 +1422,21 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified, int n; BpFunction* fp; - /* - * First work on normal functions (not real BIFs). - */ - erts_bp_match_export(&finish_bp.e, mfa, specified); fp = finish_bp.e.matching; n = finish_bp.e.matched; for (i = 0; i < n; i++) { ErtsCodeInfo *ci = fp[i].ci; - BeamInstr* pc = erts_codeinfo_to_code(ci); - Export* ep = ErtsContainerStruct(ci, Export, info); + BeamInstr* pc; + Export* ep; + + pc = erts_codeinfo_to_code(ci); + ep = ErtsContainerStruct(ci, Export, info); + + if (ep->bif_number != -1) { + ep->is_bif_traced = !!on; + } if (on && !flags.breakpoint) { /* Turn on global call tracing */ @@ -1446,12 +1445,12 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified, #ifdef DEBUG ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI); #endif - ep->beam[0] = BeamOpCodeAddr(op_trace_jump_W); - ep->beam[1] = (BeamInstr) ep->addressv[code_ix]; + ep->trampoline.op = BeamOpCodeAddr(op_trace_jump_W); + ep->trampoline.trace.address = (BeamInstr) ep->addressv[code_ix]; } - erts_set_call_trace_bif(ci, match_prog_set, 0); + erts_set_export_trace(ci, match_prog_set, 0); if (ep->addressv[code_ix] != pc) { - ep->beam[0] = BeamOpCodeAddr(op_i_generic_breakpoint); + ep->trampoline.op = BeamOpCodeAddr(op_i_generic_breakpoint); } } else if (!on && flags.breakpoint) { /* Turn off breakpoint tracing -- nothing to do here. */ @@ -1460,91 +1459,14 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified, * Turn off global tracing, either explicitly or implicitly * before turning on breakpoint tracing. */ - erts_clear_call_trace_bif(ci, 0); - if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) { - ep->beam[0] = BeamOpCodeAddr(op_trace_jump_W); + erts_clear_export_trace(ci, 0); + if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) { + ep->trampoline.op = BeamOpCodeAddr(op_trace_jump_W); } } } /* - ** OK, now for the bif's - */ - for (i = 0; i < BIF_SIZE; ++i) { - Export *ep = bif_export[i]; - - if (!ExportIsBuiltIn(ep)) { - continue; - } - - if (bif_table[i].f == bif_table[i].traced) { - /* Trace wrapper same as regular function - untraceable */ - continue; - } - - switch (specified) { - case 3: - if (mfa->arity != ep->info.mfa.arity) - continue; - case 2: - if (mfa->function != ep->info.mfa.function) - continue; - case 1: - if (mfa->module != ep->info.mfa.module) - continue; - case 0: - break; - default: - ASSERT(0); - } - - if (! flags.breakpoint) { /* Export entry call trace */ - if (on) { - erts_clear_call_trace_bif(&ep->info, 1); - erts_clear_mtrace_bif(&ep->info); - erts_set_call_trace_bif(&ep->info, match_prog_set, 0); - } else { /* off */ - erts_clear_call_trace_bif(&ep->info, 0); - } - matches++; - } else { /* Breakpoint call trace */ - int m = 0; - - if (on) { - if (flags.local) { - erts_clear_call_trace_bif(&ep->info, 0); - erts_set_call_trace_bif(&ep->info, match_prog_set, 1); - m = 1; - } - if (flags.meta) { - erts_set_mtrace_bif(&ep->info, meta_match_prog_set, - meta_tracer); - m = 1; - } - if (flags.call_time) { - erts_set_time_trace_bif(&ep->info, on); - /* I don't want to remove any other tracers */ - m = 1; - } - } else { /* off */ - if (flags.local) { - erts_clear_call_trace_bif(&ep->info, 1); - m = 1; - } - if (flags.meta) { - erts_clear_mtrace_bif(&ep->info); - m = 1; - } - if (flags.call_time) { - erts_clear_time_trace_bif(&ep->info); - m = 1; - } - } - matches += m; - } - } - - /* ** So, now for breakpoint tracing */ erts_bp_match_functions(&finish_bp.f, mfa, specified); @@ -1670,7 +1592,6 @@ erts_finish_breakpointing(void) install_exp_breakpoints(&finish_bp.e); } } - setup_bif_trace(); return 1; case 1: /* @@ -1699,7 +1620,6 @@ erts_finish_breakpointing(void) uninstall_exp_breakpoints(&finish_bp.e); } } - reset_bif_trace(); return 1; case 3: /* @@ -1710,7 +1630,6 @@ erts_finish_breakpointing(void) * updated). If any breakpoints have been totally disabled, * deallocate the GenericBp structs for them. */ - erts_consolidate_bif_bp_data(); clean_export_entries(&finish_bp.e); erts_consolidate_bp_data(&finish_bp.e, 0); erts_consolidate_bp_data(&finish_bp.f, 1); @@ -1736,7 +1655,7 @@ install_exp_breakpoints(BpFunctions* f) for (i = 0; i < ne; i++) { Export* ep = ErtsContainerStruct(fp[i].ci, Export, info); - ep->addressv[code_ix] = ep->beam; + ep->addressv[code_ix] = ep->trampoline.raw; } } @@ -1751,11 +1670,12 @@ uninstall_exp_breakpoints(BpFunctions* f) for (i = 0; i < ne; i++) { Export* ep = ErtsContainerStruct(fp[i].ci, Export, info); - if (ep->addressv[code_ix] != ep->beam) { - continue; - } - ASSERT(BeamIsOpCode(ep->beam[0], op_trace_jump_W)); - ep->addressv[code_ix] = (BeamInstr *) ep->beam[1]; + if (ep->addressv[code_ix] != ep->trampoline.raw) { + continue; + } + + ASSERT(BeamIsOpCode(ep->trampoline.op, op_trace_jump_W)); + ep->addressv[code_ix] = (BeamInstr *) ep->trampoline.trace.address; } } @@ -1770,48 +1690,14 @@ clean_export_entries(BpFunctions* f) for (i = 0; i < ne; i++) { Export* ep = ErtsContainerStruct(fp[i].ci, Export, info); - if (ep->addressv[code_ix] == ep->beam) { - continue; - } - if (BeamIsOpCode(ep->beam[0], op_trace_jump_W)) { - ep->beam[0] = (BeamInstr) 0; - ep->beam[1] = (BeamInstr) 0; - } - } -} - -static void -setup_bif_trace(void) -{ - int i; - - for (i = 0; i < BIF_SIZE; ++i) { - Export *ep = bif_export[i]; - GenericBp* g = ep->info.u.gen_bp; - if (g) { - if (ExportIsBuiltIn(ep)) { - ASSERT(ep->beam[1]); - ep->beam[1] = (BeamInstr) bif_table[i].traced; - } - } - } -} + if (ep->addressv[code_ix] == ep->trampoline.raw) { + continue; + } -static void -reset_bif_trace(void) -{ - int i; - ErtsBpIndex active = erts_active_bp_ix(); - - for (i = 0; i < BIF_SIZE; ++i) { - Export *ep = bif_export[i]; - GenericBp* g = ep->info.u.gen_bp; - if (g && g->data[active].flags == 0) { - if (ExportIsBuiltIn(ep)) { - ASSERT(ep->beam[1]); - ep->beam[1] = (BeamInstr) bif_table[i].f; - } - } + if (BeamIsOpCode(ep->trampoline.op, op_trace_jump_W)) { + ep->trampoline.op = (BeamInstr) 0; + ep->trampoline.trace.address = (BeamInstr) 0; + } } } diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 285c23ea01..a78e6b53ae 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -75,7 +75,7 @@ static BIF_RETTYPE db_bif_fail(Process* p, Uint freason, { if (freason == TRAP) { if (!bif_exp) - bif_exp = bif_export[bif_ix]; + bif_exp = &bif_trap_export[bif_ix]; p->arity = bif_exp->info.mfa.arity; p->i = (BeamInstr*) bif_exp->addressv[erts_active_code_ix()]; } @@ -2170,7 +2170,7 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2) tb->common.status |= DB_BUSY; db_unlock(tb, LCK_WRITE); BUMP_ALL_REDS(BIF_P); - BIF_TRAP2(bif_export[BIF_ets_internal_delete_all_2], BIF_P, + BIF_TRAP2(&bif_trap_export[BIF_ets_internal_delete_all_2], BIF_P, BIF_ARG_1, nitems_holder); } else { @@ -3334,7 +3334,7 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1) BIF_RET(am_undefined); } if (rp == ERTS_PROC_LOCK_BUSY) { - ERTS_BIF_YIELD1(bif_export[BIF_ets_info_1], BIF_P, BIF_ARG_1); + ERTS_BIF_YIELD1(&bif_trap_export[BIF_ets_info_1], BIF_P, BIF_ARG_1); } if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ)) == NULL || tb->common.owner != owner) { @@ -3355,10 +3355,10 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1) db_unlock(tb, LCK_READ); hp = HAlloc(BIF_P, 3); tuple = TUPLE2(hp, res.trap_resume_state, table); - BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, tuple); + BIF_TRAP1(&bif_trap_export[BIF_ets_info_1], BIF_P, tuple); } else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) { db_unlock(tb, LCK_READ); - BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, table); + BIF_TRAP1(&bif_trap_export[BIF_ets_info_1], BIF_P, table); } else { size = res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID]; memory = res.result[ERTS_DB_TABLE_MEM_COUNTER_ID]; @@ -3429,10 +3429,10 @@ BIF_RETTYPE ets_info_2(BIF_ALIST_2) erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P); if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) { db_unlock(tb, LCK_READ); - BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, res.trap_resume_state, BIF_ARG_2); + BIF_TRAP2(&bif_trap_export[BIF_ets_info_2], BIF_P, res.trap_resume_state, BIF_ARG_2); } else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) { db_unlock(tb, LCK_READ); - BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2); + BIF_TRAP2(&bif_trap_export[BIF_ets_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2); } else if (BIF_ARG_2 == am_size) { ret = erts_make_integer(res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID], BIF_P); } else { /* BIF_ARG_2 == am_memory */ @@ -3498,7 +3498,7 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3) for (lst = BIF_ARG_1; is_list(lst); lst = CDR(list_val(lst))) { if (++i > CONTEXT_REDS) { BUMP_ALL_REDS(BIF_P); - BIF_TRAP3(bif_export[BIF_ets_match_spec_run_r_3], + BIF_TRAP3(&bif_trap_export[BIF_ets_match_spec_run_r_3], BIF_P,lst,BIF_ARG_2,ret); } res = db_prog_match(BIF_P, BIF_P, diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index ac1e9beaa5..2bcdb47a54 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -3328,6 +3328,12 @@ void db_foreach_offheap_hash(DbTable *tbl, int i; int nactive = NACTIVE(tb); + if (nactive > tb->nslots) { + /* Table is being emptied by delete/1 or delete_all_objects/1 */ + ASSERT(!(tb->common.status & (DB_PRIVATE|DB_PROTECTED|DB_PUBLIC))); + nactive = tb->nslots; + } + for (i = 0; i < nactive; i++) { list = BUCKET(tb,i); while(list != 0) { diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 723b3c5d29..954fe1dcaf 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -1175,7 +1175,7 @@ int db_select_continue_tree_common(Process *p, sc.accum, tptr[7], make_small(sc.got)); - RET_TO_BIF(bif_trap1(bif_export[BIF_ets_select_1], p, continuation), + RET_TO_BIF(bif_trap1(&bif_trap_export[BIF_ets_select_1], p, continuation), DB_ERROR_NONE); #undef RET_TO_BIF @@ -1320,7 +1320,7 @@ int db_select_tree_common(Process *p, DbTable *tb, make_small(sc.got)); /* Don't free mpi.mp, so don't use macro */ - *ret = bif_trap1(bif_export[BIF_ets_select_1], p, continuation); + *ret = bif_trap1(&bif_trap_export[BIF_ets_select_1], p, continuation); return DB_ERROR_NONE; #undef RET_TO_BIF @@ -1728,7 +1728,7 @@ int db_select_chunk_tree_common(Process *p, DbTable *tb, make_small(reverse), make_small(sc.got)); /* Don't let RET_TO_BIF macro free mpi.mp*/ - *ret = bif_trap1(bif_export[BIF_ets_select_1], p, continuation); + *ret = bif_trap1(&bif_trap_export[BIF_ets_select_1], p, continuation); return DB_ERROR_NONE; #undef RET_TO_BIF diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index f387960b08..4a6f204cb5 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2588,7 +2588,7 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) /* * If a NIF or BIF has saved arguments, they need to be added */ - if (erts_setup_nif_export_rootset(p, &roots[n].v, &roots[n].sz)) + if (erts_setup_nfunc_rootset(p, &roots[n].v, &roots[n].sz)) n++; ASSERT(n <= rootset->size); @@ -3236,7 +3236,7 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size, offset_heap_ptr(objv, nobj, offs, area, area_size); } offset_off_heap(p, offs, area, area_size); - if (erts_setup_nif_export_rootset(p, &v, &sz)) + if (erts_setup_nfunc_rootset(p, &v, &sz)) offset_heap_ptr(v, sz, offs, area, area_size); } diff --git a/erts/emulator/beam/erl_io_queue.c b/erts/emulator/beam/erl_io_queue.c index 2ae5b56b5c..c82d67f893 100644 --- a/erts/emulator/beam/erl_io_queue.c +++ b/erts/emulator/beam/erl_io_queue.c @@ -1078,7 +1078,7 @@ static BIF_RETTYPE iol2v_yield(iol2v_state_t *state) { state = boxed_state; } - ERTS_BIF_YIELD1(bif_export[BIF_iolist_to_iovec_1], + ERTS_BIF_YIELD1(&bif_trap_export[BIF_iolist_to_iovec_1], state->process, state->magic_reference); } diff --git a/erts/emulator/beam/erl_nfunc_sched.c b/erts/emulator/beam/erl_nfunc_sched.c index b2658ef180..8263a6e9b7 100644 --- a/erts/emulator/beam/erl_nfunc_sched.c +++ b/erts/emulator/beam/erl_nfunc_sched.c @@ -30,76 +30,37 @@ #include "erl_nfunc_sched.h" #include "erl_trace.h" -NifExport * -erts_new_proc_nif_export(Process *c_p, int argc) +ErtsNativeFunc * +erts_new_proc_nfunc(Process *c_p, int argc) { + ErtsNativeFunc *nep, *old_nep; size_t size; - int i; - NifExport *nep, *old_nep; - - size = sizeof(NifExport) + (argc-1)*sizeof(Eterm); - nep = erts_alloc(ERTS_ALC_T_NIF_TRAP_EXPORT, size); - for (i = 0; i < ERTS_NUM_CODE_IX; i++) - nep->exp.addressv[i] = &nep->exp.beam[0]; + size = sizeof(ErtsNativeFunc) + (argc-1)*sizeof(Eterm); + nep = erts_alloc(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, size); nep->argc = -1; /* unused marker */ nep->argv_size = argc; - nep->trace = NULL; - old_nep = ERTS_PROC_SET_NIF_TRAP_EXPORT(c_p, nep); + old_nep = ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(c_p, nep); if (old_nep) { - ASSERT(!nep->trace); - erts_free(ERTS_ALC_T_NIF_TRAP_EXPORT, old_nep); + erts_free(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, old_nep); } return nep; } void -erts_destroy_nif_export(Process *p) +erts_destroy_nfunc(Process *p) { - NifExport *nep = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, NULL); + ErtsNativeFunc *nep = ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(p, NULL); if (nep) { if (nep->m) - erts_nif_export_cleanup_nif_mod(nep); - erts_free(ERTS_ALC_T_NIF_TRAP_EXPORT, nep); + erts_nfunc_cleanup_nif_mod(nep); + erts_free(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, nep); } } -void -erts_nif_export_save_trace(Process *c_p, NifExport *nep, int applying, - Export* ep, Uint32 flags, - Uint32 flags_meta, BeamInstr* I, - ErtsTracer meta_tracer) -{ - NifExportTrace *netp; - ASSERT(nep && nep->argc >= 0); - ASSERT(!nep->trace); - netp = erts_alloc(ERTS_ALC_T_NIF_EXP_TRACE, - sizeof(NifExportTrace)); - netp->applying = applying; - netp->ep = ep; - netp->flags = flags; - netp->flags_meta = flags_meta; - netp->I = I; - netp->meta_tracer = NIL; - erts_tracer_update(&netp->meta_tracer, meta_tracer); - nep->trace = netp; -} - -void -erts_nif_export_restore_trace(Process *c_p, Eterm result, NifExport *nep) -{ - NifExportTrace *netp = nep->trace; - nep->trace = NULL; - erts_bif_trace_epilogue(c_p, result, netp->applying, netp->ep, - netp->flags, netp->flags_meta, - netp->I, netp->meta_tracer); - erts_tracer_update(&netp->meta_tracer, NIL); - erts_free(ERTS_ALC_T_NIF_EXP_TRACE, netp); -} - -NifExport * -erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc, +ErtsNativeFunc * +erts_nfunc_schedule(Process *c_p, Process *dirty_shadow_proc, ErtsCodeMFA *mfa, BeamInstr *pc, BeamInstr instr, void *dfunc, void *ifunc, @@ -109,7 +70,7 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc, Process *used_proc; ErtsSchedulerData *esdp; Eterm* reg; - NifExport* nep; + ErtsNativeFunc* nep; int i; ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) @@ -132,10 +93,10 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc, reg = esdp->x_reg_array; if (mfa) - nep = erts_get_proc_nif_export(c_p, (int) mfa->arity); + nep = erts_get_proc_nfunc(c_p, (int) mfa->arity); else { /* If no mfa, this is not the first schedule... */ - nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p); + nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p); ASSERT(nep && nep->argc >= 0); } @@ -153,9 +114,9 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc, nep->argc = (int) mfa->arity; nep->m = NULL; - ASSERT(!erts_check_nif_export_in_area(c_p, + ASSERT(!erts_check_nfunc_in_area(c_p, (char *) nep, - (sizeof(NifExport) + (sizeof(ErtsNativeFunc) + (sizeof(Eterm) *(nep->argc-1))))); } @@ -165,14 +126,14 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc, reg[i] = argv[i]; } ASSERT(is_atom(mod) && is_atom(func)); - nep->exp.info.mfa.module = mod; - nep->exp.info.mfa.function = func; - nep->exp.info.mfa.arity = (Uint) argc; - nep->exp.beam[0] = (BeamInstr) instr; /* call_nif || apply_bif */ - nep->exp.beam[1] = (BeamInstr) dfunc; + nep->trampoline.info.mfa.module = mod; + nep->trampoline.info.mfa.function = func; + nep->trampoline.info.mfa.arity = (Uint) argc; + nep->trampoline.call_op = (BeamInstr) instr; /* call_bif || call_nif */ + nep->trampoline.dfunc = (BeamInstr) dfunc; nep->func = ifunc; used_proc->arity = argc; used_proc->freason = TRAP; - used_proc->i = (BeamInstr*) nep->exp.addressv[0]; + used_proc->i = (BeamInstr*)&nep->trampoline.call_op; return nep; } diff --git a/erts/emulator/beam/erl_nfunc_sched.h b/erts/emulator/beam/erl_nfunc_sched.h index 5c6486cbb8..4dae242d4f 100644 --- a/erts/emulator/beam/erl_nfunc_sched.h +++ b/erts/emulator/beam/erl_nfunc_sched.h @@ -25,90 +25,78 @@ #include "bif.h" #include "error.h" -typedef struct { - int applying; - Export* ep; - Uint32 flags; - Uint32 flags_meta; - BeamInstr* I; - ErtsTracer meta_tracer; -} NifExportTrace; - /* - * NIF exports need a few more items than the Export struct provides, - * including the erl_module_nif* and a NIF function pointer, so the - * NifExport below adds those. The Export member must be first in the - * struct. A number of values are stored for error handling purposes - * only. + * Native function wrappers are used to schedule native functions on both + * normal and dirty schedulers. + * + * A number of values are only stored for error handling, and the fields + * following `current` can be omitted when a wrapper is statically "scheduled" + * through placement in a function stub. * - * 'argc' is >= 0 when NifExport is in use, and < 0 when not. + * 'argc' is >= 0 when ErtsNativeFunc is in use, and < 0 when not. */ typedef struct { - Export exp; + struct { + ErtsCodeInfo info; + BeamInstr call_op; /* call_bif || call_nif */ + BeamInstr dfunc; + } trampoline; + struct erl_module_nif* m; /* NIF module, or NULL if BIF */ void *func; /* Indirect NIF or BIF to execute (may be unused) */ ErtsCodeMFA *current;/* Current as set when originally called */ - NifExportTrace *trace; /* --- The following is only used on error --- */ BeamInstr *pc; /* Program counter */ ErtsCodeMFA *mfa; /* MFA of original call */ int argc; /* Number of arguments in original call */ int argv_size; /* Allocated size of argv */ Eterm argv[1]; /* Saved arguments from the original call */ -} NifExport; - -NifExport *erts_new_proc_nif_export(Process *c_p, int argc); -void erts_nif_export_save_trace(Process *c_p, NifExport *nep, int applying, - Export* ep, Uint32 flags, - Uint32 flags_meta, BeamInstr* I, - ErtsTracer meta_tracer); -void erts_nif_export_restore_trace(Process *c_p, Eterm result, NifExport *nep); -void erts_destroy_nif_export(Process *p); -NifExport *erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc, - ErtsCodeMFA *mfa, BeamInstr *pc, - BeamInstr instr, - void *dfunc, void *ifunc, - Eterm mod, Eterm func, - int argc, const Eterm *argv); -void erts_nif_export_cleanup_nif_mod(NifExport *ep); /* erl_nif.c */ -ERTS_GLB_INLINE NifExport *erts_get_proc_nif_export(Process *c_p, int extra); -ERTS_GLB_INLINE int erts_setup_nif_export_rootset(Process* proc, Eterm** objv, - Uint* nobj); -ERTS_GLB_INLINE int erts_check_nif_export_in_area(Process *p, - char *start, Uint size); -ERTS_GLB_INLINE void erts_nif_export_restore(Process *c_p, NifExport *ep, - Eterm result); -ERTS_GLB_INLINE void erts_nif_export_restore_error(Process* c_p, BeamInstr **pc, - Eterm *reg, ErtsCodeMFA **nif_mfa); -ERTS_GLB_INLINE int erts_nif_export_check_save_trace(Process *c_p, Eterm result, - int applying, Export* ep, - Uint32 flags, - Uint32 flags_meta, BeamInstr* I, - ErtsTracer meta_tracer); +} ErtsNativeFunc; + +ErtsNativeFunc *erts_new_proc_nfunc(Process *c_p, int argc); +void erts_destroy_nfunc(Process *p); +ErtsNativeFunc *erts_nfunc_schedule(Process *c_p, Process *dirty_shadow_proc, + ErtsCodeMFA *mfa, BeamInstr *pc, + BeamInstr instr, + void *dfunc, void *ifunc, + Eterm mod, Eterm func, + int argc, const Eterm *argv); +void erts_nfunc_cleanup_nif_mod(ErtsNativeFunc *ep); /* erl_nif.c */ +ERTS_GLB_INLINE ErtsNativeFunc *erts_get_proc_nfunc(Process *c_p, int extra); +ERTS_GLB_INLINE int erts_setup_nfunc_rootset(Process* proc, Eterm** objv, + Uint* nobj); +ERTS_GLB_INLINE int erts_check_nfunc_in_area(Process *p, + char *start, Uint size); +ERTS_GLB_INLINE void erts_nfunc_restore(Process *c_p, ErtsNativeFunc *ep, + Eterm result); +ERTS_GLB_INLINE void erts_nfunc_restore_error(Process* c_p, + BeamInstr **pc, + Eterm *reg, + ErtsCodeMFA **nif_mfa); ERTS_GLB_INLINE Process *erts_proc_shadow2real(Process *c_p); #if ERTS_GLB_INLINE_INCL_FUNC_DEF -ERTS_GLB_INLINE NifExport * -erts_get_proc_nif_export(Process *c_p, int argc) +ERTS_GLB_INLINE ErtsNativeFunc * +erts_get_proc_nfunc(Process *c_p, int argc) { - NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p); + ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p); if (!nep || (nep->argc < 0 && nep->argv_size < argc)) - return erts_new_proc_nif_export(c_p, argc); + return erts_new_proc_nfunc(c_p, argc); return nep; } /* * If a process has saved arguments, they need to be part of the GC * rootset. The function below is called from setup_rootset() in - * erl_gc.c. Any exception term saved in the NifExport is also made + * erl_gc.c. Any exception term saved in the ErtsNativeFunc is also made * part of the GC rootset here; it always resides in rootset[0]. */ ERTS_GLB_INLINE int -erts_setup_nif_export_rootset(Process* proc, Eterm** objv, Uint* nobj) +erts_setup_nfunc_rootset(Process* proc, Eterm** objv, Uint* nobj) { - NifExport* ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + ErtsNativeFunc* ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc); if (!ep || ep->argc <= 0) return 0; @@ -119,12 +107,12 @@ erts_setup_nif_export_rootset(Process* proc, Eterm** objv, Uint* nobj) } /* - * Check if nif export points into code area... + * Check if native func wrapper points into code area... */ ERTS_GLB_INLINE int -erts_check_nif_export_in_area(Process *p, char *start, Uint size) +erts_check_nfunc_in_area(Process *p, char *start, Uint size) { - NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(p); + ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(p); if (!nep || nep->argc < 0) return 0; if (ErtsInArea(nep->pc, start, size)) @@ -137,7 +125,7 @@ erts_check_nif_export_in_area(Process *p, char *start, Uint size) } ERTS_GLB_INLINE void -erts_nif_export_restore(Process *c_p, NifExport *ep, Eterm result) +erts_nfunc_restore(Process *c_p, ErtsNativeFunc *ep, Eterm result) { ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_get_scheduler_data())); ERTS_LC_ASSERT(!(c_p->static_flags @@ -147,15 +135,13 @@ erts_nif_export_restore(Process *c_p, NifExport *ep, Eterm result) c_p->current = ep->current; ep->argc = -1; /* Unused nif-export marker... */ - if (ep->trace) - erts_nif_export_restore_trace(c_p, result, ep); } ERTS_GLB_INLINE void -erts_nif_export_restore_error(Process* c_p, BeamInstr **pc, +erts_nfunc_restore_error(Process* c_p, BeamInstr **pc, Eterm *reg, ErtsCodeMFA **nif_mfa) { - NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p); + ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p); int ix; ASSERT(nep); @@ -163,26 +149,7 @@ erts_nif_export_restore_error(Process* c_p, BeamInstr **pc, *nif_mfa = nep->mfa; for (ix = 0; ix < nep->argc; ix++) reg[ix] = nep->argv[ix]; - erts_nif_export_restore(c_p, nep, THE_NON_VALUE); -} - -ERTS_GLB_INLINE int -erts_nif_export_check_save_trace(Process *c_p, Eterm result, - int applying, Export* ep, - Uint32 flags, - Uint32 flags_meta, BeamInstr* I, - ErtsTracer meta_tracer) -{ - if (is_non_value(result) && c_p->freason == TRAP) { - NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p); - if (nep && nep->argc >= 0) { - erts_nif_export_save_trace(c_p, nep, applying, ep, - flags, flags_meta, - I, meta_tracer); - return 1; - } - } - return 0; + erts_nfunc_restore(c_p, nep, THE_NON_VALUE); } ERTS_GLB_INLINE Process * @@ -205,10 +172,10 @@ erts_proc_shadow2real(Process *c_p) #if defined(ERTS_WANT_NFUNC_SCHED_INTERNALS__) && !defined(ERTS_NFUNC_SCHED_INTERNALS__) #define ERTS_NFUNC_SCHED_INTERNALS__ -#define ERTS_I_BEAM_OP_TO_NIF_EXPORT(I) \ - (ASSERT(BeamIsOpCode(*(I), op_apply_bif) || \ - BeamIsOpCode(*(I), op_call_nif)), \ - ((NifExport *) (((char *) (I)) - offsetof(NifExport, exp.beam[0])))) +#define ERTS_I_BEAM_OP_TO_NFUNC(I) \ + (ASSERT(BeamIsOpCode(*(I), op_call_bif_W) || \ + BeamIsOpCode(*(I), op_call_nif_WWW)), \ + ((ErtsNativeFunc *) (((char *) (I)) - offsetof(ErtsNativeFunc, trampoline.call_op)))) #include "erl_message.h" diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 46f7e864fd..6e27b4b7cb 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -309,10 +309,10 @@ void erts_post_nif(ErlNifEnv* env) /* - * Initialize a NifExport struct. Create it if needed and store it in the + * Initialize a ErtsNativeFunc struct. Create it if needed and store it in the * proc. The direct_fp function is what will be invoked by op_call_nif, and * the indirect_fp function, if not NULL, is what the direct_fp function - * will call. If the allocated NifExport isn't enough to hold all of argv, + * will call. If the allocated ErtsNativeFunc isn't enough to hold all of argv, * allocate a larger one. Save 'current' and registers if first time this * call is scheduled. */ @@ -321,7 +321,7 @@ static ERTS_INLINE ERL_NIF_TERM schedule(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp, Eterm mod, Eterm func_name, int argc, const ERL_NIF_TERM argv[]) { - NifExport *ep; + ErtsNativeFunc *ep; Process *c_p, *dirty_shadow_proc; execution_state(env, &c_p, NULL); @@ -332,10 +332,10 @@ schedule(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp, ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p)); - ep = erts_nif_export_schedule(c_p, dirty_shadow_proc, + ep = erts_nfunc_schedule(c_p, dirty_shadow_proc, c_p->current, cp_val(c_p->stop[0]), - BeamOpCodeAddr(op_call_nif), + BeamOpCodeAddr(op_call_nif_WWW), direct_fp, indirect_fp, mod, func_name, argc, (const Eterm *) argv); @@ -356,7 +356,7 @@ erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm * { int exiting; ERL_NIF_TERM *argv = (ERL_NIF_TERM *) reg; - NifExport *nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I); + ErtsNativeFunc *nep = ERTS_I_BEAM_OP_TO_NFUNC(I); ErtsCodeMFA *codemfa = erts_code_to_codemfa(I); NativeFunPtr dirty_nif = (NativeFunPtr) I[1]; ErlNifEnv env; @@ -364,7 +364,7 @@ erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm * #ifdef DEBUG erts_aint32_t state = erts_atomic32_read_nob(&c_p->state); - ASSERT(nep == ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p)); + ASSERT(nep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p)); ASSERT(!c_p->scheduler_data); ASSERT((state & ERTS_PSFLG_DIRTY_RUNNING) @@ -2823,7 +2823,7 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent) } static ERTS_INLINE void -nif_export_cleanup_nif_mod(NifExport *ep) +nfunc_cleanup_nif_mod(ErtsNativeFunc *ep) { if (erts_refc_dectest(&ep->m->rt_dtor_cnt, 0) == 0 && ep->m->mod == NULL) close_lib(ep->m); @@ -2831,17 +2831,17 @@ nif_export_cleanup_nif_mod(NifExport *ep) } void -erts_nif_export_cleanup_nif_mod(NifExport *ep) +erts_nfunc_cleanup_nif_mod(ErtsNativeFunc *ep) { - nif_export_cleanup_nif_mod(ep); + nfunc_cleanup_nif_mod(ep); } static ERTS_INLINE void -nif_export_restore(Process *c_p, NifExport *ep, Eterm res) +nfunc_restore(Process *c_p, ErtsNativeFunc *ep, Eterm res) { - erts_nif_export_restore(c_p, ep, res); + erts_nfunc_restore(c_p, ep, res); ASSERT(ep->m); - nif_export_cleanup_nif_mod(ep); + nfunc_cleanup_nif_mod(ep); } @@ -2858,15 +2858,15 @@ static ERL_NIF_TERM dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { Process* proc; - NifExport* ep; + ErtsNativeFunc* ep; execution_state(env, &proc, NULL); ASSERT(argc == 1); ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc))); - ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc); ASSERT(ep); - nif_export_restore(proc, ep, argv[0]); + nfunc_restore(proc, ep, argv[0]); return argv[0]; } @@ -2878,21 +2878,22 @@ dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM ret; Process* proc; - NifExport* ep; + ErtsNativeFunc* ep; Eterm exception; execution_state(env, &proc, NULL); ASSERT(argc == 1); ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc))); - ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); + ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc); ASSERT(ep); exception = argv[0]; /* argv overwritten by restore below... */ - nif_export_cleanup_nif_mod(ep); + nfunc_cleanup_nif_mod(ep); ret = enif_raise_exception(env, exception); - /* Restore orig info for error and clear nif export in handle_error() */ - proc->freason |= EXF_RESTORE_NIF; + /* Restore orig info for error and clear native func wrapper in + * handle_error() */ + proc->freason |= EXF_RESTORE_NFUNC; return ret; } @@ -2929,7 +2930,7 @@ static_schedule_dirty_nif(ErlNifEnv* env, erts_aint32_t dirty_psflg, int argc, const ERL_NIF_TERM argv[]) { Process *proc; - NifExport *ep; + ErtsNativeFunc *ep; Eterm mod, func; NativeFunPtr fp; @@ -2939,12 +2940,11 @@ static_schedule_dirty_nif(ErlNifEnv* env, erts_aint32_t dirty_psflg, * Called in order to schedule statically determined * dirty NIF calls... * - * Note that 'current' does not point into a NifExport - * structure; only a structure with similar - * parts (located in code). + * Note that 'current' does not point into a ErtsNativeFunc + * structure; only a structure with similar parts (located in code). */ - ep = ErtsContainerStruct(proc->current, NifExport, exp.info.mfa); + ep = ErtsContainerStruct(proc->current, ErtsNativeFunc, trampoline.info.mfa); mod = proc->current->module; func = proc->current->function; fp = (NativeFunPtr) ep->func; @@ -2983,12 +2983,12 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { Process* proc; NativeFunPtr fp; - NifExport* ep; + ErtsNativeFunc* ep; ERL_NIF_TERM result; execution_state(env, &proc, NULL); - ep = ErtsContainerStruct(proc->current, NifExport, exp.info.mfa); + ep = ErtsContainerStruct(proc->current, ErtsNativeFunc, trampoline.info.mfa); fp = ep->func; ASSERT(ep); ASSERT(!env->exception_thrown); @@ -3001,20 +3001,20 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) result = (*fp)(env, argc, argv); - ASSERT(ep == ERTS_PROC_GET_NIF_TRAP_EXPORT(proc)); + ASSERT(ep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc)); if (is_value(result) || proc->freason != TRAP) { /* Done (not rescheduled)... */ ASSERT(ep->func == ERTS_DBG_NIF_NOT_SCHED_MARKER); if (!env->exception_thrown) - nif_export_restore(proc, ep, result); + nfunc_restore(proc, ep, result); else { - nif_export_cleanup_nif_mod(ep); + nfunc_cleanup_nif_mod(ep); /* * Restore orig info for error and clear nif * export in handle_error() */ - proc->freason |= EXF_RESTORE_NIF; + proc->freason |= EXF_RESTORE_NFUNC; } } @@ -4117,7 +4117,23 @@ static struct erl_module_nif* create_lib(const ErlNifEntry* src) return lib; }; -BIF_RETTYPE load_nif_2(BIF_ALIST_2) +/* load_nif/2 is implemented as an instruction as it needs to know where it + * was called from, and it's a pain to get that information in a BIF. + * + * This is a small stub that rejects apply(erlang, load_nif, [Path, Args]). */ +BIF_RETTYPE load_nif_2(BIF_ALIST_2) { + if (BIF_P->flags & F_HIPE_MODE) { + BIF_RET(load_nif_error(BIF_P, "notsup", + "Calling load_nif from HiPE compiled modules " + "not supported")); + } + + BIF_RET(load_nif_error(BIF_P, "bad_lib", + "load_nif/2 must be explicitly called from the NIF " + "module. It cannot be called through apply/3.")); +} + +Eterm erts_load_nif(Process *c_p, BeamInstr *I, Eterm filename, Eterm args) { static const char bad_lib[] = "bad_lib"; static const char upgrade[] = "upgrade"; @@ -4139,13 +4155,6 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) struct erl_module_nif* lib = NULL; struct erl_module_instance* this_mi; struct erl_module_instance* prev_mi; - BeamInstr* caller_cp; - - if (BIF_P->flags & F_HIPE_MODE) { - ret = load_nif_error(BIF_P, "notsup", "Calling load_nif from HiPE compiled " - "modules not supported"); - BIF_RET(ret); - } encoding = erts_get_native_filename_encoding(); if (encoding == ERL_FILENAME_WIN_WCHAR) { @@ -4153,30 +4162,19 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) /* since lib_name is used in error messages */ encoding = ERL_FILENAME_UTF8; } - lib_name = erts_convert_filename_to_encoding(BIF_ARG_1, NULL, 0, + lib_name = erts_convert_filename_to_encoding(filename, NULL, 0, ERTS_ALC_T_TMP, 1, 0, encoding, NULL, 0); if (!lib_name) { - BIF_ERROR(BIF_P, BADARG); - } - - if (!erts_try_seize_code_write_permission(BIF_P)) { - erts_free(ERTS_ALC_T_TMP, lib_name); - ERTS_BIF_YIELD2(bif_export[BIF_load_nif_2], - BIF_P, BIF_ARG_1, BIF_ARG_2); + return THE_NON_VALUE; } /* Block system (is this the right place to do it?) */ - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); /* Find calling module */ - ASSERT(BIF_P->current != NULL); - ASSERT(BIF_P->current->module == am_erlang - && BIF_P->current->function == am_load_nif - && BIF_P->current->arity == 2); - caller_cp = cp_val(BIF_P->stop[0]); - caller = find_function_from_pc(caller_cp); + caller = find_function_from_pc(I); ASSERT(caller != NULL); mod_atom = caller->module; ASSERT(is_atom(mod_atom)); @@ -4196,7 +4194,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) this_mi = &module_p->curr; prev_mi = &module_p->old; if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) { - ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old " + ret = load_nif_error(c_p, "old_code", "Calling load_nif from old " "module '%T' not allowed", mod_atom); goto error; } else if (module_p->on_load) { @@ -4210,52 +4208,52 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } if (this_mi->nif != NULL) { - ret = load_nif_error(BIF_P,"reload","NIF library already loaded" + ret = load_nif_error(c_p,"reload","NIF library already loaded" " (reload disallowed since OTP 20)."); } else if (init_func == NULL && (err=erts_sys_ddll_open(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) { const char slogan[] = "Failed to load NIF library"; if (strstr(errdesc.str, lib_name) != NULL) { - ret = load_nif_error(BIF_P, "load_failed", "%s: '%s'", slogan, errdesc.str); + ret = load_nif_error(c_p, "load_failed", "%s: '%s'", slogan, errdesc.str); } else { - ret = load_nif_error(BIF_P, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str); + ret = load_nif_error(c_p, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str); } } else if (init_func == NULL && erts_sys_ddll_load_nif_init(handle, &init_func, &errdesc) != ERL_DE_NO_ERROR) { - ret = load_nif_error(BIF_P, bad_lib, "Failed to find library init" + ret = load_nif_error(c_p, bad_lib, "Failed to find library init" " function: '%s'", errdesc.str); } else if ((taint ? erts_add_taint(mod_atom) : 0, (entry = erts_sys_ddll_call_nif_init(init_func)) == NULL)) { - ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful"); + ret = load_nif_error(c_p, bad_lib, "Library init-call unsuccessful"); } else if (entry->major > ERL_NIF_MAJOR_VERSION || (entry->major == ERL_NIF_MAJOR_VERSION && entry->minor > ERL_NIF_MINOR_VERSION)) { char* fmt = "That '%T' NIF library needs %s or newer. Either try to" " recompile the NIF lib or use a newer erts runtime."; - ret = load_nif_error(BIF_P, bad_lib, fmt, mod_atom, entry->min_erts); + ret = load_nif_error(c_p, bad_lib, fmt, mod_atom, entry->min_erts); } else if (entry->major < ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD || (entry->major==2 && entry->minor == 5)) { /* experimental maps */ char* fmt = "That old NIF library (%d.%d) is not compatible with this " "erts runtime (%d.%d). Try recompile the NIF lib."; - ret = load_nif_error(BIF_P, bad_lib, fmt, entry->major, entry->minor, + ret = load_nif_error(c_p, bad_lib, fmt, entry->major, entry->minor, ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION); } else if (AT_LEAST_VERSION(entry, 2, 1) && sys_strcmp(entry->vm_variant, ERL_NIF_VM_VARIANT) != 0) { - ret = load_nif_error(BIF_P, bad_lib, "Library (%s) not compiled for " + ret = load_nif_error(c_p, bad_lib, "Library (%s) not compiled for " "this vm variant (%s).", entry->vm_variant, ERL_NIF_VM_VARIANT); } else if (!erts_is_atom_str((char*)entry->name, mod_atom, 1)) { - ret = load_nif_error(BIF_P, bad_lib, "Library module name '%s' does not" + ret = load_nif_error(c_p, bad_lib, "Library module name '%s' does not" " match calling module '%T'", entry->name, mod_atom); } else { @@ -4274,7 +4272,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1) || (ci_pp = get_func_pp(this_mi->code_hdr, f_atom, f->arity))==NULL) { - ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u", + ret = load_nif_error(c_p,bad_lib,"Function not found %T:%s/%u", mod_atom, f->name, f->arity); } else if (f->flags) { @@ -4286,16 +4284,13 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) * a load error. */ if (f->flags != ERL_NIF_DIRTY_JOB_IO_BOUND && f->flags != ERL_NIF_DIRTY_JOB_CPU_BOUND) - ret = load_nif_error(BIF_P, bad_lib, "Illegal flags field value %d for NIF %T:%s/%u", + ret = load_nif_error(c_p, bad_lib, "Illegal flags field value %d for NIF %T:%s/%u", f->flags, mod_atom, f->name, f->arity); } - else if (erts_codeinfo_to_code(ci_pp[1]) - erts_codeinfo_to_code(ci_pp[0]) - < BEAM_NIF_MIN_FUNC_SZ) - { - ret = load_nif_error(BIF_P,bad_lib,"No explicit call to load_nif" - " in module (%T:%s/%u too small)", - mod_atom, f->name, f->arity); - } + + ASSERT(erts_codeinfo_to_code(ci_pp[1]) - erts_codeinfo_to_code(ci_pp[0]) + >= BEAM_NATIVE_MIN_FUNC_SZ); + /*erts_fprintf(stderr, "Found NIF %T:%s/%u\r\n", mod_atom, f->name, f->arity);*/ } @@ -4314,23 +4309,23 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) if (prev_mi->nif != NULL) { /**************** Upgrade ***************/ void* prev_old_data = prev_mi->nif->priv_data; if (entry->upgrade == NULL) { - ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library."); + ret = load_nif_error(c_p, upgrade, "Upgrade not supported by this NIF library."); goto error; } - erts_pre_nif(&env, BIF_P, lib, NULL); - veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, BIF_ARG_2); + erts_pre_nif(&env, c_p, lib, NULL); + veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, args); erts_post_nif(&env); if (veto) { prev_mi->nif->priv_data = prev_old_data; - ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful (%d).", veto); + ret = load_nif_error(c_p, upgrade, "Library upgrade-call unsuccessful (%d).", veto); } } else if (entry->load != NULL) { /********* Initial load ***********/ - erts_pre_nif(&env, BIF_P, lib, NULL); - veto = entry->load(&env, &lib->priv_data, BIF_ARG_2); + erts_pre_nif(&env, c_p, lib, NULL); + veto = entry->load(&env, &lib->priv_data, args); erts_post_nif(&env); if (veto) { - ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful (%d).", veto); + ret = load_nif_error(c_p, "load", "Library load-call unsuccessful (%d).", veto); } } if (ret == am_ok) { @@ -4352,12 +4347,12 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) code_ptr = erts_codeinfo_to_code(ci); if (ci->u.gen_bp == NULL) { - code_ptr[0] = BeamOpCodeAddr(op_call_nif); + code_ptr[0] = BeamOpCodeAddr(op_call_nif_WWW); } else { /* Function traced, patch the original instruction word */ GenericBp* g = ci->u.gen_bp; ASSERT(BeamIsOpCode(code_ptr[0], op_i_generic_breakpoint)); - g->orig_instr = BeamOpCodeAddr(op_call_nif); + g->orig_instr = BeamOpCodeAddr(op_call_nif_WWW); } if (f->flags) { code_ptr[3] = (BeamInstr) f->fptr; @@ -4384,8 +4379,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } erts_thr_progress_unblock(); - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_release_code_write_permission(); + erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); erts_free(ERTS_ALC_T_TMP, lib_name); BIF_RET(ret); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a247258eff..479a4c6f9a 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -708,10 +708,10 @@ erts_pre_init_process(void) erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks = ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS; - erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].get_locks - = ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS; - erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].set_locks - = ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS; + erts_psd_required_locks[ERTS_PSD_NFUNC_TRAP_WRAPPER].get_locks + = ERTS_PSD_NFUNC_TRAP_WRAPPER_GET_LOCKS; + erts_psd_required_locks[ERTS_PSD_NFUNC_TRAP_WRAPPER].set_locks + = ERTS_PSD_NFUNC_TRAP_WRAPPER_SET_LOCKS; erts_psd_required_locks[ERTS_PSD_ETS_OWNED_TABLES].get_locks = ERTS_PSD_ETS_OWNED_TABLES_GET_LOCKS; @@ -6478,8 +6478,8 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, ASSERT(!(state & (ERTS_PSFLG_DIRTY_IO_PROC |ERTS_PSFLG_DIRTY_CPU_PROC)) - || (BeamIsOpCode(*p->i, op_call_nif) - || BeamIsOpCode(*p->i, op_apply_bif))); + || (BeamIsOpCode(*p->i, op_call_nif_WWW) + || BeamIsOpCode(*p->i, op_call_bif_W))); a = state; @@ -11976,7 +11976,7 @@ delete_process(Process* p) if (pbt) erts_free(ERTS_ALC_T_BPD, (void *) pbt); - erts_destroy_nif_export(p); + erts_destroy_nfunc(p); /* Cleanup psd */ @@ -12118,6 +12118,7 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds) ASSERT(c_p->flags & F_DISABLE_GC); ASSERT(erts_monitor_is_target(mon) && mon->type == ERTS_MON_TYPE_DIST_PROC); ASSERT(ctxt->dist_state == NIL); + ASSERT(!ctxt->yield); mdp = erts_monitor_to_data(mon); @@ -12164,10 +12165,12 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds) switch (code) { case ERTS_DSIG_SEND_YIELD: reds_consumed = reds; /* force yield */ + ctxt->yield = 1; break; case ERTS_DSIG_SEND_CONTINUE: ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx); reds_consumed = reds; /* force yield */ + ctxt->yield = 1; break; case ERTS_DSIG_SEND_OK: break; @@ -12379,6 +12382,7 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds) ASSERT(c_p->flags & F_DISABLE_GC); ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC); ASSERT(ctxt->dist_state == NIL); + ASSERT(!ctxt->yield); dlnk = erts_link_to_other(lnk, &ldp); dist = ((ErtsLinkDataExtended *) ldp)->dist; @@ -12418,10 +12422,12 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds) switch (code) { case ERTS_DSIG_SEND_YIELD: reds_consumed = reds; /* force yield */ + ctxt->yield = 1; break; case ERTS_DSIG_SEND_CONTINUE: ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx); reds_consumed = reds; /* force yield */ + ctxt->yield = 1; break; case ERTS_DSIG_SEND_OK: break; @@ -12844,6 +12850,7 @@ restart: trap_state->pectxt.dist_links = NULL; trap_state->pectxt.dist_monitors = NULL; trap_state->pectxt.dist_state = NIL; + trap_state->pectxt.yield = 0; erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ); @@ -12946,7 +12953,7 @@ restart: (void *) &trap_state->pectxt, &trap_state->yield_state, reds); - if (reds <= 0 || is_not_nil(trap_state->pectxt.dist_state)) + if (reds <= 0 || trap_state->pectxt.yield) goto yield; trap_state->phase = ERTS_CONTINUE_EXIT_DIST_MONITORS; } @@ -12961,7 +12968,7 @@ restart: (void *) &trap_state->pectxt, &trap_state->yield_state, reds); - if (reds <= 0 || is_not_nil(trap_state->pectxt.dist_state)) + if (reds <= 0 || trap_state->pectxt.yield) goto yield; trap_state->phase = ERTS_CONTINUE_EXIT_DONE; @@ -13046,6 +13053,7 @@ restart: sys_memcpy(trap_state, &static_state, sizeof(*trap_state)); p->u.terminate = trap_state; } + trap_state->pectxt.yield = 0; ASSERT(p->scheduler_data); ASSERT(p->scheduler_data->current_process == p); @@ -13461,7 +13469,7 @@ static void print_current_process_info(fmtfn_t to, void *to_arg, * * A BIF that calls this should make sure to schedule out to never come back: * erts_halt(code); - * ERTS_BIF_YIELD1(bif_export[BIF_erlang_halt_1], BIF_P, NIL); + * ERTS_BIF_YIELD1(&bif_trap_export[BIF_erlang_halt_1], BIF_P, NIL); */ void erts_halt(int code) { diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index a50d9efb89..09a6c0e961 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -812,7 +812,7 @@ erts_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi) #define ERTS_PSD_SCHED_ID 2 #define ERTS_PSD_CALL_TIME_BP 3 #define ERTS_PSD_DELAYED_GC_TASK_QS 4 -#define ERTS_PSD_NIF_TRAP_EXPORT 5 +#define ERTS_PSD_NFUNC_TRAP_WRAPPER 5 #define ERTS_PSD_ETS_OWNED_TABLES 6 #define ERTS_PSD_ETS_FIXED_TABLES 7 #define ERTS_PSD_DIST_ENTRY 8 @@ -849,8 +849,8 @@ typedef struct { #define ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS ERTS_PROC_LOCK_MAIN #define ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS ERTS_PROC_LOCK_MAIN -#define ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS ERTS_PROC_LOCK_MAIN -#define ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS ERTS_PROC_LOCK_MAIN +#define ERTS_PSD_NFUNC_TRAP_WRAPPER_GET_LOCKS ERTS_PROC_LOCK_MAIN +#define ERTS_PSD_NFUNC_TRAP_WRAPPER_SET_LOCKS ERTS_PROC_LOCK_MAIN #define ERTS_PSD_ETS_OWNED_TABLES_GET_LOCKS ERTS_PROC_LOCK_STATUS #define ERTS_PSD_ETS_OWNED_TABLES_SET_LOCKS ERTS_PROC_LOCK_STATUS @@ -1832,6 +1832,7 @@ typedef struct { ErtsLink *dist_links; ErtsMonitor *dist_monitors; Eterm dist_state; + int yield; } ErtsProcExitContext; int erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds); int erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt, Sint reds); @@ -2037,10 +2038,10 @@ erts_psd_set(Process *p, int ix, void *data) #define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, PBT) \ ((ErtsProcSysTaskQs *) erts_psd_set((P), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT))) -#define ERTS_PROC_GET_NIF_TRAP_EXPORT(P) \ - erts_psd_get((P), ERTS_PSD_NIF_TRAP_EXPORT) -#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, NTE) \ - erts_psd_set((P), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE)) +#define ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(P) \ + erts_psd_get((P), ERTS_PSD_NFUNC_TRAP_WRAPPER) +#define ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(P, NTE) \ + erts_psd_set((P), ERTS_PSD_NFUNC_TRAP_WRAPPER, (void *) (NTE)) #define ERTS_PROC_GET_DIST_ENTRY(P) \ ((DistEntry *) erts_psd_get((P), ERTS_PSD_DIST_ENTRY)) diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index f564549ab9..c0f31e0cb6 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -142,12 +142,6 @@ void monitor_generic(Process *p, Eterm type, Eterm spec); Uint erts_trace_flag2bit(Eterm flag); int erts_trace_flags(Eterm List, Uint *pMask, ErtsTracer *pTracer, int *pCpuTimestamp); -Eterm erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr *I); -Eterm -erts_bif_trace_epilogue(Process *p, Eterm result, int applying, - Export* ep, Uint32 flags, - Uint32 flags_meta, BeamInstr* I, - ErtsTracer meta_tracer); void erts_send_pending_trace_msgs(ErtsSchedulerData *esdp); #define ERTS_CHK_PEND_TRACE_MSGS(ESDP) \ diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d index 8792138d53..8864a8ec84 100644 --- a/erts/emulator/beam/erlang_dtrace.d +++ b/erts/emulator/beam/erlang_dtrace.d @@ -176,7 +176,7 @@ provider erlang { * Fired whenever a user function returns. * * @param p the PID (string form) of the process - * @param mfa the m:f/a of the function + * @param mfa the m:f/a of the function being returned from * @param depth the stack depth */ probe function__return(char *p, char *mfa, int depth); @@ -193,7 +193,7 @@ provider erlang { * Fired whenever a Built In Function returns. * * @param p the PID (string form) of the process - * @param mfa the m:f/a of the function + * @param mfa the m:f/a of the function being returned from */ probe bif__return(char *p, char *mfa); @@ -209,7 +209,7 @@ provider erlang { * Fired whenever a Native Function returns. * * @param p the PID (string form) of the process - * @param mfa the m:f/a of the function + * @param mfa the m:f/a of the function being returned from */ probe nif__return(char *p, char *mfa); diff --git a/erts/emulator/beam/error.h b/erts/emulator/beam/error.h index 64c08b1570..44a9809a18 100644 --- a/erts/emulator/beam/error.h +++ b/erts/emulator/beam/error.h @@ -66,13 +66,13 @@ #define EXF_OFFSET EXTAG_BITS #define EXF_BITS 7 -#define EXF_PANIC (1<<(0+EXF_OFFSET)) /* ignore catches */ -#define EXF_THROWN (1<<(1+EXF_OFFSET)) /* nonlocal return */ -#define EXF_LOG (1<<(2+EXF_OFFSET)) /* write to logger on termination */ -#define EXF_NATIVE (1<<(3+EXF_OFFSET)) /* occurred in native code */ -#define EXF_SAVETRACE (1<<(4+EXF_OFFSET)) /* save stack trace in internal form */ -#define EXF_ARGLIST (1<<(5+EXF_OFFSET)) /* has arglist for top of trace */ -#define EXF_RESTORE_NIF (1<<(6+EXF_OFFSET)) /* restore original bif/nif */ +#define EXF_PANIC (1<<(0+EXF_OFFSET)) /* ignore catches */ +#define EXF_THROWN (1<<(1+EXF_OFFSET)) /* nonlocal return */ +#define EXF_LOG (1<<(2+EXF_OFFSET)) /* write to logger on termination */ +#define EXF_NATIVE (1<<(3+EXF_OFFSET)) /* occurred in native code */ +#define EXF_SAVETRACE (1<<(4+EXF_OFFSET)) /* save stack trace in internal form */ +#define EXF_ARGLIST (1<<(5+EXF_OFFSET)) /* has arglist for top of trace */ +#define EXF_RESTORE_NFUNC (1<<(6+EXF_OFFSET)) /* restore original bif/nif */ #define EXC_FLAGBITS (((1<<(EXF_BITS+EXF_OFFSET))-1) \ & ~((1<<(EXF_OFFSET))-1)) diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c index b928f03b2f..af1b1c2892 100644 --- a/erts/emulator/beam/export.c +++ b/erts/emulator/beam/export.c @@ -129,14 +129,17 @@ export_alloc(struct export_entry* tmpl_e) obj->info.mfa.module = tmpl->info.mfa.module; obj->info.mfa.function = tmpl->info.mfa.function; obj->info.mfa.arity = tmpl->info.mfa.arity; - obj->beam[0] = 0; + obj->bif_number = -1; + obj->is_bif_traced = 0; + + memset(&obj->trampoline, 0, sizeof(obj->trampoline)); + if (BeamOpsAreInitialized()) { - obj->beam[0] = BeamOpCodeAddr(op_call_error_handler); + obj->trampoline.op = BeamOpCodeAddr(op_call_error_handler); } - obj->beam[1] = 0; for (ix=0; ix<ERTS_NUM_CODE_IX; ix++) { - obj->addressv[ix] = obj->beam; + obj->addressv[ix] = obj->trampoline.raw; blob->entryv[ix].slot.index = -1; blob->entryv[ix].ep = &blob->exp; @@ -204,6 +207,8 @@ static struct export_entry* init_template(struct export_templ* templ, templ->exp.info.mfa.module = m; templ->exp.info.mfa.function = f; templ->exp.info.mfa.arity = a; + templ->exp.bif_number = -1; + templ->exp.is_bif_traced = 0; return &templ->entry; } @@ -253,8 +258,8 @@ erts_find_function(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix) ee = hash_get(&export_tables[code_ix].htable, init_template(&templ, m, f, a)); if (ee == NULL || - (ee->ep->addressv[code_ix] == ee->ep->beam && - ! BeamIsOpCode(ee->ep->beam[0], op_i_generic_breakpoint))) { + (ee->ep->addressv[code_ix] == ee->ep->trampoline.raw && + ! BeamIsOpCode(ee->ep->trampoline.op, op_i_generic_breakpoint))) { return NULL; } return ee->ep; diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h index ae8dfa4cf8..91c4844d20 100644 --- a/erts/emulator/beam/export.h +++ b/erts/emulator/beam/export.h @@ -31,24 +31,72 @@ typedef struct export { - void* addressv[ERTS_NUM_CODE_IX]; /* Pointer to code for function. */ - - ErtsCodeInfo info; /* MUST be just before beam[] */ - - /* - * beam[0]: This entry is 0 unless the 'addressv' field points to it. - * Threaded code instruction to load function - * (em_call_error_handler), execute BIF (em_apply_bif), - * or a breakpoint instruction (op_i_generic_breakpoint). - * beam[1]: Function pointer to BIF function (for BIFs only), - * or pointer to threaded code if the module has an - * on_load function that has not been run yet, or pointer - * to code if function beam[0] is a breakpoint instruction. - * Otherwise: 0. - */ - BeamInstr beam[2]; + /* Pointer to code for function. */ + void* addressv[ERTS_NUM_CODE_IX]; + + /* Index into bif_table[], or -1 if not a BIF. */ + int bif_number; + /* Non-zero if this is a BIF that's traced. */ + int is_bif_traced; + + /* This is a small trampoline function that can be used for lazy code + * loading, global call tracing, and so on. It's only valid when + * addressv points to it and should otherwise be left zeroed. + * + * Needless to say, the order of the fields below is significant. */ + ErtsCodeInfo info; + union { + BeamInstr op; /* Union discriminant. */ + + struct { + BeamInstr op; /* op_i_generic_breakpoint */ + BeamInstr address; /* Address of the original function */ + } breakpoint; + + /* This is used when a module refers to (imports) a function that + * hasn't been loaded yet. Upon loading we create an export entry which + * redirects to the error_handler so that the appropriate module will + * be loaded when called (or crash). + * + * This is also used when a module has an on_load callback as we need + * to defer all calls until the callback returns. `deferred` contains + * the address of the original function in this case, and there's an + * awkward condiditon where `deferred` may be set while op is zero. See + * erlang:finish_after_on_load/2 for details. */ + struct { + BeamInstr op; /* op_call_error_handler, or 0 during the last + * phase of code loading when on_load is + * present. See above. */ + BeamInstr deferred; + } not_loaded; + + struct { + BeamInstr op; /* op_trace_jump_W */ + BeamInstr address; /* Address of the traced function */ + } trace; + + BeamInstr raw[2]; /* For use in address comparisons, should not + * be tampered directly. */ + } trampoline; } Export; +#ifdef DEBUG +#define DBG_CHECK_EXPORT(EP, CX) \ + do { \ + if((EP)->addressv[CX] == (EP)->trampoline.raw) { \ + /* The entry currently points at the trampoline, so the + * instructions must be valid. */ \ + ASSERT(((BeamIsOpCode((EP)->trampoline.op, op_i_generic_breakpoint)) && \ + (EP)->trampoline.breakpoint.address != 0) || \ + ((BeamIsOpCode((EP)->trampoline.op, op_trace_jump_W)) && \ + (EP)->trampoline.trace.address != 0) || \ + /* (EP)->trampoline.not_loaded.deferred may be zero. */ \ + (BeamIsOpCode((EP)->trampoline.op, op_call_error_handler))); \ + } \ + } while(0) +#else +#define DBG_CHECK_EXPORT(EP, CX) ((void)(EP), (void)(CX)) +#endif void init_export_table(void); void export_info(fmtfn_t, void *); @@ -71,9 +119,6 @@ extern erts_mtx_t export_staging_lock; #define export_staging_unlock() erts_mtx_unlock(&export_staging_lock) #include "beam_load.h" /* For em_* extern declarations */ -#define ExportIsBuiltIn(EntryPtr) \ -(((EntryPtr)->addressv[erts_active_code_ix()] == (EntryPtr)->beam) && \ - (BeamIsOpCode((EntryPtr)->beam[0], op_apply_bif))) #if ERTS_GLB_INLINE_INCL_FUNC_DEF diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 5cea253ebe..7f24876773 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1294,10 +1294,10 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1) } if (Opts == am_undefined) ERTS_BIF_ERROR_TRAPPED1(BIF_P, SYSTEM_LIMIT, - bif_export[BIF_term_to_binary_1], Term); + &bif_trap_export[BIF_term_to_binary_1], Term); else ERTS_BIF_ERROR_TRAPPED2(BIF_P, SYSTEM_LIMIT, - bif_export[BIF_term_to_binary_2], Term, Opts); + &bif_trap_export[BIF_term_to_binary_2], Term, Opts); } if (is_tuple(res)) { ASSERT(BIF_P->flags & F_DISABLE_GC); @@ -1413,7 +1413,7 @@ enum B2TState { /* order is somewhat significant */ }; typedef struct { - int heap_size; + Sint heap_size; int terms; byte* ep; int atom_extra_skip; @@ -1803,8 +1803,8 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Eterm bin, B2TContext *ctx) case B2TBadArg: BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION); - ASSERT(ctx->bif == bif_export[BIF_binary_to_term_1] - || ctx->bif == bif_export[BIF_binary_to_term_2]); + ASSERT(ctx->bif == &bif_trap_export[BIF_binary_to_term_1] + || ctx->bif == &bif_trap_export[BIF_binary_to_term_2]); if (is_first_call) ERTS_BIF_PREP_ERROR(ret_val, p, BADARG); @@ -1885,7 +1885,7 @@ BIF_RETTYPE binary_to_term_1(BIF_ALIST_1) ctx.flags = 0; ctx.used_bytes = 0; ctx.trap_bin = THE_NON_VALUE; - ctx.bif = bif_export[BIF_binary_to_term_1]; + ctx.bif = &bif_trap_export[BIF_binary_to_term_1]; ctx.arg[0] = BIF_ARG_1; ctx.arg[1] = THE_NON_VALUE; return binary_to_term_int(BIF_P, BIF_ARG_1, &ctx); @@ -1920,7 +1920,7 @@ BIF_RETTYPE binary_to_term_2(BIF_ALIST_2) goto error; ctx.trap_bin = THE_NON_VALUE; - ctx.bif = bif_export[BIF_binary_to_term_2]; + ctx.bif = &bif_trap_export[BIF_binary_to_term_2]; ctx.arg[0] = BIF_ARG_1; ctx.arg[1] = BIF_ARG_2; return binary_to_term_int(BIF_P, BIF_ARG_1, &ctx); @@ -4536,7 +4536,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, static Sint decoded_size(byte *ep, byte* endp, int internal_tags, B2TContext* ctx) { - int heap_size; + Sint heap_size; int terms; int atom_extra_skip; Uint n; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 40c65461bc..dd114a98fa 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -122,6 +122,10 @@ void erts_nif_demonitored(ErtsResource* resource); extern void erts_add_taint(Eterm mod_atom); extern Eterm erts_nif_taints(Process* p); extern void erts_print_nif_taints(fmtfn_t to, void* to_arg); + +/* Loads the specified NIF. The caller must have code write permission. */ +Eterm erts_load_nif(Process *c_p, BeamInstr *I, Eterm filename, Eterm args); + void erts_unload_nif(struct erl_module_nif* nif); extern void erl_nif_init(void); extern int erts_nif_get_funcs(struct erl_module_nif*, @@ -885,6 +889,8 @@ void erts_bif_info_init(void); /* bif.c */ +void erts_write_bif_wrapper(Export *export, BeamInstr *address); + void erts_queue_monitor_message(Process *, ErtsProcLocks*, Eterm, @@ -1147,6 +1153,7 @@ void erts_dirty_process_main(ErtsSchedulerData *); Eterm build_stacktrace(Process* c_p, Eterm exc); Eterm expand_error_value(Process* c_p, Uint freason, Eterm Value); void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth); +BeamInstr *erts_printable_return_address(Process* p, Eterm *E) ERTS_NOINLINE; /* erl_init.c */ diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab index 38b1e5909b..c9291c8cc4 100644 --- a/erts/emulator/beam/instrs.tab +++ b/erts/emulator/beam/instrs.tab @@ -120,7 +120,7 @@ dealloc_ret.execute() { E = ADD_BYTE_OFFSET(E, num_bytes); $RETURN(); CHECK_TERM(x(0)); - DispatchReturn; + $DISPATCH_RETURN(); } move_deallocate_return(Src, Deallocate) { @@ -135,33 +135,22 @@ move_deallocate_return(Src, Deallocate) { Eterm src = $Src; E = ADD_BYTE_OFFSET(E, bytes_to_pop); x(0) = src; + DTRACE_RETURN_FROM_PC(c_p, I); $RETURN(); CHECK_TERM(x(0)); - DispatchReturn; + $DISPATCH_RETURN(); } // Call instructions -DISPATCH_REL(CallDest) { - //| -no_next - $SET_I_REL($CallDest); - DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I)); - Dispatch(); -} - -DISPATCH_ABS(CallDest) { - //| -no_next - SET_I((BeamInstr *) $CallDest); - DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I)); - Dispatch(); -} - i_call(CallDest) { + //| -no_next $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION); $DISPATCH_REL($CallDest); } move_call(Src, CallDest) { + //| -no_next Eterm call_dest = $CallDest; Eterm src = $Src; $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION); @@ -170,11 +159,13 @@ move_call(Src, CallDest) { } i_call_last(CallDest, Deallocate) { + //| -no_next $deallocate($Deallocate); $DISPATCH_REL($CallDest); } move_call_last(Src, CallDest, Deallocate) { + //| -no_next Eterm call_dest = $CallDest; Eterm src = $Src; $deallocate($Deallocate); @@ -183,59 +174,59 @@ move_call_last(Src, CallDest, Deallocate) { } i_call_only(CallDest) { + //| -no_next $DISPATCH_REL($CallDest); } move_call_only(Src, CallDest) { + //| -no_next Eterm call_dest = $CallDest; Eterm src = $Src; x(0) = src; $DISPATCH_REL(call_dest); } -DISPATCHX(Dest) { - //| -no_next - DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, $Dest); - // Dispatchx assumes the Export* is in Arg(0) - I = (&$Dest) - 1; - Dispatchx(); -} - i_call_ext(Dest) { + //| -no_next $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION); - $DISPATCHX($Dest); + $DISPATCH_EXPORT($Dest); } i_move_call_ext(Src, CallDest) { + //| -no_next Eterm call_dest = $CallDest; Eterm src = $Src; $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION); x(0) = src; - $DISPATCHX(call_dest); + $DISPATCH_EXPORT(call_dest); } i_call_ext_only(Dest) { - $DISPATCHX($Dest); + //| -no_next + $DISPATCH_EXPORT($Dest); } i_move_call_ext_only(CallDest, Src) { + //| -no_next Eterm call_dest = $CallDest; Eterm src = $Src; x(0) = src; - $DISPATCHX(call_dest); + $DISPATCH_EXPORT(call_dest); } i_call_ext_last(Dest, Deallocate) { + //| -no_next $deallocate($Deallocate); - $DISPATCHX($Dest); + $DISPATCH_EXPORT($Dest); } i_move_call_ext_last(CallDest, Deallocate, Src) { + //| -no_next Eterm call_dest = $CallDest; Eterm src = $Src; $deallocate($Deallocate); x(0) = src; - $DISPATCHX(call_dest); + $DISPATCH_EXPORT(call_dest); } APPLY(I, Deallocate, Next) { @@ -246,11 +237,12 @@ APPLY(I, Deallocate, Next) { } HANDLE_APPLY_ERROR() { - I = handle_error(c_p, I, reg, &bif_export[BIF_apply_3]->info.mfa); + I = handle_error(c_p, I, reg, &bif_trap_export[BIF_apply_3].info.mfa); goto post_error_handling; } i_apply() { + //| -no_next BeamInstr *next; $APPLY(NULL, 0, next); if (ERTS_LIKELY(next != NULL)) { @@ -261,6 +253,7 @@ i_apply() { } i_apply_last(Deallocate) { + //| -no_next BeamInstr *next; $APPLY(I, $Deallocate, next); if (ERTS_LIKELY(next != NULL)) { @@ -271,6 +264,7 @@ i_apply_last(Deallocate) { } i_apply_only() { + //| -no_next BeamInstr *next; $APPLY(I, 0, next); if (ERTS_LIKELY(next != NULL)) { @@ -287,6 +281,7 @@ FIXED_APPLY(Arity, I, Deallocate, Next) { } apply(Arity) { + //| -no_next BeamInstr *next; $FIXED_APPLY($Arity, NULL, 0, next); if (ERTS_LIKELY(next != NULL)) { @@ -297,6 +292,7 @@ apply(Arity) { } apply_last(Arity, Deallocate) { + //| -no_next BeamInstr *next; $FIXED_APPLY($Arity, I, $Deallocate, next); if (ERTS_LIKELY(next != NULL)) { @@ -316,13 +312,8 @@ HANDLE_APPLY_FUN_ERROR() { goto find_func_info; } -DISPATCH_FUN(I) { - //| -no_next - SET_I($I); - Dispatchfun(); -} - i_apply_fun() { + //| -no_next BeamInstr *next; $APPLY_FUN(next); if (ERTS_LIKELY(next != NULL)) { @@ -333,6 +324,7 @@ i_apply_fun() { } i_apply_fun_last(Deallocate) { + //| -no_next BeamInstr *next; $APPLY_FUN(next); if (ERTS_LIKELY(next != NULL)) { @@ -343,6 +335,7 @@ i_apply_fun_last(Deallocate) { } i_apply_fun_only() { + //| -no_next BeamInstr *next; $APPLY_FUN(next); if (ERTS_LIKELY(next != NULL)) { @@ -359,6 +352,7 @@ CALL_FUN(Fun, Next) { } i_call_fun(Fun) { + //| -no_next BeamInstr *next; $CALL_FUN($Fun, next); if (ERTS_LIKELY(next != NULL)) { @@ -369,6 +363,7 @@ i_call_fun(Fun) { } i_call_fun_last(Fun, Deallocate) { + //| -no_next BeamInstr *next; $CALL_FUN($Fun, next); if (ERTS_LIKELY(next != NULL)) { @@ -380,11 +375,12 @@ i_call_fun_last(Fun, Deallocate) { return() { //| -no_next + DTRACE_RETURN_FROM_PC(c_p, I); $RETURN(); - DTRACE_RETURN_FROM_PC(c_p); CHECK_TERM(r(0)); HEAP_SPACE_VERIFIED(0); - DispatchReturn; + + $DISPATCH_RETURN(); } get_list(Src, Hd, Tl) { @@ -676,8 +672,9 @@ move_window5(S1, S2, S3, S4, S5, D) { move_return(Src) { //| -no_next x(0) = $Src; + DTRACE_RETURN_FROM_PC(c_p, I); $RETURN(); - DispatchReturn; + $DISPATCH_RETURN(); } move_x1(Src) { diff --git a/erts/emulator/beam/macros.tab b/erts/emulator/beam/macros.tab index 9d183e1f41..802c8aec9a 100644 --- a/erts/emulator/beam/macros.tab +++ b/erts/emulator/beam/macros.tab @@ -136,6 +136,90 @@ AH(NeedStack, NeedHeap, Live) { *E = NIL; } + +// +// Helpers for call instructions +// + +DISPATCH() { + BeamInstr dis_next; + + dis_next = *I; + CHECK_ARGS(I); + + if (FCALLS > 0 || FCALLS > neg_o_reds) { + FCALLS--; + Goto(dis_next); + } else { + goto context_switch; + } +} + +DISPATCH_ABS(CallDest) { + SET_I((BeamInstr *) $CallDest); + DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I)); + + $DISPATCH(); +} + +DISPATCH_EXPORT(Export) { + BeamInstr dis_next; + Export *ep; + + ep = (Export*)($Export); + + DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, ep); + + SET_I(ep->addressv[erts_active_code_ix()]); + CHECK_ARGS(I); + dis_next = *I; + + if (ERTS_UNLIKELY(FCALLS <= 0)) { + if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) && FCALLS > neg_o_reds) { + save_calls(c_p, ep); + } else { + goto context_switch; + } + } + + FCALLS--; + Goto(dis_next); +} + +DISPATCH_FUN(I) { + BeamInstr dis_next; + + SET_I($I); + + dis_next = *I; + CHECK_ARGS(I); + + if (FCALLS > 0 || FCALLS > neg_o_reds) { + FCALLS--; + Goto(dis_next); + } else { + goto context_switch_fun; + } +} + +DISPATCH_REL(CallDest) { + $SET_I_REL($CallDest); + DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I)); + + $DISPATCH(); +} + +DISPATCH_RETURN() { + if (FCALLS > 0 || FCALLS > neg_o_reds) { + FCALLS--; + Goto(*I); + } else { + c_p->current = NULL; + c_p->arity = 1; + goto context_switch3; + } +} + // Save the continuation pointer in the reserved slot at the // top of the stack as preparation for doing a function call. @@ -205,7 +289,7 @@ BIF_ERROR_ARITY_1(Fail, BIF, Op1) { } reg[0] = $Op1; SWAPOUT; - I = handle_error(c_p, I, reg, &bif_export[$BIF]->info.mfa); + I = handle_error(c_p, I, reg, &bif_trap_export[$BIF].info.mfa); goto post_error_handling; } @@ -217,6 +301,6 @@ BIF_ERROR_ARITY_2(Fail, BIF, Op1, Op2) { reg[0] = $Op1; reg[1] = $Op2; SWAPOUT; - I = handle_error(c_p, I, reg, &bif_export[$BIF]->info.mfa); + I = handle_error(c_p, I, reg, &bif_trap_export[$BIF].info.mfa); goto post_error_handling; } diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index c0ca9260a0..1d336e4b7b 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -77,19 +77,32 @@ return # To ensure that a "move Src x(0)" instruction can be combined with # the following call instruction, we need to make sure that there is # no line/1 instruction between the move and the call. -# -# A tail-recursive call to an external function (BIF or non-BIF) will -# never be saved on the stack, so there is no reason to keep the line -# instruction. + +move S X0=x==0 | line Loc | call Ar Func => \ + line Loc | move S X0 | call Ar Func move S X0=x==0 | line Loc | call_ext Ar Func => \ line Loc | move S X0 | call_ext Ar Func + +# +# A tail call will not refer to the current function on error unless it's a +# BIF, so we can omit the line instruction for non-BIFs. +# + +move S X0=x==0 | line Loc | call_ext_last Ar Func=u$is_bif D => \ + line Loc | move S X0 | call_ext_last Ar Func D +move S X0=x==0 | line Loc | call_ext_only Ar Func=u$is_bif => \ + line Loc | move S X0 | call_ext_only Ar Func + move S X0=x==0 | line Loc | call_ext_last Ar Func D => \ move S X0 | call_ext_last Ar Func D move S X0=x==0 | line Loc | call_ext_only Ar Func => \ move S X0 | call_ext_only Ar Func -move S X0=x==0 | line Loc | call Ar Func => \ - line Loc | move S X0 | call Ar Func + +move S X0=x==0 | line Loc | call_last Ar Func D => \ + move S X0 | call_last Ar Func D +move S X0=x==0 | line Loc | call_only Ar Func => \ + move S X0 | call_only Ar Func line Loc | func_info M F A => func_info M F A | line Loc @@ -574,8 +587,8 @@ put_list s s d %cold normal_exit continue_exit -apply_bif -call_nif +call_bif W +call_nif W W W call_error_handler error_action_code return_trace @@ -787,62 +800,22 @@ allocate_init t t? y # External function and bif calls. ################################################################# -# -# The BIFs erts_internal:check_process_code/1 must be called like a function, -# to ensure that c_p->i (program counter) is set correctly (an ordinary -# BIF call doesn't set it). -# - -call_ext u==1 Bif=u$bif:erts_internal:check_process_code/1 => i_call_ext Bif -call_ext_last u==1 Bif=u$bif:erts_internal:check_process_code/1 D => i_call_ext_last Bif D -call_ext_only u==1 Bif=u$bif:erts_internal:check_process_code/1 => i_call_ext_only Bif - -# -# The BIFs erts_internal:garbage_collect/1 must be called like a function, -# to allow them to invoke the garbage collector. (The stack pointer must -# be saved and p->arity must be zeroed, which is not done on ordinary BIF calls.) -# -call_ext u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext Bif -call_ext_last u==1 Bif=u$bif:erts_internal:garbage_collect/1 D => i_call_ext_last Bif D -call_ext_only u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext_only Bif +# Expands into call_light_bif(_only)/2 +call_light_bif/1 +call_light_bif_only/1 +call_light_bif_last/2 # -# put/2 and erase/1 must be able to do garbage collection, so we must call -# them like functions. +# The load_nif/2 BIF is an instruction. # -call_ext u==2 Bif=u$bif:erlang:put/2 => i_call_ext Bif -call_ext_last u==2 Bif=u$bif:erlang:put/2 D => i_call_ext_last Bif D -call_ext_only u==2 Bif=u$bif:erlang:put/2 => i_call_ext_only Bif - -call_ext u==1 Bif=u$bif:erlang:erase/1 => i_call_ext Bif -call_ext_last u==1 Bif=u$bif:erlang:erase/1 D => i_call_ext_last Bif D -call_ext_only u==1 Bif=u$bif:erlang:erase/1 => i_call_ext_only Bif - -# -# The process_info/1,2 BIF should be called like a function, to force -# the emulator to set c_p->current before calling it (a BIF call doesn't -# set it). -# -# In addition, we force the use of a non-tail-recursive call. This will ensure -# that c_p->cp points into the function making the call. -# - -call_ext u==1 Bif=u$bif:erlang:process_info/1 => i_call_ext Bif -call_ext_last u==1 Bif=u$bif:erlang:process_info/1 D => i_call_ext Bif | deallocate_return D -call_ext_only Ar=u==1 Bif=u$bif:erlang:process_info/1 => allocate u Ar | i_call_ext Bif | deallocate_return u - -call_ext u==2 Bif=u$bif:erlang:process_info/2 => i_call_ext Bif -call_ext_last u==2 Bif=u$bif:erlang:process_info/2 D => i_call_ext Bif | deallocate_return D -call_ext_only Ar=u==2 Bif=u$bif:erlang:process_info/2 => allocate u Ar | i_call_ext Bif | deallocate_return u - -# -# load_nif/2 also needs to know calling function like process_info -# -call_ext u==2 Bif=u$bif:erlang:load_nif/2 => i_call_ext Bif -call_ext_last u==2 Bif=u$bif:erlang:load_nif/2 D => i_call_ext Bif | deallocate_return D -call_ext_only Ar=u==2 Bif=u$bif:erlang:load_nif/2 => allocate u Ar | i_call_ext Bif | deallocate_return u +call_ext u==2 u$func:erlang:load_nif/2 => i_load_nif +call_ext_last u==2 u$func:erlang:load_nif/2 D => i_load_nif | deallocate_return D +call_ext_only u==2 u$func:erlang:load_nif/2 => i_load_nif | return +%cold +i_load_nif +%hot # # apply/2 is an instruction, not a BIF. @@ -861,33 +834,6 @@ call_ext_last u==3 u$bif:erlang:apply/3 D => i_apply_last D call_ext_only u==3 u$bif:erlang:apply/3 => i_apply_only # -# The exit/1 and throw/1 BIFs never execute the instruction following them; -# thus there is no need to generate any return instruction. -# - -call_ext_last u==1 Bif=u$bif:erlang:exit/1 D => call_bif Bif -call_ext_last u==1 Bif=u$bif:erlang:throw/1 D => call_bif Bif - -call_ext_only u==1 Bif=u$bif:erlang:exit/1 => call_bif Bif -call_ext_only u==1 Bif=u$bif:erlang:throw/1 => call_bif Bif - -# -# The error/1 and error/2 BIFs never execute the instruction following them; -# thus there is no need to generate any return instruction. -# However, they generate stack backtraces, so if the call instruction -# is call_ext_only/2 instruction, we explicitly do an allocate/2 to store -# the continuation pointer on the stack. -# - -call_ext_last u==1 Bif=u$bif:erlang:error/1 D => call_bif Bif -call_ext_last u==2 Bif=u$bif:erlang:error/2 D => call_bif Bif - -call_ext_only Ar=u==1 Bif=u$bif:erlang:error/1 => \ - allocate u Ar | call_bif Bif -call_ext_only Ar=u==2 Bif=u$bif:erlang:error/2 => \ - allocate u Ar | call_bif Bif - -# # The yield/0 BIF is an instruction # @@ -999,17 +945,24 @@ call_ext_only u==0 u$func:os:perf_counter/0 => \ i_perf_counter | return # -# The general case for BIFs that have no special instructions. -# A BIF used in the tail must be followed by a return instruction. +# BIFs like process_info/1,2 require up-to-date information about the current +# emulator state, which the ordinary call_light_bif instruction doesn't save. # -# To make trapping and stack backtraces work correctly, we make sure that -# the continuation pointer is always stored on the stack. -call_ext u Bif=u$is_bif => call_bif Bif +call_ext u Bif=u$is_bif | is_heavy_bif(Bif) => \ + i_call_ext Bif +call_ext_last u Bif=u$is_bif D | is_heavy_bif(Bif) => \ + i_call_ext Bif | deallocate_return D +call_ext_only Ar=u Bif=u$is_bif | is_heavy_bif(Bif) => \ + allocate u Ar | i_call_ext Bif | deallocate_return u -call_ext_last u Bif=u$is_bif D => deallocate D | call_bif_only Bif +# +# The general case for BIFs that have no special requirements. +# -call_ext_only Ar=u Bif=u$is_bif => call_bif_only Bif +call_ext u Bif=u$is_bif => call_light_bif Bif +call_ext_last u Bif=u$is_bif D => call_light_bif_last Bif D +call_ext_only Ar=u Bif=u$is_bif => call_light_bif_only Bif # # Any remaining calls are calls to Erlang functions, not BIFs. @@ -1034,14 +987,32 @@ i_apply_fun i_apply_fun_last Q i_apply_fun_only +# +# When a BIF is traced, these instructions make a body call through the export +# entry instead of calling the BIF directly (setting up a temporary stack frame +# if needed). We therefore retain the stack frame in call_light_bif_last, and +# add a deallocate_return after call_light_bif_only to remove the temporary +# stack frame before returning. +# + +call_light_bif Bif=u$is_bif => \ + call_light_bif Bif Bif + +call_light_bif_last Bif=u$is_bif D => \ + call_light_bif Bif Bif | deallocate_return D + +call_light_bif_only Bif=u$is_bif => \ + call_light_bif_only Bif Bif | deallocate_return u + +call_light_bif b e +call_light_bif_only b e + %cold -i_hibernate +i_hibernate i_perf_counter -%hot -call_bif e -call_bif_only e +%hot # # Calls to non-building and guard BIFs. diff --git a/erts/emulator/beam/trace_instrs.tab b/erts/emulator/beam/trace_instrs.tab index 9f22587f96..18e48804f7 100644 --- a/erts/emulator/beam/trace_instrs.tab +++ b/erts/emulator/beam/trace_instrs.tab @@ -107,7 +107,7 @@ i_hibernate() { goto do_schedule; } else { HEAVY_SWAPIN; - I = handle_error(c_p, I, reg, &bif_export[BIF_hibernate_3]->info.mfa); + I = handle_error(c_p, I, reg, &bif_trap_export[BIF_hibernate_3].info.mfa); goto post_error_handling; } //| -no_next diff --git a/erts/emulator/hipe/hipe_instrs.tab b/erts/emulator/hipe/hipe_instrs.tab index 62162fcb9c..8aa8544b2a 100644 --- a/erts/emulator/hipe/hipe_instrs.tab +++ b/erts/emulator/hipe/hipe_instrs.tab @@ -93,7 +93,7 @@ hipe_trap.post() { /*fall through*/ case HIPE_MODE_SWITCH_RES_CALL_BEAM: SET_I(c_p->i); - Dispatch(); + $DISPATCH(); case HIPE_MODE_SWITCH_RES_CALL_CLOSURE: /* This can be used to call any function value, but currently it's only used to call closures referring to unloaded @@ -104,8 +104,7 @@ hipe_trap.post() { next = call_fun(c_p, c_p->arity - 1, reg, THE_NON_VALUE); HEAVY_SWAPIN; if (next != NULL) { - SET_I(next); - Dispatchfun(); + $DISPATCH_FUN(next); } goto find_func_info; } diff --git a/erts/emulator/internal_doc/beam_makeops.md b/erts/emulator/internal_doc/beam_makeops.md index 2880099b70..267af78412 100644 --- a/erts/emulator/internal_doc/beam_makeops.md +++ b/erts/emulator/internal_doc/beam_makeops.md @@ -1457,26 +1457,26 @@ all instructions. It expands to the address of the next instruction. Here is an example: i_call(CallDest) { - SET_CP(c_p, $NEXT_INSTRUCTION); + //| -no_next + $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION); $DISPATCH_REL($CallDest); } -When calling a function, the return address is first stored in `c_p->cp` -(using the `SET_CP()` macro defined in `beam_emu.c`), and then control is +When calling a function, the return address is first stored in `E[0]` +(using the `$SAVE_CONTINUATION_POINTER()` macro), and then control is transferred to the callee. Here is the generated code: OpCase(i_call_f): { - SET_CP(c_p, I+1); - ASSERT(VALID_INSTR(*(I + (fb(BeamExtraData(I[0]))) + 0))); - I += fb(BeamExtraData(I[0])) + 0;; - DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I)); - Dispatch();; + ASSERT(VALID_INSTR(*(I+2))); + *E = (BeamInstr) (I+2);; + + /* ... dispatch code intentionally left out ... */ } -We can see that that `$NEXT_INSTRUCTION` has been expanded to `I+1`. +We can see that that `$NEXT_INSTRUCTION` has been expanded to `I+2`. That makes sense since the size of the `i_call_f/1` instruction is -one word. +two words. ##### The IP_ADJUSTMENT pre-bound variable ##### diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c index 176a9318b2..e5099dd921 100644 --- a/erts/emulator/nifs/unix/unix_prim_file.c +++ b/erts/emulator/nifs/unix/unix_prim_file.c @@ -566,18 +566,54 @@ int efile_allocate(efile_data_t *d, Sint64 offset, Sint64 length) { } while(ret < 0 && errno == EINTR); #elif defined(F_PREALLOCATE) /* Mac-specific */ + off_t original_position, eof_offset; fstore_t fs = {}; + if(offset < 0 || length < 0 || (offset > ERTS_SINT64_MAX - length)) { + u->common.posix_errno = EINVAL; + return 0; + } + + original_position = lseek(u->fd, 0, SEEK_CUR); + + if(original_position < 0) { + u->common.posix_errno = errno; + return 0; + } + + eof_offset = lseek(u->fd, 0, SEEK_END); + + if(eof_offset < 0 || lseek(u->fd, original_position, SEEK_SET) < 0) { + u->common.posix_errno = errno; + return 0; + } + + if(offset + length <= eof_offset) { + /* File is already large enough. */ + return 1; + } + fs.fst_flags = F_ALLOCATECONTIG; - fs.fst_posmode = F_VOLPOSMODE; - fs.fst_offset = offset; - fs.fst_length = length; + fs.fst_posmode = F_PEOFPOSMODE; + fs.fst_offset = 0; + fs.fst_length = (offset + length) - eof_offset; ret = fcntl(u->fd, F_PREALLOCATE, &fs); if(ret < 0) { fs.fst_flags = F_ALLOCATEALL; ret = fcntl(u->fd, F_PREALLOCATE, &fs); } + + if(ret >= 0) { + /* We MUST truncate since F_PREALLOCATE works relative to end-of-file, + * otherwise we will expand the file on repeated calls to + * file:allocate/3 with the same arguments. */ + ret = ftruncate(u->fd, offset + length); + if(ret < 0) { + u->common.posix_errno = errno; + return 0; + } + } #elif !defined(HAVE_POSIX_FALLOCATE) u->common.posix_errno = ENOTSUP; return 0; diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index 742592f88e..477b0f5bb3 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -832,21 +832,27 @@ deep_exception() -> R1 -> ct:fail({returned,abbr(R1)}) catch error:badarg -> ok end, - expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) + expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps) when is_list(L1), is_list(L2), S == Self -> - next; + %% Each trapping call to reverse/2 must have a corresponding + %% exception_from + {next, Traps + 1}; ({trace,S,exception_from, - {lists,reverse,2},{error,badarg}}) + {lists,reverse,2},{error,badarg}}, Traps) + when S == Self, Traps > 1 -> + {next, Traps - 1}; + ({trace,S,exception_from, + {lists,reverse,2},{error,badarg}}, 1) when S == Self -> expected; - ('_') -> + ('_', _Traps) -> {trace,Self,exception_from, {lists,reverse,2},{error,badarg}}; - (_) -> + (_, _Traps) -> {unexpected, {trace,Self,exception_from, {lists,reverse,2},{error,badarg}}} - end), + end, 0), deep_exception(?LINE, deep_5, [1,2], 7, [{trace,Self,call,{erlang,error,[undef]}}, {trace,Self,exception_from,{erlang,error,1}, @@ -896,21 +902,27 @@ deep_exception() -> R2 -> ct:fail({returned,abbr(R2)}) catch error:badarg -> ok end, - expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) + expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps) when is_list(L1), is_list(L2), S == Self -> - next; + %% Each trapping call to reverse/2 must have a corresponding + %% exception_from + {next, Traps + 1}; + ({trace,S,exception_from, + {lists,reverse,2},{error,badarg}}, Traps) + when S == Self, Traps > 1 -> + {next, Traps - 1}; ({trace,S,exception_from, - {lists,reverse,2},{error,badarg}}) + {lists,reverse,2},{error,badarg}}, 1) when S == Self -> expected; - ('_') -> + ('_', _Traps) -> {trace,Self,exception_from, {lists,reverse,2},{error,badarg}}; - (_) -> + (_, _Traps) -> {unexpected, {trace,Self,exception_from, {lists,reverse,2},{error,badarg}}} - end), + end, 0), deep_exception(?LINE, apply, [?MODULE,deep_5,[1,2]], 7, [{trace,Self,call,{erlang,error,[undef]}}, {trace,Self,exception_from,{erlang,error,1}, @@ -975,21 +987,27 @@ deep_exception() -> R3 -> ct:fail({returned,abbr(R3)}) catch error:badarg -> ok end, - expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}) + expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps) when is_list(L1), is_list(L2), S == Self -> - next; + %% Each trapping call to reverse/2 must have a corresponding + %% exception_from + {next, Traps + 1}; + ({trace,S,exception_from, + {lists,reverse,2},{error,badarg}}, Traps) + when S == Self, Traps > 1 -> + {next, Traps - 1}; ({trace,S,exception_from, - {lists,reverse,2},{error,badarg}}) + {lists,reverse,2},{error,badarg}}, 1) when S == Self -> expected; - ('_') -> + ('_', _Traps) -> {trace,Self,exception_from, {lists,reverse,2},{error,badarg}}; - (_) -> + (_, _Traps) -> {unexpected, {trace,Self,exception_from, {lists,reverse,2},{error,badarg}}} - end), + end, 0), deep_exception(?LINE, apply, [fun () -> ?MODULE:deep_5(1,2) end, []], 7, [{trace,Self,call,{erlang,error,[undef]}}, @@ -1249,6 +1267,24 @@ expect(Message) -> ct:fail(no_trace_message) end. +expect(Validator, State0) when is_function(Validator) -> + receive + M -> + case Validator(M, State0) of + expected -> + ok = io:format("Expected and got ~p", [abbr(M)]); + {next, State} -> + ok = io:format("Expected and got ~p", [abbr(M)]), + expect(Validator, State); + {unexpected,Message} -> + io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]), + ct:fail({unexpected,abbr([M|flush()])}) + end + after 5000 -> + io:format("Expected ~p; got nothing", [abbr(Validator('_'))]), + ct:fail(no_trace_message) + end. + trace_info(What, Key) -> get(tracer) ! {apply,self(),{erlang,trace_info,[What,Key]}}, Res = receive diff --git a/erts/emulator/test/dirty_bif_SUITE.erl b/erts/emulator/test/dirty_bif_SUITE.erl index 4f5ad0295a..2ded862b8a 100644 --- a/erts/emulator/test/dirty_bif_SUITE.erl +++ b/erts/emulator/test/dirty_bif_SUITE.erl @@ -397,7 +397,9 @@ dirty_process_trace(Config) when is_list(Config) -> access_dirty_process( Config, fun() -> - erlang:trace_pattern({erts_debug,dirty_io,2}, + %% BIFs can only be traced when their modules are loaded. + code:ensure_loaded(erts_debug), + 1 = erlang:trace_pattern({erts_debug,dirty_io,2}, [{'_',[],[{return_trace}]}], [local,meta]), ok diff --git a/erts/emulator/test/lcnt_SUITE.erl b/erts/emulator/test/lcnt_SUITE.erl index 2dbaec9942..7bee7cf1d4 100644 --- a/erts/emulator/test/lcnt_SUITE.erl +++ b/erts/emulator/test/lcnt_SUITE.erl @@ -24,8 +24,7 @@ -export( [all/0, suite/0, - init_per_suite/1, end_per_suite/1, - init_per_testcase/2, end_per_testcase/2]). + init_per_suite/1, end_per_suite/1]). -export( [toggle_lock_counting/1, error_on_invalid_category/1, preserve_locks/1, @@ -63,13 +62,6 @@ end_per_suite(Config) -> erts_debug:lcnt_clear(), ok. -init_per_testcase(_Case, Config) -> - disable_lock_counting(), - Config. - -end_per_testcase(_Case, _Config) -> - ok. - disable_lock_counting() -> ok = erts_debug:lcnt_control(copy_save, false), ok = erts_debug:lcnt_control(mask, []), @@ -96,7 +88,10 @@ wait_for_empty_lock_list(Tries) when Tries > 0 -> wait_for_empty_lock_list(Tries - 1) end; wait_for_empty_lock_list(0) -> - ct:fail("Lock list failed to clear after disabling lock counting."). + [{duration, _}, {locks, Locks0}] = erts_debug:lcnt_collect(), + Locks = remove_untoggleable_locks(Locks0), + ct:fail("Lock list failed to clear after disabling lock counting.~n\t~p", + [Locks]). %% Queue up a lot of thread progress cleanup ops in a vain attempt to %% flush the lock list. @@ -109,6 +104,8 @@ try_flush_cleanup_ops() -> %% toggle_lock_counting(Config) when is_list(Config) -> + ok = disable_lock_counting(), + Categories = [allocator, db, debug, distribution, generic, io, process, scheduler], lists:foreach( @@ -131,6 +128,8 @@ get_lock_info_for(Category) when is_atom(Category) -> get_lock_info_for([Category]). preserve_locks(Config) when is_list(Config) -> + ok = disable_lock_counting(), + erts_debug:lcnt_control(mask, [process]), erts_debug:lcnt_control(copy_save, true), @@ -155,10 +154,14 @@ preserve_locks(Config) when is_list(Config) -> end. error_on_invalid_category(Config) when is_list(Config) -> + ok = disable_lock_counting(), + {error, badarg, q_invalid} = erts_debug:lcnt_control(mask, [q_invalid]), ok. registered_processes(Config) when is_list(Config) -> + ok = disable_lock_counting(), + %% There ought to be at least one registered process (init/code_server) erts_debug:lcnt_control(mask, [process]), [_, {locks, ProcLocks}] = erts_debug:lcnt_collect(), @@ -170,6 +173,8 @@ registered_processes(Config) when is_list(Config) -> ok. registered_db_tables(Config) when is_list(Config) -> + ok = disable_lock_counting(), + %% There ought to be at least one registered table (code) erts_debug:lcnt_control(mask, [db]), [_, {locks, DbLocks}] = erts_debug:lcnt_collect(), @@ -187,7 +192,7 @@ remove_untoggleable_locks([]) -> []; remove_untoggleable_locks([{resource_monitors, _, _, _} | T]) -> remove_untoggleable_locks(T); -remove_untoggleable_locks([{'socket[gcnt]', _, _, _} | T]) -> +remove_untoggleable_locks([{'esock[gcnt]', _, _, _} | T]) -> %% Global lock used by socket NIF remove_untoggleable_locks(T); remove_untoggleable_locks([H | T]) -> diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 21de6b1002..686b431876 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -198,7 +198,8 @@ caller_and_return_to(Config) -> {trace,Tracee,call,{?MODULE,do_the_put,[test]},{?MODULE,do_put,1}}, {trace,Tracee,call,{erlang,integer_to_list,[1]},{?MODULE,do_the_put,1}}, {trace,Tracee,return_to,{?MODULE,do_the_put,1}}, - {trace,Tracee,call,{erlang,put,[test,"1"]},{?MODULE,do_put,1}}, + {trace,Tracee,call,{erlang,put,[test,"1"]},{?MODULE,do_the_put,1}}, + {trace,Tracee,return_to,{?MODULE,do_the_put,1}}, {trace,Tracee,return_to,{?MODULE,do_put,1}}, %% These last trace messages are a bit strange... diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 26f96a1766..9bab4cbbd8 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -254,8 +254,7 @@ combo(Config) when is_list(Config) -> 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, [], [local]), 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]), 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, MetaMs, [{meta,MetaTracer}]), - %% not implemented - %2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]), + 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]), 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]), %% @@ -284,9 +283,7 @@ combo(Config) when is_list(Config) -> {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfoBif), {value,{meta, MetaTracer}} = lists:keysearch(meta, 1, TraceInfoBif), {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfoBif), - %% not implemented - {value,{call_count,false}} = lists:keysearch(call_count, 1, TraceInfoBif), - %{value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif), + {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif), {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfoBif), %% @@ -429,6 +426,8 @@ called_function(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dead_tracer(Config) when is_list(Config) -> + TracedMFAs = dead_tracer_mfas(), + Self = self(), FirstTracer = tracer(), StartTracing = fun() -> turn_on_tracing(Self) end, @@ -443,14 +442,14 @@ dead_tracer(Config) when is_list(Config) -> erlang:yield(), %% Collect and check that we only get call_time info for the current process. - Info1 = collect_all_info(), + Info1 = collect_all_info(TracedMFAs), [] = other_than_self(Info1), io:format("~p\n", [Info1]), %% Note that we have not turned off tracing for the current process, %% but that the tracer has terminated. No more call_time information should be recorded. [1,2,3] = seq(1, 3, fun(I) -> I + 1 end), - [] = collect_all_info(), + [] = collect_all_info(TracedMFAs), %% When we start a second tracer process, that tracer process must %% not inherit the tracing flags and the dead tracer (even though @@ -459,7 +458,7 @@ dead_tracer(Config) when is_list(Config) -> tell_tracer(SecondTracer, StartTracing), Seq20 = lists:seq(1, 20), Seq20 = seq(1, 20, fun(I) -> I + 1 end), - Info2 = collect_all_info(), + Info2 = collect_all_info(TracedMFAs), io:format("~p\n", [Info2]), [] = other_than_self(Info2), SecondTracer ! quit, @@ -495,9 +494,21 @@ turn_on_tracing(Pid) -> _ = now(), ok. -collect_all_info() -> - collect_all_info([{?MODULE,F,A} || {F,A} <- module_info(functions)] ++ - erlang:system_info(snifs)). +%% We want to trace functions local to this module as well as all BIFs, and for +%% the latter we need to ensure that their modules are loaded. +dead_tracer_mfas() -> + Modules = [M || {M,_F,_A} <- erlang:system_info(snifs)], + Whitelist0 = gb_sets:from_list(Modules), + Whitelist = case code:ensure_modules_loaded(Modules) of + {error, Reasons} -> + Blacklist = gb_sets:from_list([M || {M, _} <- Reasons]), + gb_sets:subtract(Whitelist0, Blacklist); + ok -> + Whitelist0 + end, + EligibleSNIFs = [MFA || {M,_F,_A}=MFA <- erlang:system_info(snifs), + gb_sets:is_element(M, Whitelist)], + [{?MODULE,F,A} || {F,A} <- module_info(functions)] ++ EligibleSNIFs. collect_all_info([MFA|T]) -> CallTime = erlang:trace_info(MFA, call_time), @@ -567,21 +578,29 @@ seq_r(Start, Stop, Succ, R) -> seq_r(Succ(Start), Stop, Succ, [Start | R]). % Check call time tracing data and print mismatches -check_trace_info(Mfa, [{Pid, C,_,_}] = Expect, Time) -> - case erlang:trace_info(Mfa, call_time) of - % Time tests are somewhat problematic. We want to know if Time (EXPECTED_TIME) and S*1000000 + Us (ACTUAL_TIME) - % is the same. - % If the ratio EXPECTED_TIME/ACTUAL_TIME is ~ 1 or if EXPECTED_TIME - ACTUAL_TIME is near zero, the test is ok. - {call_time,[{Pid,C,S,Us}]} when S >= 0, Us >= 0, abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; abs(Time - S*1000000 - Us) < ?US_ERROR -> +check_trace_info(Mfa, [{Pid, ExpectedC,_,_}] = Expect, Time) -> + {call_time,[{Pid,C,S,Us}]} = erlang:trace_info(Mfa, call_time), + {Mod, Name, Arity} = Mfa, + IsBuiltin = erlang:is_builtin(Mod, Name, Arity), + if + %% Call count on BIFs may exceed number of calls as they often trap to + %% themselves. + IsBuiltin, C >= ExpectedC, S >= 0, Us >= 0, + abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; + abs(Time - S*1000000 - Us) < ?US_ERROR -> ok; - {call_time,[{Pid,C,S,Us}]} -> + not IsBuiltin, C =:= ExpectedC, S >= 0, Us >= 0, + abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; + abs(Time - S*1000000 - Us) < ?US_ERROR -> + ok; + true -> Sum = S*1000000 + Us, - io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, should be 1.0)~n", - [Mfa, Expect, Time, S, Us, Sum, Time, Sum - Time, Time/Sum]), - time_error; - Other -> - io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~p~n", [ Mfa, Expect, Time, Other]), - time_count_error + io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w " + "s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, " + "should be 1.0)~n", + [Mfa, Expect, Time, + S, Us, Sum, Time, Sum - Time, Time/Sum]), + time_error end; check_trace_info(Mfa, Expect, _) -> case erlang:trace_info(Mfa, call_time) of @@ -670,9 +689,12 @@ loop() -> quit -> ok; {Pid, execute, Fun } when is_function(Fun) -> + %% Make sure we always run with the same amount of reductions. + erlang:yield(), Pid ! {self(), answer, erlang:apply(Fun, [])}, loop(); {Pid, execute, {M, F, A}} -> + erlang:yield(), Pid ! {self(), answer, erlang:apply(M, F, A)}, loop() end. diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 605a402f2a..cc21f9b5b4 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1315,7 +1315,7 @@ sub combine_instruction_group { my $inc = 0; unless ($i == $#slots) { - $flags = "-no_next"; + $flags = "-micro_instruction"; my $next_offset = $label_to_offset{$next}; $inc = ($offset + $size) - $next_offset; $transfer_to_next = "I += $inc;\n" if $inc; @@ -1553,8 +1553,10 @@ sub code_gen { my $dispatch_next; my $instr_offset = $group_size + $offset + 1; - if ($flags =~ /-no_next/) { + if ($flags =~ /-micro_instruction/) { $dispatch_next = ""; + } elsif ($flags =~ /-no_next/) { + $dispatch_next = "ASSERT(!\"Fell through '$name' (-no_next)\");"; } elsif ($flags =~ /-no_prefetch/) { $dispatch_next = "\nI += $instr_offset;\n" . "ASSERT(VALID_INSTR(*I));\n" . diff --git a/erts/emulator/utils/make_tables b/erts/emulator/utils/make_tables index deee5c2344..f87472111f 100755 --- a/erts/emulator/utils/make_tables +++ b/erts/emulator/utils/make_tables @@ -35,7 +35,6 @@ use File::Basename; # Output: # <-src>/erl_am.c # <-src>/erl_bif_table.c -# <-src>/erl_bif_wrap.c # <-src>/erl_dirty_bif_wrap.c # <-src>/erl_guard_bifs.c # <-src>/hipe_nbif_impl.c @@ -92,7 +91,7 @@ while (<>) { my($type, @args) = split; if ($type eq 'atom') { save_atoms(@args); - } elsif ($type eq 'bif' or $type eq 'ubif' or $type eq 'gcbif') { + } elsif ($type eq 'bif' or $type eq 'ubif' or $type eq 'hbif') { if (@args > 2) { error("$type only allows two arguments"); } @@ -124,14 +123,22 @@ while (<>) { error("invalid sched_type: $sched_type"); } - my $wrapper; - if ($type eq 'bif') { - $wrapper = "wrap_$alias"; - } else { - $wrapper = $alias; - } + my $kind; + if ($type eq 'bif') { + $kind = 'BIF_KIND_REGULAR'; + } + elsif ($type eq 'hbif') { + $kind = 'BIF_KIND_HEAVY'; + } + elsif ($type eq 'ubif') { + $kind = 'BIF_KIND_GUARD'; + } + else { + error("invalid bif_type: $type"); + } + push(@bif, ["am_$atom_alias{$mod}","am_$atom_alias{$name}",$arity, - $alias3,$wrapper,$alias]); + $alias3,$alias,$kind]); push(@bif_info, [$type, $sched_type, $alias3, $alias]); } elsif ($type eq 'dirty-cpu' or $type eq 'dirty-io' or $type eq 'dirty-cpu-test' or $type eq 'dirty-io-test') { @@ -196,7 +203,7 @@ open_file("$include/erl_bif_list.h"); my $i; for ($i = 0; $i < @bif; $i++) { # module atom, function atom, arity, C function, table index - print "BIF_LIST($bif[$i]->[0],$bif[$i]->[1],$bif[$i]->[2],$bif[$i]->[3],$bif[$i]->[5],$i)\n"; + print "BIF_LIST($bif[$i]->[0],$bif[$i]->[1],$bif[$i]->[2],$bif[$i]->[3],$bif[$i]->[4],$i)\n"; } # @@ -208,15 +215,24 @@ my $bif_size = @bif; print <<EOF; #ifndef __ERL_BIF_TABLE_H__ #define __ERL_BIF_TABLE_H__ + +#include "sys.h" + typedef void *BifFunction; +typedef enum { + BIF_KIND_REGULAR, + BIF_KIND_HEAVY, + BIF_KIND_GUARD +} BifKind; + typedef struct bif_entry { Eterm module; Eterm name; int arity; BifFunction f; - BifFunction traced; BifFunction impl; + BifKind kind; } BifEntry; typedef struct erts_gc_bif { @@ -231,8 +247,7 @@ typedef struct erts_u_bif { } ErtsUBif; extern BifEntry bif_table[]; -extern Export* bif_export[]; -extern const ErtsGcBif erts_gc_bifs[]; +extern Export bif_trap_export[]; extern const ErtsUBif erts_u_bifs[]; #define BIF_SIZE $bif_size @@ -250,7 +265,6 @@ for ($i = 0; $i < @bif; $i++) { my $args = join(', ', 'Process*', 'Eterm*', 'UWord*'); my $name = $bif_info[$i]->[3]; print "Eterm $name($args);\n"; - print "Eterm wrap_$name($args);\n"; print "Eterm erts_gc_$name(Process* p, Eterm* reg, Uint live);\n" if $bif_info[$i]->[0] eq 'gcbif'; print "Eterm $bif_info[$i]->[2]($args);\n" @@ -273,7 +287,7 @@ my $i; includes("export.h", "sys.h", "erl_vm.h", "erl_process.h", "bif.h", "erl_bif_table.h", "erl_atom_table.h"); -print "\nExport* bif_export[BIF_SIZE];\n"; +print "\nExport bif_trap_export[BIF_SIZE];\n"; print "BifEntry bif_table[] = {\n"; for ($i = 0; $i < @bif; $i++) { @@ -283,25 +297,6 @@ for ($i = 0; $i < @bif; $i++) { print "};\n\n"; # -# Generate the bif wrappers file. -# - -open_file("$src/erl_bif_wrap.c"); -my $i; -includes("export.h", "sys.h", "erl_vm.h", "global.h", "erl_process.h", "bif.h", - "erl_bif_table.h", "erl_atom_table.h"); -for ($i = 0; $i < @bif; $i++) { - next if $bif[$i]->[3] eq $bif[$i]->[4]; # Skip unwrapped bifs - my $arity = $bif[$i]->[2]; - my $func = $bif_info[$i]->[3]; - print "Eterm\n"; - print "wrap_$func(Process* p, Eterm* args, UWord* I)\n"; - print "{\n"; - print " return erts_bif_trace($i, p, args, I);\n"; - print "}\n\n"; -} - -# # Generate erl_gc_bifs.c. # @@ -309,18 +304,11 @@ open_file("$src/erl_guard_bifs.c"); my $i; includes("export.h", "sys.h", "erl_vm.h", "global.h", "erl_process.h", "bif.h", "erl_bif_table.h"); -print "const ErtsGcBif erts_gc_bifs[] = {\n"; -for ($i = 0; $i < @bif; $i++) { - next unless $bif_info[$i]->[0] eq 'gcbif'; - print " {$bif[$i]->[3], erts_gc_$bif[$i]->[3], BIF_$bif[$i]->[5]},\n"; -} -print " {NULL, NULL, -1}\n"; -print "};\n"; print "const ErtsUBif erts_u_bifs[] = {\n"; for ($i = 0; $i < @bif; $i++) { next unless $bif_info[$i]->[0] eq 'ubif'; - print " {$bif[$i]->[3], BIF_$bif[$i]->[5]},\n"; + print " {$bif[$i]->[3], BIF_$bif[$i]->[4]},\n"; } print " {NULL, -1}\n"; print "};\n"; @@ -368,7 +356,7 @@ EOF my $i; for ($i = 0; $i < @bif; $i++) { print <<EOF; -Eterm nbif_impl_$bif[$i]->[5](Process *c_p, Eterm *regs); +Eterm nbif_impl_$bif[$i]->[4](Process *c_p, Eterm *regs); EOF } @@ -388,9 +376,9 @@ EOF for ($i = 0; $i < @bif; $i++) { print <<EOF; -Eterm nbif_impl_$bif[$i]->[5](Process *c_p, Eterm *regs) +Eterm nbif_impl_$bif[$i]->[4](Process *c_p, Eterm *regs) { - return $bif[$i]->[3](c_p, regs, (UWord *) bif_export\[BIF_$bif[$i]->[5]\]); + return $bif[$i]->[3](c_p, regs, (UWord *)&bif_trap_export\[BIF_$bif[$i]->[4]\]); } EOF diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 59de9bdec8..d85a36acd4 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -315,8 +315,13 @@ if [ "x$GDB" = "x" ]; then ncpu=`cat /proc/cpuinfo | grep -w processor | wc -l` # Choose a random core in order to not collide with any other valgrind # run on the same machine. - taskset1=$((1 << (`shuf -i 1-$ncpu -n 1` - 1) )) - taskset1="taskset $taskset1" + cpu=`shuf -i 1-$ncpu -n 1` + mask=1 + while [ $cpu -gt 1 ]; do + mask=`expr $mask \* 2` + cpu=`expr $cpu - 1` + done + taskset1="taskset $mask" sched_arg="-S$ncpu:$ncpu" else taskset1= diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex 78a9a4eef2..1530982393 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index f1a1de4ab2..94595ab78f 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -189,6 +189,27 @@ -export([dt_get_tag/0, dt_get_tag_data/0, dt_prepend_vm_tag_data/1, dt_append_vm_tag_data/1, dt_put_tag/1, dt_restore_tag/1, dt_spread_tag/1]). +%% Operators + +-export(['=='/2, '=:='/2, + '/='/2, '=/='/2, + '=<'/2, '>='/2, + '<'/2, '>'/2]). + +-export(['-'/1, '+'/1, + '-'/2, '+'/2, + '/'/2, '*'/2, + 'div'/2, 'rem'/2, + 'bsl'/2, 'bsr'/2, + 'bor'/2, 'band'/2, + 'bxor'/2, 'bnot'/1]). + +-export(['and'/2, 'or'/2, + 'xor'/2, 'not'/1]). + +-export(['--'/2, '++'/2]). + +-export(['!'/2]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Simple native code BIFs @@ -3932,3 +3953,98 @@ gc_info(Ref, N, {OrigColls,OrigRecl}) -> {Ref, {_,Colls, Recl}} -> gc_info(Ref, N-1, {Colls+OrigColls,Recl+OrigRecl}) end. + +%% Operators + +-spec erlang:'=='(term(), term()) -> boolean(). +'=='(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'=:='(term(), term()) -> boolean(). +'=:='(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'/='(term(), term()) -> boolean(). +'/='(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'=/='(term(), term()) -> boolean(). +'=/='(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'=<'(term(), term()) -> boolean(). +'=<'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'>='(term(), term()) -> boolean(). +'>='(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'<'(term(), term()) -> boolean(). +'<'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'>'(term(), term()) -> boolean(). +'>'(_A, _B) -> + erlang:nif_error(undefined). + +-spec erlang:'-'(number()) -> number(). +'-'(_A) -> + erlang:nif_error(undefined). +-spec erlang:'+'(number()) -> number(). +'+'(_A) -> + erlang:nif_error(undefined). +-spec erlang:'-'(number(), number()) -> number(). +'-'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'+'(number(), number()) -> number(). +'+'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'/'(number(), number()) -> float(). +'/'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'*'(number(), number()) -> number(). +'*'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'div'(integer(), integer()) -> integer(). +'div'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'rem'(integer(), integer()) -> integer(). +'rem'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'bsl'(integer(), integer()) -> integer(). +'bsl'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'bsr'(integer(), integer()) -> integer(). +'bsr'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'bor'(integer(), integer()) -> integer(). +'bor'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'band'(integer(), integer()) -> integer(). +'band'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'bxor'(integer(), integer()) -> integer(). +'bxor'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'bnot'(integer()) -> integer(). +'bnot'(_A) -> + erlang:nif_error(undefined). + +-spec erlang:'--'(list(), list()) -> list(). +'--'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'++'(list(), term()) -> term(). +'++'(_A, _B) -> + erlang:nif_error(undefined). + +-spec erlang:'and'(boolean(), boolean()) -> boolean(). +'and'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'or'(boolean(), boolean()) -> boolean(). +'or'(_A, _B) -> + erlang:nif_error(undefined). + +-spec erlang:'xor'(boolean(), boolean()) -> boolean(). +'xor'(_A, _B) -> + erlang:nif_error(undefined). +-spec erlang:'not'(boolean()) -> boolean(). +'not'(_A) -> + erlang:nif_error(undefined). + +-spec erlang:'!'(dst(), term()) -> term(). +'!'(_Dst, _Msg) -> + erlang:nif_error(undefined). diff --git a/erts/vsn.mk b/erts/vsn.mk index f06fd08540..d1db2ceceb 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 10.4.4 +VSN = 10.5 # Port number 4365 in 4.2 # Port number 4366 in 4.3 |