aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin11940 -> 12036 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bs.beambin5476 -> 5464 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bsm.beambin11948 -> 11936 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_clean.beambin6384 -> 6552 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dead.beambin13068 -> 13052 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dict.beambin5068 -> 5060 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_disasm.beambin21684 -> 21680 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_except.beambin3216 -> 3216 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_flatten.beambin2956 -> 2988 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin8976 -> 8968 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_receive.beambin6112 -> 6112 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_reorder.beambin1908 -> 1904 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_split.beambin2132 -> 2168 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_trim.beambin7508 -> 7508 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_type.beambin17964 -> 17964 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin21876 -> 21896 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin29648 -> 30012 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin30048 -> 30040 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_inline.beambin37736 -> 37664 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin41340 -> 41332 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_lint.beambin12716 -> 12716 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_pp.beambin11704 -> 11704 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_scan.beambin6684 -> 6672 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin47508 -> 47428 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_inline.beambin3984 -> 3976 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_pre_attributes.beambin2724 -> 2716 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_codegen.beambin64932 -> 64756 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin57744 -> 57684 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin55940 -> 55868 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel_pp.beambin12456 -> 12456 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin30752 -> 30688 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_master.beambin6352 -> 6344 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_starter.beambin1196 -> 1188 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin23948 -> 24000 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log.beambin31896 -> 31848 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin23960 -> 23888 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_ac.beambin24872 -> 24844 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin12140 -> 12140 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_boot_server.beambin5760 -> 5744 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_io_server.beambin15716 -> 15700 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global.beambin31240 -> 31240 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin17020 -> 16916 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group_history.beambin5728 -> 5720 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin12500 -> 12496 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_db.beambin26412 -> 26404 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_dns.beambin19112 -> 19112 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_gethost_native.beambin10092 -> 10084 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_res.beambin14256 -> 14256 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_kernel.beambin25608 -> 25580 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin5148 -> 5148 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user.beambin11496 -> 11488 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/wrap_log_reader.beambin3124 -> 3124 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/array.beambin11776 -> 11776 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin2760 -> 2952 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin48872 -> 48872 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_server.beambin6740 -> 6732 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_utils.beambin27264 -> 27260 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v9.beambin47832 -> 47804 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph.beambin7872 -> 7852 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin27712 -> 27708 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_bits.beambin2468 -> 2452 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_eval.beambin30276 -> 30268 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_expand_records.beambin21736 -> 21732 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin92164 -> 92104 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin97420 -> 97400 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin26616 -> 26616 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_tar.beambin33404 -> 33388 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/escript.beambin16864 -> 16848 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ets.beambin22224 -> 22268 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/file_sorter.beambin29156 -> 29132 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_event.beambin13440 -> 13432 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_statem.beambin19684 -> 19656 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io.beambin6028 -> 6020 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_pretty.beambin17048 -> 17048 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lib.beambin14976 -> 14968 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ms_transform.beambin19452 -> 19440 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc.beambin68840 -> 68796 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin75052 -> 75004 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sofs.beambin37500 -> 37420 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/string.beambin35360 -> 35368 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin21912 -> 21880 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sys.beambin8364 -> 8356 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/uri_string.beambin26548 -> 26712 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/zip.beambin26172 -> 26148 bytes
-rw-r--r--erts/doc/src/erl_nif.xml11
-rw-r--r--erts/doc/src/erlang.xml1594
-rw-r--r--erts/doc/src/notes.xml2
-rw-r--r--erts/emulator/beam/atom.names1
-rw-r--r--erts/emulator/beam/bif.c15
-rw-r--r--erts/emulator/beam/bif.tab4
-rw-r--r--erts/emulator/beam/binary.c5
-rw-r--r--erts/emulator/beam/break.c21
-rw-r--r--erts/emulator/beam/bs_instrs.tab3
-rw-r--r--erts/emulator/beam/erl_bif_binary.c191
-rw-r--r--erts/emulator/beam/erl_bif_port.c21
-rw-r--r--erts/emulator/beam/erl_db.c28
-rw-r--r--erts/emulator/beam/erl_gc.c48
-rw-r--r--erts/emulator/beam/erl_init.c1
-rw-r--r--erts/emulator/beam/erl_io_queue.c36
-rw-r--r--erts/emulator/beam/erl_message.c6
-rw-r--r--erts/emulator/beam/erl_nif.c317
-rw-r--r--erts/emulator/beam/erl_nif.h3
-rw-r--r--erts/emulator/beam/erl_port.h39
-rw-r--r--erts/emulator/beam/erl_port_task.c25
-rw-r--r--erts/emulator/beam/erl_process.c236
-rw-r--r--erts/emulator/beam/erl_process.h154
-rw-r--r--erts/emulator/beam/erl_process_dump.c76
-rw-r--r--erts/emulator/beam/erl_trace.c30
-rw-r--r--erts/emulator/beam/io.c117
-rw-r--r--erts/emulator/beam/utils.c3
-rw-r--r--erts/emulator/nifs/unix/unix_prim_file.c19
-rw-r--r--erts/emulator/test/binary_SUITE.erl1
-rw-r--r--erts/emulator/test/efile_SUITE.erl21
-rw-r--r--erts/emulator/test/iovec_SUITE.erl46
-rw-r--r--erts/emulator/test/nif_SUITE.erl30
-rw-r--r--erts/etc/common/inet_gethost.c4
-rw-r--r--erts/etc/unix/etp-commands.in4
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin54268 -> 54160 bytes
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl2
-rw-r--r--erts/preloaded/src/erlang.erl9
-rw-r--r--erts/test/z_SUITE.erl2
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl18
-rw-r--r--lib/compiler/src/beam_block.erl23
-rw-r--r--lib/compiler/src/beam_clean.erl21
-rw-r--r--lib/compiler/src/beam_flatten.erl3
-rw-r--r--lib/compiler/src/beam_split.erl7
-rw-r--r--lib/compiler/src/beam_utils.erl81
-rw-r--r--lib/compiler/src/beam_validator.erl24
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl12
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/bad_try_catch_nesting.S64
-rw-r--r--lib/crypto/test/engine_SUITE.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl14
-rw-r--r--lib/dialyzer/src/dialyzer_plt.erl20
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl15
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl13
-rw-r--r--lib/diameter/doc/src/diameter.xml14
-rw-r--r--lib/diameter/src/base/diameter_reg.erl17
-rw-r--r--lib/diameter/src/base/diameter_service.erl22
-rw-r--r--lib/diameter/src/diameter.appup.src15
-rw-r--r--lib/diameter/vsn.mk4
-rw-r--r--lib/hipe/icode/hipe_icode_inline_bifs.erl22
-rw-r--r--lib/inets/doc/src/http_client.xml15
-rw-r--r--lib/inets/doc/src/http_uri.xml35
-rw-r--r--lib/inets/doc/src/httpc.xml18
-rw-r--r--lib/inets/src/http_client/httpc.erl56
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl34
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl6
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl10
-rw-r--r--lib/inets/src/http_lib/http_uri.erl43
-rw-r--r--lib/inets/src/inets_app/inets.appup.src4
-rw-r--r--lib/inets/test/http_test_lib.erl21
-rw-r--r--lib/inets/test/httpc_SUITE.erl112
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/kernel/doc/src/file.xml3
-rw-r--r--lib/kernel/doc/src/inet.xml19
-rw-r--r--lib/kernel/doc/src/inet_res.xml6
-rw-r--r--lib/kernel/doc/src/rpc.xml11
-rw-r--r--lib/kernel/src/code_server.erl27
-rw-r--r--lib/kernel/src/inet.erl17
-rw-r--r--lib/kernel/src/inet_hosts.erl5
-rw-r--r--lib/kernel/src/inet_res.erl28
-rw-r--r--lib/kernel/src/os.erl8
-rw-r--r--lib/kernel/src/rpc.erl10
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl8
-rw-r--r--lib/kernel/test/inet_SUITE.erl33
-rw-r--r--lib/kernel/test/os_SUITE.erl4
-rw-r--r--lib/observer/src/cdv_port_cb.erl17
-rw-r--r--lib/observer/src/cdv_proc_cb.erl4
-rw-r--r--lib/observer/src/cdv_sched_cb.erl18
-rw-r--r--lib/observer/src/crashdump_viewer.erl188
-rw-r--r--lib/observer/src/crashdump_viewer.hrl16
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl4
-rw-r--r--lib/runtime_tools/src/system_information.erl40
-rw-r--r--lib/sasl/doc/src/release_handler.xml2
-rw-r--r--lib/sasl/doc/src/systools.xml6
-rw-r--r--lib/sasl/src/systools_make.erl20
-rw-r--r--lib/sasl/test/systools_SUITE.erl37
-rw-r--r--lib/snmp/doc/src/snmp_impl_example_agent.xml10
-rw-r--r--lib/ssh/doc/src/notes.xml158
-rw-r--r--lib/ssh/doc/src/ssh.xml12
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml7
-rw-r--r--lib/ssh/src/ssh.erl3
-rw-r--r--lib/ssh/src/ssh.hrl2
-rw-r--r--lib/ssh/src/ssh_acceptor_sup.erl5
-rw-r--r--lib/ssh/src/ssh_channel_sup.erl11
-rw-r--r--lib/ssh/src/ssh_cli.erl17
-rw-r--r--lib/ssh/src/ssh_connection.erl28
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl53
-rw-r--r--lib/ssh/src/ssh_connection_sup.erl5
-rw-r--r--lib/ssh/src/ssh_options.erl8
-rw-r--r--lib/ssh/src/ssh_subsystem_sup.erl8
-rw-r--r--lib/ssh/src/ssh_sup.erl15
-rw-r--r--lib/ssh/src/ssh_system_sup.erl9
-rw-r--r--lib/ssh/src/ssh_transport.erl5
-rw-r--r--lib/ssh/src/sshc_sup.erl5
-rw-r--r--lib/ssh/src/sshd_sup.erl6
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl44
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl1
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl95
-rw-r--r--lib/ssh/test/ssh_engine_SUITE.erl26
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl122
-rw-r--r--lib/ssh/test/ssh_sup_SUITE.erl113
-rw-r--r--lib/ssh/test/ssh_test_lib.erl6
-rw-r--r--lib/ssh/test/ssh_test_lib.hrl7
-rw-r--r--lib/ssh/vsn.mk3
-rw-r--r--lib/ssl/doc/src/notes.xml15
-rw-r--r--lib/ssl/src/ssl_cipher.erl11
-rw-r--r--lib/ssl/src/ssl_handshake.erl9
-rw-r--r--lib/ssl/test/ssl_engine_SUITE.erl39
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl122
-rw-r--r--lib/stdlib/doc/src/assert_hrl.xml2
-rw-r--r--lib/stdlib/doc/src/erl_tar.xml2
-rw-r--r--lib/stdlib/doc/src/ets.xml31
-rw-r--r--lib/stdlib/doc/src/timer.xml2
-rw-r--r--lib/stdlib/doc/src/uri_string.xml57
-rw-r--r--lib/stdlib/src/beam_lib.erl3
-rw-r--r--lib/stdlib/src/binary.erl28
-rw-r--r--lib/stdlib/src/epp.erl18
-rw-r--r--lib/stdlib/src/ets.erl11
-rw-r--r--lib/stdlib/src/string.erl8
-rw-r--r--lib/stdlib/src/uri_string.erl65
-rw-r--r--lib/stdlib/test/ets_SUITE.erl35
-rw-r--r--lib/stdlib/test/rand_SUITE.erl70
-rw-r--r--lib/stdlib/test/re_SUITE.erl15
-rw-r--r--lib/stdlib/test/string_SUITE.erl4
-rw-r--r--lib/stdlib/test/uri_string_SUITE.erl55
-rw-r--r--lib/tools/emacs/Makefile20
-rw-r--r--lib/tools/emacs/erlang-skels.el248
-rw-r--r--lib/tools/emacs/erlang.el127
-rw-r--r--lib/tools/emacs/test.erl.indented784
-rw-r--r--lib/tools/emacs/test.erl.orig784
-rw-r--r--lib/tools/test/emacs_SUITE.erl73
-rw-r--r--lib/tools/test/emacs_SUITE_data/comments25
-rw-r--r--lib/tools/test/emacs_SUITE_data/comprehensions47
-rw-r--r--lib/tools/test/emacs_SUITE_data/funcs174
-rw-r--r--lib/tools/test/emacs_SUITE_data/highlight78
-rw-r--r--lib/tools/test/emacs_SUITE_data/icr157
-rw-r--r--lib/tools/test/emacs_SUITE_data/macros31
-rw-r--r--lib/tools/test/emacs_SUITE_data/records35
-rw-r--r--lib/tools/test/emacs_SUITE_data/terms174
-rw-r--r--lib/tools/test/emacs_SUITE_data/try_catch166
-rw-r--r--lib/tools/test/emacs_SUITE_data/type_specs110
-rw-r--r--otp_versions.table3
-rw-r--r--system/doc/design_principles/release_structure.xml6
-rw-r--r--system/doc/efficiency_guide/advanced.xml2
-rw-r--r--system/doc/system_principles/create_target.xmlsrc8
246 files changed, 5172 insertions, 3694 deletions
diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam
index edfd5f4039..18f5dc6967 100644
--- a/bootstrap/lib/compiler/ebin/beam_block.beam
+++ b/bootstrap/lib/compiler/ebin/beam_block.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bs.beam b/bootstrap/lib/compiler/ebin/beam_bs.beam
index aa5bdc0a80..1361b0b08d 100644
--- a/bootstrap/lib/compiler/ebin/beam_bs.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bs.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bsm.beam b/bootstrap/lib/compiler/ebin/beam_bsm.beam
index 2897650a23..6bf3b576b2 100644
--- a/bootstrap/lib/compiler/ebin/beam_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam
index 853f1b929e..ab18a32fa8 100644
--- a/bootstrap/lib/compiler/ebin/beam_clean.beam
+++ b/bootstrap/lib/compiler/ebin/beam_clean.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dead.beam b/bootstrap/lib/compiler/ebin/beam_dead.beam
index da3c5cd96f..33ee4dea87 100644
--- a/bootstrap/lib/compiler/ebin/beam_dead.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dead.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dict.beam b/bootstrap/lib/compiler/ebin/beam_dict.beam
index 9c66f6d9f3..2e9c7d0f00 100644
--- a/bootstrap/lib/compiler/ebin/beam_dict.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dict.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_disasm.beam b/bootstrap/lib/compiler/ebin/beam_disasm.beam
index 3bf7fe79f3..5983f341f5 100644
--- a/bootstrap/lib/compiler/ebin/beam_disasm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_disasm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_except.beam b/bootstrap/lib/compiler/ebin/beam_except.beam
index 494c5855b9..6eed82587a 100644
--- a/bootstrap/lib/compiler/ebin/beam_except.beam
+++ b/bootstrap/lib/compiler/ebin/beam_except.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_flatten.beam b/bootstrap/lib/compiler/ebin/beam_flatten.beam
index 903a29b02d..8758e98a71 100644
--- a/bootstrap/lib/compiler/ebin/beam_flatten.beam
+++ b/bootstrap/lib/compiler/ebin/beam_flatten.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index 831d051efb..5551ba9f90 100644
--- a/bootstrap/lib/compiler/ebin/beam_jump.beam
+++ b/bootstrap/lib/compiler/ebin/beam_jump.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_receive.beam b/bootstrap/lib/compiler/ebin/beam_receive.beam
index 878501d1e9..8d16d7d39d 100644
--- a/bootstrap/lib/compiler/ebin/beam_receive.beam
+++ b/bootstrap/lib/compiler/ebin/beam_receive.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_reorder.beam b/bootstrap/lib/compiler/ebin/beam_reorder.beam
index 5f04b10e37..05562e19ea 100644
--- a/bootstrap/lib/compiler/ebin/beam_reorder.beam
+++ b/bootstrap/lib/compiler/ebin/beam_reorder.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_split.beam b/bootstrap/lib/compiler/ebin/beam_split.beam
index d52cbd3dff..8165aab002 100644
--- a/bootstrap/lib/compiler/ebin/beam_split.beam
+++ b/bootstrap/lib/compiler/ebin/beam_split.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_trim.beam b/bootstrap/lib/compiler/ebin/beam_trim.beam
index 9143b1615d..75fcff822e 100644
--- a/bootstrap/lib/compiler/ebin/beam_trim.beam
+++ b/bootstrap/lib/compiler/ebin/beam_trim.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_type.beam b/bootstrap/lib/compiler/ebin/beam_type.beam
index 9aaed00cea..0c8341a6c9 100644
--- a/bootstrap/lib/compiler/ebin/beam_type.beam
+++ b/bootstrap/lib/compiler/ebin/beam_type.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index 5d00f77e92..1e9c30d00a 100644
--- a/bootstrap/lib/compiler/ebin/beam_utils.beam
+++ b/bootstrap/lib/compiler/ebin/beam_utils.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam
index 0463b03d3b..25f8772409 100644
--- a/bootstrap/lib/compiler/ebin/beam_validator.beam
+++ b/bootstrap/lib/compiler/ebin/beam_validator.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index 5a10584227..61515da20a 100644
--- a/bootstrap/lib/compiler/ebin/cerl.beam
+++ b/bootstrap/lib/compiler/ebin/cerl.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_inline.beam b/bootstrap/lib/compiler/ebin/cerl_inline.beam
index 1891c09bd2..36888f6363 100644
--- a/bootstrap/lib/compiler/ebin/cerl_inline.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_inline.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index bcda9ba1ba..82d601c128 100644
--- a/bootstrap/lib/compiler/ebin/compile.beam
+++ b/bootstrap/lib/compiler/ebin/compile.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam
index 177de1981b..f3b420121a 100644
--- a/bootstrap/lib/compiler/ebin/core_lint.beam
+++ b/bootstrap/lib/compiler/ebin/core_lint.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_pp.beam b/bootstrap/lib/compiler/ebin/core_pp.beam
index 4ace82ae7d..59503a2933 100644
--- a/bootstrap/lib/compiler/ebin/core_pp.beam
+++ b/bootstrap/lib/compiler/ebin/core_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_scan.beam b/bootstrap/lib/compiler/ebin/core_scan.beam
index 949abb1981..bc0190fdb1 100644
--- a/bootstrap/lib/compiler/ebin/core_scan.beam
+++ b/bootstrap/lib/compiler/ebin/core_scan.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
index c4b2708363..62e56d92b0 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_fold.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_inline.beam b/bootstrap/lib/compiler/ebin/sys_core_inline.beam
index b0cb95430a..40f6ebf167 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_inline.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_inline.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam b/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
index 2dd8388c18..91a29c5266 100644
--- a/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
+++ b/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam
index 22c9416347..bf1bcaafd2 100644
--- a/bootstrap/lib/compiler/ebin/v3_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/v3_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index af6f7cde54..b4672b1315 100644
--- a/bootstrap/lib/compiler/ebin/v3_core.beam
+++ b/bootstrap/lib/compiler/ebin/v3_core.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam
index 1a09753a22..082bd6b75d 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
index 57f30e63d8..fb780c2a43 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam
index fe9f168f12..5e63aa4e8a 100644
--- a/bootstrap/lib/kernel/ebin/application_controller.beam
+++ b/bootstrap/lib/kernel/ebin/application_controller.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_master.beam b/bootstrap/lib/kernel/ebin/application_master.beam
index e7c9136475..4bafc43619 100644
--- a/bootstrap/lib/kernel/ebin/application_master.beam
+++ b/bootstrap/lib/kernel/ebin/application_master.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_starter.beam b/bootstrap/lib/kernel/ebin/application_starter.beam
index 0602f44d4b..ca7b77abcb 100644
--- a/bootstrap/lib/kernel/ebin/application_starter.beam
+++ b/bootstrap/lib/kernel/ebin/application_starter.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index fd064ca340..922c5e6308 100644
--- a/bootstrap/lib/kernel/ebin/code_server.beam
+++ b/bootstrap/lib/kernel/ebin/code_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log.beam b/bootstrap/lib/kernel/ebin/disk_log.beam
index ce9a424724..76264b975b 100644
--- a/bootstrap/lib/kernel/ebin/disk_log.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_1.beam b/bootstrap/lib/kernel/ebin/disk_log_1.beam
index 441db153b1..05c002ba67 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_1.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_1.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_ac.beam b/bootstrap/lib/kernel/ebin/dist_ac.beam
index c59c245f66..7419ba7c27 100644
--- a/bootstrap/lib/kernel/ebin/dist_ac.beam
+++ b/bootstrap/lib/kernel/ebin/dist_ac.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_util.beam b/bootstrap/lib/kernel/ebin/dist_util.beam
index c8defc4f52..3a4d230a7f 100644
--- a/bootstrap/lib/kernel/ebin/dist_util.beam
+++ b/bootstrap/lib/kernel/ebin/dist_util.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_boot_server.beam b/bootstrap/lib/kernel/ebin/erl_boot_server.beam
index 226052b9ac..d921ffabd9 100644
--- a/bootstrap/lib/kernel/ebin/erl_boot_server.beam
+++ b/bootstrap/lib/kernel/ebin/erl_boot_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file_io_server.beam b/bootstrap/lib/kernel/ebin/file_io_server.beam
index 00c19eb71c..d37ed85781 100644
--- a/bootstrap/lib/kernel/ebin/file_io_server.beam
+++ b/bootstrap/lib/kernel/ebin/file_io_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam
index b175e25a4f..8b057dff1a 100644
--- a/bootstrap/lib/kernel/ebin/global.beam
+++ b/bootstrap/lib/kernel/ebin/global.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global_group.beam b/bootstrap/lib/kernel/ebin/global_group.beam
index 9d119f0c96..b072f96e5d 100644
--- a/bootstrap/lib/kernel/ebin/global_group.beam
+++ b/bootstrap/lib/kernel/ebin/global_group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/group_history.beam b/bootstrap/lib/kernel/ebin/group_history.beam
index 4a071216da..97f98ebe2f 100644
--- a/bootstrap/lib/kernel/ebin/group_history.beam
+++ b/bootstrap/lib/kernel/ebin/group_history.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
index 3b14e184fc..16841cc417 100644
--- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
+++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_db.beam b/bootstrap/lib/kernel/ebin/inet_db.beam
index 1fb6322536..e86ce51920 100644
--- a/bootstrap/lib/kernel/ebin/inet_db.beam
+++ b/bootstrap/lib/kernel/ebin/inet_db.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_dns.beam b/bootstrap/lib/kernel/ebin/inet_dns.beam
index 312e52fd6f..c437ed85d7 100644
--- a/bootstrap/lib/kernel/ebin/inet_dns.beam
+++ b/bootstrap/lib/kernel/ebin/inet_dns.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
index 62414aa72e..ccd9f21cd4 100644
--- a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
+++ b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_res.beam b/bootstrap/lib/kernel/ebin/inet_res.beam
index a2d5924dd0..8c18d0d1ad 100644
--- a/bootstrap/lib/kernel/ebin/inet_res.beam
+++ b/bootstrap/lib/kernel/ebin/inet_res.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_kernel.beam b/bootstrap/lib/kernel/ebin/net_kernel.beam
index c3e4cc96eb..d9abceade5 100644
--- a/bootstrap/lib/kernel/ebin/net_kernel.beam
+++ b/bootstrap/lib/kernel/ebin/net_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/os.beam b/bootstrap/lib/kernel/ebin/os.beam
index bb190ba66a..f2a003d44f 100644
--- a/bootstrap/lib/kernel/ebin/os.beam
+++ b/bootstrap/lib/kernel/ebin/os.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user.beam b/bootstrap/lib/kernel/ebin/user.beam
index bb4037943a..e57d6896bd 100644
--- a/bootstrap/lib/kernel/ebin/user.beam
+++ b/bootstrap/lib/kernel/ebin/user.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
index b38ddd7288..a0f24625b4 100644
--- a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
+++ b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/array.beam b/bootstrap/lib/stdlib/ebin/array.beam
index 4ec44e7d34..1dd8846725 100644
--- a/bootstrap/lib/stdlib/ebin/array.beam
+++ b/bootstrap/lib/stdlib/ebin/array.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/binary.beam b/bootstrap/lib/stdlib/ebin/binary.beam
index 85fc484c32..e712319947 100644
--- a/bootstrap/lib/stdlib/ebin/binary.beam
+++ b/bootstrap/lib/stdlib/ebin/binary.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam
index 52ee0b4642..9384a4379f 100644
--- a/bootstrap/lib/stdlib/ebin/dets.beam
+++ b/bootstrap/lib/stdlib/ebin/dets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_server.beam b/bootstrap/lib/stdlib/ebin/dets_server.beam
index 540a1a2997..31f0f2701a 100644
--- a/bootstrap/lib/stdlib/ebin/dets_server.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_utils.beam b/bootstrap/lib/stdlib/ebin/dets_utils.beam
index 7fa23bb92b..265facaa57 100644
--- a/bootstrap/lib/stdlib/ebin/dets_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_v9.beam b/bootstrap/lib/stdlib/ebin/dets_v9.beam
index d295ebbeb7..f53f9fcf85 100644
--- a/bootstrap/lib/stdlib/ebin/dets_v9.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_v9.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/digraph.beam b/bootstrap/lib/stdlib/ebin/digraph.beam
index e411e04ccd..c51ecf54fc 100644
--- a/bootstrap/lib/stdlib/ebin/digraph.beam
+++ b/bootstrap/lib/stdlib/ebin/digraph.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index c37fb1be5e..58e29d0c3b 100644
--- a/bootstrap/lib/stdlib/ebin/epp.beam
+++ b/bootstrap/lib/stdlib/ebin/epp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_bits.beam b/bootstrap/lib/stdlib/ebin/erl_bits.beam
index 5b2a46aec1..b9b75ecaef 100644
--- a/bootstrap/lib/stdlib/ebin/erl_bits.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_bits.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam
index aa71f417dc..eb913d070d 100644
--- a/bootstrap/lib/stdlib/ebin/erl_eval.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_eval.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
index 790037359b..1869788f7b 100644
--- a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index bdcec0f3fb..b8350e8975 100644
--- a/bootstrap/lib/stdlib/ebin/erl_lint.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_parse.beam b/bootstrap/lib/stdlib/ebin/erl_parse.beam
index e0bbaa83d1..bfc5843912 100644
--- a/bootstrap/lib/stdlib/ebin/erl_parse.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_parse.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_pp.beam b/bootstrap/lib/stdlib/ebin/erl_pp.beam
index 2e0b4cd9ee..ca3d18a5db 100644
--- a/bootstrap/lib/stdlib/ebin/erl_pp.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_pp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_tar.beam b/bootstrap/lib/stdlib/ebin/erl_tar.beam
index 1f3e651d7b..ba7382f874 100644
--- a/bootstrap/lib/stdlib/ebin/erl_tar.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_tar.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/escript.beam b/bootstrap/lib/stdlib/ebin/escript.beam
index e068621f8c..5ae2752a6d 100644
--- a/bootstrap/lib/stdlib/ebin/escript.beam
+++ b/bootstrap/lib/stdlib/ebin/escript.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ets.beam b/bootstrap/lib/stdlib/ebin/ets.beam
index e676ce6b8f..f2a649e0d1 100644
--- a/bootstrap/lib/stdlib/ebin/ets.beam
+++ b/bootstrap/lib/stdlib/ebin/ets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/file_sorter.beam b/bootstrap/lib/stdlib/ebin/file_sorter.beam
index 504fc72130..8a9e501e1b 100644
--- a/bootstrap/lib/stdlib/ebin/file_sorter.beam
+++ b/bootstrap/lib/stdlib/ebin/file_sorter.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_event.beam b/bootstrap/lib/stdlib/ebin/gen_event.beam
index 6cdcd0fdb7..21806df360 100644
--- a/bootstrap/lib/stdlib/ebin/gen_event.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_event.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_statem.beam b/bootstrap/lib/stdlib/ebin/gen_statem.beam
index afabd64a65..c79b4a16f9 100644
--- a/bootstrap/lib/stdlib/ebin/gen_statem.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_statem.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io.beam b/bootstrap/lib/stdlib/ebin/io.beam
index dd5ea1a0a8..e1841e184c 100644
--- a/bootstrap/lib/stdlib/ebin/io.beam
+++ b/bootstrap/lib/stdlib/ebin/io.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
index bcf407eaa2..bf9e95e6be 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/lib.beam b/bootstrap/lib/stdlib/ebin/lib.beam
index 5281d1f49a..7d342a59a9 100644
--- a/bootstrap/lib/stdlib/ebin/lib.beam
+++ b/bootstrap/lib/stdlib/ebin/lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam
index e6507e4d1b..8228a076ba 100644
--- a/bootstrap/lib/stdlib/ebin/ms_transform.beam
+++ b/bootstrap/lib/stdlib/ebin/ms_transform.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam
index 14da40af23..6c2895ac79 100644
--- a/bootstrap/lib/stdlib/ebin/qlc.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc_pt.beam b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
index 2c4fbaa3e1..d40c197029 100644
--- a/bootstrap/lib/stdlib/ebin/qlc_pt.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sofs.beam b/bootstrap/lib/stdlib/ebin/sofs.beam
index 941c397b22..bfefd0c3a9 100644
--- a/bootstrap/lib/stdlib/ebin/sofs.beam
+++ b/bootstrap/lib/stdlib/ebin/sofs.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/string.beam b/bootstrap/lib/stdlib/ebin/string.beam
index 841cb8ff76..feeeec6a84 100644
--- a/bootstrap/lib/stdlib/ebin/string.beam
+++ b/bootstrap/lib/stdlib/ebin/string.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam
index 8446b160a4..61e8fca92c 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sys.beam b/bootstrap/lib/stdlib/ebin/sys.beam
index abf6451cb0..0cec9f4be8 100644
--- a/bootstrap/lib/stdlib/ebin/sys.beam
+++ b/bootstrap/lib/stdlib/ebin/sys.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/uri_string.beam b/bootstrap/lib/stdlib/ebin/uri_string.beam
index e618d6c360..95b7a2e6ae 100644
--- a/bootstrap/lib/stdlib/ebin/uri_string.beam
+++ b/bootstrap/lib/stdlib/ebin/uri_string.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam
index cafbc013bd..3e19c83090 100644
--- a/bootstrap/lib/stdlib/ebin/zip.beam
+++ b/bootstrap/lib/stdlib/ebin/zip.beam
Binary files differ
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 23f0c66429..f2f380ca4e 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -974,6 +974,10 @@ typedef struct {
<seealso marker="#enif_make_binary"><c>enif_make_binary</c></seealso>.
An allocated (and owned) <c>ErlNifBinary</c> can be kept between NIF
calls.</p>
+ <p>If you do not need to reallocate or keep the data alive across NIF
+ calls, consider using <seealso marker="#enif_make_new_binary">
+ <c>enif_make_new_binary</c></seealso> instead as it will allocate
+ small binaries on the process heap when possible.</p>
<p>Returns <c>true</c> on success, or <c>false</c> if allocation
fails.</p>
</desc>
@@ -3060,9 +3064,10 @@ if (retval &amp; ERL_NIF_SELECT_STOP_CALLED) {
</name>
<fsummary>Get the pid of the calling process.</fsummary>
<desc>
- <p>Initializes the pid variable <c>*pid</c> to represent the
- calling process.</p>
- <p>Returns <c>pid</c>.</p>
+ <p>Initializes the <seealso marker="#ErlNifPid"><c>ErlNifPid</c></seealso>
+ variable at <c>*pid</c> to represent the calling process.</p>
+ <p>Returns <c>pid</c> if successful, or NULL if <c>caller_env</c> is not
+ a <seealso marker="#ErlNifEnv">process-bound environment</seealso>.</p>
</desc>
</func>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 771897ba94..56eaa47af4 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -6753,17 +6753,20 @@ lists:map(
be included in the result. That is, all scheduler threads
that are expected to handle CPU bound work. If you also
want information about dirty I/O schedulers, use
- <seealso marker="#statistics_scheduler_wall_time_all"><c>statistics(scheduler_wall_time_all)</c></seealso>
+ <seealso marker="#statistics_scheduler_wall_time_all">
+ <c>statistics(scheduler_wall_time_all)</c></seealso>
instead.</p>
<p>Normal schedulers will have scheduler identifiers in
the range <c>1 =&lt; <anno>SchedulerId</anno> =&lt;
- </c><seealso marker="#system_info_schedulers"><c>erlang:system_info(schedulers)</c></seealso>.
+ </c><seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.
Dirty CPU schedulers will have scheduler identifiers in
the range <c>erlang:system_info(schedulers) &lt;
<anno>SchedulerId</anno> =&lt; erlang:system_info(schedulers)
+
- </c><seealso marker="#system_info_dirty_cpu_schedulers"><c>erlang:system_info(dirty_cpu_schedulers)</c></seealso>.
+ </c><seealso marker="#system_info_dirty_cpu_schedulers">
+ <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso>.
</p>
<note><p>The different types of schedulers handle
specific types of jobs. Every job is assigned to a specific
@@ -6839,13 +6842,16 @@ ok
schedulers.</p>
<p>Dirty IO schedulers will have scheduler identifiers in
the range
- <seealso marker="#system_info_schedulers"><c>erlang:system_info(schedulers)</c></seealso><c>
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso><c>
+
- </c><seealso marker="#system_info_dirty_cpu_schedulers"><c>erlang:system_info(dirty_cpu_schedulers)</c></seealso><c> &lt;
+ </c><seealso marker="#system_info_dirty_cpu_schedulers">
+ <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso><c> &lt;
<anno>SchedulerId</anno> =&lt; erlang:system_info(schedulers)
+ erlang:system_info(dirty_cpu_schedulers)
+
- </c><seealso marker="#system_info_dirty_io_schedulers"><c>erlang:system_info(dirty_io_schedulers)</c></seealso>.</p>
+ </c><seealso marker="#system_info_dirty_io_schedulers">
+ <c>erlang:system_info(dirty_io_schedulers)</c></seealso>.</p>
<note><p>Note that work executing on dirty I/O schedulers
are expected to mainly wait for I/O. That is, when you
get high scheduler utilization on dirty I/O schedulers,
@@ -7484,11 +7490,144 @@ ok
</func>
<func>
- <name name="system_info" arity="1" clause_i="1"/>
- <name name="system_info" arity="1" clause_i="2"/>
- <name name="system_info" arity="1" clause_i="3"/>
- <name name="system_info" arity="1" clause_i="4"/>
- <name name="system_info" arity="1" clause_i="5"/>
+ <name name="system_info" arity="1" clause_i="75"/>
+ <fsummary>System info overview.</fsummary>
+ <desc>
+ <p>Returns information about the current system.
+ The documentation of this function is broken into the following
+ sections in order to make it easier to navigate.</p>
+ <taglist>
+ <tag><seealso marker="#system_info_allocator">
+ <c>Memory Allocation</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_allocated_areas"><c>allocated_areas</c></seealso>,
+ <seealso marker="#system_info_allocator"><c>allocator</c></seealso>,
+ <seealso marker="#system_info_alloc_util_allocators"><c>alloc_util_allocators</c></seealso>,
+ <seealso marker="#system_info_allocator_sizes"><c>allocator_sizes</c></seealso>,
+ <seealso marker="#system_info_elib_malloc"><c>elib_malloc</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_cpu_topology">
+ <c>CPU Topology</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_cpu_topology"><c>cpu_topology</c></seealso>,
+ <seealso marker="#system_info_logical_processors"><c>logical_processors</c></seealso>,
+ <seealso marker="#system_info_update_cpu_info"><c>update_cpu_info</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_process">
+ <c>Process Information</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_fullsweep_after"><c>fullsweep_after</c></seealso>,
+ <seealso marker="#system_info_garbage_collection"><c>garbage_collection</c></seealso>,
+ <seealso marker="#system_info_heap_sizes"><c>heap_sizes</c></seealso>,
+ <seealso marker="#system_info_heap_type"><c>heap_type</c></seealso>,
+ <seealso marker="#system_info_max_heap_size"><c>max_heap_size</c></seealso>,
+ <seealso marker="#system_info_message_queue_data"><c>message_queue_data</c></seealso>,
+ <seealso marker="#system_info_min_heap_size"><c>min_heap_size</c></seealso>,
+ <seealso marker="#system_info_min_bin_vheap_size"><c>min_bin_vheap_size</c></seealso>,
+ <seealso marker="#system_info_procs"><c>procs</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_limits">
+ <c>System Limits</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_atom_count"><c>atom_count</c></seealso>,
+ <seealso marker="#system_info_atom_limit"><c>atom_limit</c></seealso>,
+ <seealso marker="#system_info_ets_limit"><c>ets_limit</c></seealso>,
+ <seealso marker="#system_info_port_count"><c>port_count</c></seealso>,
+ <seealso marker="#system_info_port_limit"><c>port_limit</c></seealso>,
+ <seealso marker="#system_info_process_count"><c>process_count</c></seealso>,
+ <seealso marker="#system_info_process_limit"><c>process_limit</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_time">
+ <c>System Time</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_end_time"><c>end_time</c></seealso>,
+ <seealso marker="#system_info_os_monotonic_time_source"><c>os_monotonic_time_source</c></seealso>,
+ <seealso marker="#system_info_os_system_time_source"><c>os_system_time_source</c></seealso>,
+ <seealso marker="#system_info_start_time"><c>start_time</c></seealso>,
+ <seealso marker="#system_info_time_correction"><c>time_correction</c></seealso>,
+ <seealso marker="#system_info_time_offset"><c>time_offset</c></seealso>,
+ <seealso marker="#system_info_time_warp_mode"><c>time_warp_mode</c></seealso>,
+ <seealso marker="#system_info_tolerant_timeofday"><c>tolerant_timeofday</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_scheduler">
+ <c>Scheduler Information</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_dirty_cpu_schedulers"><c>dirty_cpu_schedulers</c></seealso>,
+ <seealso marker="#system_info_dirty_cpu_schedulers_online"><c>dirty_cpu_schedulers_online</c></seealso>,
+ <seealso marker="#system_info_dirty_io_schedulers"><c>dirty_io_schedulers</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling"><c>multi_scheduling</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling_blockers"><c>multi_scheduling_blockers</c></seealso>,
+ <seealso marker="#system_info_normal_multi_scheduling_blockers"><c>normal_multi_scheduling_blockers</c></seealso>,
+ <seealso marker="#system_info_scheduler_bind_type"><c>scheduler_bind_type</c></seealso>,
+ <seealso marker="#system_info_scheduler_bindings"><c>scheduler_bindings</c></seealso>,
+ <seealso marker="#system_info_scheduler_id"><c>scheduler_id</c></seealso>,
+ <seealso marker="#system_info_schedulers"><c>schedulers</c></seealso>,
+ <seealso marker="#system_info_smp_support"><c>smp_support</c></seealso>,
+ <seealso marker="#system_info_threads"><c>threads</c></seealso>,
+ <seealso marker="#system_info_thread_pool_size"><c>thread_pool_size</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_dist">
+ <c>Distribution Information</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_creation"><c>creation</c></seealso>,
+ <seealso marker="#system_info_delayed_node_table_gc"><c>delayed_node_table_gc</c></seealso>,
+ <seealso marker="#system_info_dist"><c>dist</c></seealso>,
+ <seealso marker="#system_info_dist_buf_busy_limit"><c>dist_buf_busy_limit</c></seealso>,
+ <seealso marker="#system_info_dist_ctrl"><c>dist_ctrl</c></seealso>
+ </p>
+ </item>
+ <tag><seealso marker="#system_info_misc">
+ <c>System Information</c></seealso></tag>
+ <item>
+ <p>
+ <seealso marker="#system_info_build_type"><c>build_type</c></seealso>,
+ <seealso marker="#system_info_c_compiler_used"><c>c_compiler_used</c></seealso>,
+ <seealso marker="#system_info_check_io"><c>check_io</c></seealso>,
+ <seealso marker="#system_info_compat_rel"><c>compat_rel</c></seealso>,
+ <seealso marker="#system_info_debug_compiled"><c>debug_compiled</c></seealso>,
+ <seealso marker="#system_info_driver_version"><c>driver_version</c></seealso>,
+ <seealso marker="#system_info_dynamic_trace"><c>dynamic_trace</c></seealso>,
+ <seealso marker="#system_info_dynamic_trace_probes"><c>dynamic_trace_probes</c></seealso>,
+ <seealso marker="#system_info_info"><c>info</c></seealso>,
+ <seealso marker="#system_info_kernel_poll"><c>kernel_poll</c></seealso>,
+ <seealso marker="#system_info_loaded"><c>loaded</c></seealso>,
+ <seealso marker="#system_info_machine"><c>machine</c></seealso>,
+ <seealso marker="#system_info_modified_timing_level"><c>modified_timing_level</c></seealso>,
+ <seealso marker="#system_info_nif_version"><c>nif_version</c></seealso>,
+ <seealso marker="#system_info_otp_release"><c>otp_release</c></seealso>,
+ <seealso marker="#system_info_port_parallelism"><c>port_parallelism</c></seealso>,
+ <seealso marker="#system_info_system_version"><c>system_version</c></seealso>,
+ <seealso marker="#system_info_system_architecture"><c>system_architecture</c></seealso>,
+ <seealso marker="#system_info_trace_control_word"><c>trace_control_word</c></seealso>,
+ <seealso marker="#system_info_version"><c>version</c></seealso>,
+ <seealso marker="#system_info_wordsize"><c>wordsize</c></seealso>
+ </p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_info" arity="1" clause_i="1"
+ anchor="system_info_allocator"/> <!-- allocated_areas -->
+ <name name="system_info" arity="1" clause_i="2"/> <!-- allocator -->
+ <name name="system_info" arity="1" clause_i="3"/> <!-- {allocator, _} -->
+ <name name="system_info" arity="1" clause_i="4"/> <!-- alloc_util_allocators -->
+ <name name="system_info" arity="1" clause_i="5"/> <!-- {allocator_sizes, _} -->
+ <name name="system_info" arity="1" clause_i="27"/> <!-- elib_malloc -->
<fsummary>Information about the system allocators.</fsummary>
<type variable="Allocator" name_i="2"/>
<type variable="Version" name_i="2"/>
@@ -7497,12 +7636,13 @@ ok
<type variable="Alloc" name_i="3"/>
<desc>
<marker id="system_info_allocator_tags"></marker>
- <p>Returns various information about the allocators of the
- current system (emulator) as specified by
+ <p>Returns various information about the memory allocators
+ of the current system (emulator) as specified by
<c><anno>Item</anno></c>:</p>
<marker id="system_info_allocated_areas"></marker>
<taglist>
- <tag><c>allocated_areas</c></tag>
+ <tag><marker id="system_info_allocated_areas"/>
+ <c>allocated_areas</c></tag>
<item>
<p>Returns a list of tuples with information about
miscellaneous allocated memory areas.</p>
@@ -7524,9 +7664,9 @@ ok
<seealso marker="#memory/0">
<c>erlang:memory/0,1</c></seealso>.</p>
</item>
- <tag><c>allocator</c></tag>
+ <tag><marker id="system_info_allocator"/>
+ <c>allocator</c></tag>
<item>
- <marker id="system_info_allocator"></marker>
<p>Returns <c>{<anno>Allocator</anno>, <anno>Version</anno>,
<anno>Features</anno>, <anno>Settings</anno></c>, where:</p>
<list type="bulleted">
@@ -7559,19 +7699,9 @@ ok
<seealso marker="erts:erts_alloc#flags">
<c>erts_alloc(3)</c></seealso>.</p>
</item>
- <tag><c>alloc_util_allocators</c></tag>
- <item>
- <marker id="system_info_alloc_util_allocators"></marker>
- <p>Returns a list of the names of all allocators using
- the ERTS internal <c>alloc_util</c> framework
- as atoms. For more information, see section
- <seealso marker="erts:erts_alloc#alloc_util">The
- alloc_util framework</seealso>
- in <c>erts_alloc(3)</c>.</p>
- </item>
- <tag><c>{allocator, <anno>Alloc</anno>}</c></tag>
+ <tag><marker id="system_info_allocator_tuple"></marker>
+ <c>{allocator, <anno>Alloc</anno>}</c></tag>
<item>
- <marker id="system_info_allocator_tuple"></marker>
<p>Returns information about the specified allocator.
As from ERTS 5.6.1, the return value is a list
of <c>{instance, InstanceNo, InstanceInfo}</c> tuples,
@@ -7616,9 +7746,19 @@ ok
values. The first value is the memory pool size and
the second value is the used memory size.</p>
</item>
- <tag><c>{allocator_sizes, <anno>Alloc</anno>}</c></tag>
+ <tag><marker id="system_info_alloc_util_allocators"/>
+ <c>alloc_util_allocators</c></tag>
+ <item>
+ <p>Returns a list of the names of all allocators using
+ the ERTS internal <c>alloc_util</c> framework
+ as atoms. For more information, see section
+ <seealso marker="erts:erts_alloc#alloc_util">The
+ alloc_util framework</seealso>
+ in <c>erts_alloc(3)</c>.</p>
+ </item>
+ <tag><marker id="system_info_allocator_sizes"/>
+ <c>{allocator_sizes, <anno>Alloc</anno>}</c></tag>
<item>
- <marker id="system_info_allocator_sizes"></marker>
<p>Returns various size information for the specified
allocator. The information returned is a subset of the
information returned by
@@ -7626,14 +7766,23 @@ ok
<c>erlang:system_info({allocator,
<anno>Alloc</anno>})</c></seealso>.</p>
</item>
+ <tag><marker id="system_info_elib_malloc"/>
+ <c>elib_malloc</c></tag>
+ <item>
+ <p>This option will be removed in a future release.
+ The return value will always be <c>false</c>, as the
+ <c>elib_malloc</c> allocator has been removed.</p>
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="system_info" arity="1" clause_i="12"
- anchor="system_info_cpu_topology"/>
- <name name="system_info" arity="1" clause_i="13"/>
+ anchor="system_info_cpu_topology"/> <!-- cpu_topology -->
+ <name name="system_info" arity="1" clause_i="13"/> <!-- {cpu_topology, _} -->
+ <name name="system_info" arity="1" clause_i="37"/> <!-- logical_processors -->
+ <name name="system_info" arity="1" clause_i="72"/> <!-- update_cpu_info -->
<fsummary>Information about the CPU topology of the system.</fsummary>
<type name="cpu_topology"/>
<type name="level_entry"/>
@@ -7664,7 +7813,8 @@ ok
the current system (emulator) as specified by
<c><anno>Item</anno></c>:</p>
<taglist>
- <tag><c>cpu_topology</c></tag>
+ <tag><marker id="system_info_cpu_topology"/>
+ <c>cpu_topology</c></tag>
<item>
<p>Returns the <c><anno>CpuTopology</anno></c> currently used by
the emulator. The CPU topology is used when binding schedulers
@@ -7727,31 +7877,89 @@ ok
<seealso marker="#system_info_cpu_topology">
<c>cpu_topology</c></seealso>.</p>
</item>
+ <tag><marker id="system_info_logical_processors"/>
+ <c>logical_processors</c></tag>
+ <item>
+ <p>Returns the detected number of logical processors configured
+ in the system. The return value is either an integer, or
+ the atom <c>unknown</c> if the emulator cannot
+ detect the configured logical processors.</p>
+ </item>
+ <tag><marker id="system_info_logical_processors_available"/>
+ <c>logical_processors_available</c></tag>
+ <item>
+ <p>Returns the detected number of logical processors available
+ to the Erlang runtime system. The return value is either an
+ integer, or the atom <c>unknown</c> if the emulator
+ cannot detect the available logical processors. The number
+ of available logical processors is less than or equal to
+ the number of <seealso marker="#system_info_logical_processors_online">
+ logical processors online</seealso>.</p>
+ </item>
+ <tag><marker id="system_info_logical_processors_online"/>
+ <c>logical_processors_online</c></tag>
+ <item>
+ <p>Returns the detected number of logical processors online on
+ the system. The return value is either an integer,
+ or the atom <c>unknown</c> if the emulator cannot
+ detect logical processors online. The number of logical
+ processors online is less than or equal to the number of
+ <seealso marker="#system_info_logical_processors">logical processors
+ configured</seealso>.</p>
+ </item>
+ <tag><marker id="system_info_update_cpu_info"/>
+ <c>update_cpu_info</c></tag>
+ <item>
+ <p>The runtime system rereads the CPU information available
+ and updates its internally stored information about the
+ <seealso marker="#system_info_cpu_topology_detected">detected
+ CPU topology</seealso> and the number of logical processors
+ <seealso marker="#system_info_logical_processors">configured</seealso>,
+ <seealso marker="#system_info_logical_processors_online">online</seealso>,
+ and <seealso marker="#system_info_logical_processors_available">
+ available</seealso>.</p>
+ <p>If the CPU information has changed since the last time
+ it was read, the atom <c>changed</c> is returned, otherwise
+ the atom <c>unchanged</c>. If the CPU information has changed,
+ you probably want to
+ <seealso marker="#system_flag_schedulers_online">adjust the
+ number of schedulers online</seealso>. You typically want
+ to have as many schedulers online as
+ <seealso marker="#system_info_logical_processors_available">logical
+ processors available</seealso>.</p>
+ </item>
</taglist>
</desc>
</func>
<func>
- <name name="system_info" arity="1" clause_i="29"/>
- <name name="system_info" arity="1" clause_i="30"/>
- <name name="system_info" arity="1" clause_i="38"/>
- <name name="system_info" arity="1" clause_i="39"/>
- <name name="system_info" arity="1" clause_i="40"/>
- <name name="system_info" arity="1" clause_i="41"/>
+ <name name="system_info" arity="1" clause_i="30"
+ anchor="system_info_process"/> <!-- fullsweep_after -->
+ <name name="system_info" arity="1" clause_i="31"/> <!-- garbage_collection -->
+ <name name="system_info" arity="1" clause_i="32"/> <!-- heap_sizes -->
+ <name name="system_info" arity="1" clause_i="33"/> <!-- heap_type -->
+ <name name="system_info" arity="1" clause_i="39"/> <!-- max_heap_size -->
+ <name name="system_info" arity="1" clause_i="40"/> <!-- message_queue_data -->
+ <name name="system_info" arity="1" clause_i="41"/> <!-- min_heap_size -->
+ <name name="system_info" arity="1" clause_i="42"/> <!-- min_bin_vheap_size -->
+ <name name="system_info" arity="1" clause_i="56"/> <!-- procs -->
<fsummary>Information about the default process heap settings.</fsummary>
<type name="message_queue_data"/>
<type name="max_heap_size"/>
<desc>
+ <marker id="system_info_process_tags"/>
<p>Returns information about the default process heap settings:</p>
<taglist>
- <tag><c>fullsweep_after</c></tag>
+ <tag><marker id="system_info_fullsweep_after"/>
+ <c>fullsweep_after</c></tag>
<item>
<p>Returns <c>{fullsweep_after, integer() >= 0}</c>, which is
the <c>fullsweep_after</c> garbage collection setting used
by default. For more information, see
<c>garbage_collection</c> described below.</p>
</item>
- <tag><c>garbage_collection</c></tag>
+ <tag><marker id="system_info_garbage_collection"/>
+ <c>garbage_collection</c></tag>
<item>
<p>Returns a list describing the default garbage collection
settings. A process spawned on the local node by a
@@ -7764,7 +7972,30 @@ ok
can spawn a process that does not use the default
settings.</p>
</item>
- <tag><c>max_heap_size</c></tag>
+ <tag><marker id="system_info_heap_sizes"/>
+ <c>heap_sizes</c></tag>
+ <item>
+ <p>Returns a list of integers representing valid heap sizes
+ in words. All Erlang heaps are sized from sizes in this
+ list.</p>
+ </item>
+ <tag><marker id="system_info_heap_type"/>
+ <c>heap_type</c></tag>
+ <item>
+ <p>Returns the heap type used by the current emulator. One
+ heap type exists:</p>
+ <taglist>
+ <tag><c>private</c></tag>
+ <item>
+ Each process has a heap reserved for its use and no
+ references between heaps of different processes are
+ allowed. Messages passed between processes are copied
+ between heaps.
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_max_heap_size"/>
+ <c>max_heap_size</c></tag>
<item>
<p>Returns <c>{max_heap_size, <anno>MaxHeapSize</anno>}</c>,
where <c><anno>MaxHeapSize</anno></c> is the current
@@ -7792,173 +8023,364 @@ ok
<seealso marker="#process_flag_message_queue_data">
<c>process_flag(message_queue_data, MQD)</c></seealso>.</p>
</item>
- <tag><c>min_heap_size</c></tag>
+ <tag><marker id="system_info_min_heap_size"/>
+ <c>min_heap_size</c></tag>
<item>
<p>Returns <c>{min_heap_size, <anno>MinHeapSize</anno>}</c>,
where <c><anno>MinHeapSize</anno></c> is the current
system-wide minimum heap size for spawned processes.</p>
</item>
- <tag><c>min_bin_vheap_size</c></tag>
+ <tag><marker id="system_info_min_bin_vheap_size"/>
+ <c>min_bin_vheap_size</c></tag>
<item>
<p>Returns <c>{min_bin_vheap_size,
<anno>MinBinVHeapSize</anno>}</c>, where
<c><anno>MinBinVHeapSize</anno></c> is the current system-wide
minimum binary virtual heap size for spawned processes.</p>
</item>
+ <tag><marker id="system_info_procs"/>
+ <c>procs</c></tag>
+ <item>
+ <p>Returns a binary containing a string of process and port
+ information formatted as in Erlang crash dumps. For more
+ information, see section <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
+ in the User's Guide.</p>
+ </item>
</taglist>
</desc>
</func>
<func>
- <name name="system_info" arity="1" clause_i="6"/>
- <name name="system_info" arity="1" clause_i="7"/>
- <name name="system_info" arity="1" clause_i="8"/>
- <name name="system_info" arity="1" clause_i="9"/>
- <name name="system_info" arity="1" clause_i="10"/>
- <name name="system_info" arity="1" clause_i="11"/>
- <name name="system_info" arity="1" clause_i="14"/>
- <name name="system_info" arity="1" clause_i="15"/>
- <name name="system_info" arity="1" clause_i="16"/>
- <name name="system_info" arity="1" clause_i="17"/>
- <name name="system_info" arity="1" clause_i="18"/>
- <name name="system_info" arity="1" clause_i="19"/>
- <name name="system_info" arity="1" clause_i="20"/>
- <name name="system_info" arity="1" clause_i="21"/>
- <name name="system_info" arity="1" clause_i="22"/>
- <name name="system_info" arity="1" clause_i="23"/>
- <name name="system_info" arity="1" clause_i="24"/>
- <name name="system_info" arity="1" clause_i="25"/>
- <name name="system_info" arity="1" clause_i="26"/>
- <name name="system_info" arity="1" clause_i="27"/>
- <name name="system_info" arity="1" clause_i="28"/>
- <name name="system_info" arity="1" clause_i="31"/>
- <name name="system_info" arity="1" clause_i="32"/>
- <name name="system_info" arity="1" clause_i="33"/>
- <name name="system_info" arity="1" clause_i="34"/>
- <name name="system_info" arity="1" clause_i="35"/>
- <name name="system_info" arity="1" clause_i="36"/>
- <name name="system_info" arity="1" clause_i="37"/>
- <name name="system_info" arity="1" clause_i="42"/>
- <name name="system_info" arity="1" clause_i="43"/>
- <name name="system_info" arity="1" clause_i="44"/>
- <name name="system_info" arity="1" clause_i="45"/>
- <name name="system_info" arity="1" clause_i="46"/>
- <name name="system_info" arity="1" clause_i="47"/>
- <name name="system_info" arity="1" clause_i="48"/>
- <name name="system_info" arity="1" clause_i="49"/>
- <name name="system_info" arity="1" clause_i="50"/>
- <name name="system_info" arity="1" clause_i="51"/>
- <name name="system_info" arity="1" clause_i="52"/>
- <name name="system_info" arity="1" clause_i="53"/>
- <name name="system_info" arity="1" clause_i="54"/>
- <name name="system_info" arity="1" clause_i="55"/>
- <name name="system_info" arity="1" clause_i="56"/>
- <name name="system_info" arity="1" clause_i="57"/>
- <name name="system_info" arity="1" clause_i="58"/>
- <name name="system_info" arity="1" clause_i="59"/>
- <name name="system_info" arity="1" clause_i="60"/>
- <name name="system_info" arity="1" clause_i="61"/>
- <name name="system_info" arity="1" clause_i="62"/>
- <name name="system_info" arity="1" clause_i="63"/>
- <name name="system_info" arity="1" clause_i="64"/>
- <name name="system_info" arity="1" clause_i="65"/>
- <name name="system_info" arity="1" clause_i="66"/>
- <name name="system_info" arity="1" clause_i="67"/>
- <name name="system_info" arity="1" clause_i="68"/>
- <name name="system_info" arity="1" clause_i="69"/>
- <name name="system_info" arity="1" clause_i="70"/>
- <name name="system_info" arity="1" clause_i="71"/>
- <fsummary>Information about the system.</fsummary>
+ <name name="system_info" arity="1" clause_i="6"
+ anchor="system_info_limits"/> <!-- atom_count -->
+ <name name="system_info" arity="1" clause_i="7"/> <!-- atom_limit -->
+ <name name="system_info" arity="1" clause_i="29"/> <!-- ets_limit -->
+ <name name="system_info" arity="1" clause_i="52"/> <!-- port_count -->
+ <name name="system_info" arity="1" clause_i="53"/> <!-- port_limit -->
+ <name name="system_info" arity="1" clause_i="54"/> <!-- process_count -->
+ <name name="system_info" arity="1" clause_i="55"/> <!-- process_limit -->
+ <fsummary>Information about various system limits.</fsummary>
<desc>
- <p>Returns various information about the current system
- (emulator) as specified by <c><anno>Item</anno></c>:</p>
+ <marker id="system_info_limits"/>
+ <p>Returns information about the current system
+ (emulator) limits as specified by <c><anno>Item</anno></c>:</p>
<taglist>
- <tag><c>atom_count</c></tag>
+ <tag><marker id="system_info_atom_count"/>
+ <c>atom_count</c></tag>
<item>
- <marker id="system_info_atom_count"></marker>
<p>Returns the number of atoms currently existing at the
- local node. The value is given as an integer.</p>
+ local node. The value is given as an integer.</p>
</item>
- <tag><c>atom_limit</c></tag>
+ <tag><marker id="system_info_atom_limit"/>
+ <c>atom_limit</c></tag>
<item>
- <marker id="system_info_atom_limit"></marker>
<p>Returns the maximum number of atoms allowed.
- This limit can be increased at startup by passing
- command-line flag
- <seealso marker="erts:erl#+t"><c>+t</c></seealso> to
- <c>erl(1)</c>.
+ This limit can be increased at startup by passing
+ command-line flag
+ <seealso marker="erts:erl#+t"><c>+t</c></seealso> to
+ <c>erl(1)</c>.
</p>
</item>
- <tag><c>build_type</c></tag>
+ <tag><marker id="system_info_ets_limit"/>
+ <c>ets_limit</c></tag>
<item>
- <p>Returns an atom describing the build type of the runtime
- system. This is normally the atom <c>opt</c> for optimized.
- Other possible return values are <c>debug</c>, <c>purify</c>,
- <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>,
- <c>gprof</c>, and <c>lcnt</c>. Possible return values
- can be added or removed at any time without prior notice.</p>
+ <p>Returns the maximum number of ETS tables allowed. This
+ limit can be increased at startup by passing
+ command-line flag
+ <seealso marker="erts:erl#+e"><c>+e</c></seealso> to
+ <c>erl(1)</c> or by setting environment variable
+ <c>ERL_MAX_ETS_TABLES</c> before starting the Erlang
+ runtime system.</p>
</item>
- <tag><c>c_compiler_used</c></tag>
+ <tag><marker id="system_info_port_count"/><c>port_count</c></tag>
<item>
- <p>Returns a two-tuple describing the C compiler used when
- compiling the runtime system. The first element is an
- atom describing the name of the compiler, or <c>undefined</c>
- if unknown. The second element is a term describing the
- version of the compiler, or <c>undefined</c> if unknown.</p>
+ <p>Returns the number of ports currently existing at the
+ local node. The value is given as an integer. This is
+ the same value as returned by
+ <c>length(erlang:ports())</c>, but more efficient.</p>
</item>
- <tag><c>check_io</c></tag>
+ <tag><marker id="system_info_port_limit"/>
+ <c>port_limit</c></tag>
<item>
- <p>Returns a list containing miscellaneous information
- about the emulators internal I/O checking. Notice that
- the content of the returned list can vary between
- platforms and over time. It is only guaranteed
- that a list is returned.</p>
+ <p>Returns the maximum number of simultaneously existing
+ ports at the local node as an integer. This limit can be
+ configured at startup by using command-line flag
+ <seealso marker="erl#+Q"><c>+Q</c></seealso> in <c>erl(1)</c>.</p>
</item>
- <tag><c>compat_rel</c></tag>
+ <tag><marker id="system_info_process_count"/>
+ <c>process_count</c></tag>
<item>
- <p>Returns the compatibility mode of the local node as
- an integer. The integer returned represents the
- Erlang/OTP release that the current emulator has been
- set to be backward compatible with. The compatibility
- mode can be configured at startup by using command-line flag
- <seealso marker="erts:erl#compat_rel"><c>+R</c></seealso> in
- <c>erl(1)</c>.</p>
+ <p>Returns the number of processes currently existing at the
+ local node. The value is given as an integer. This is
+ the same value as returned by
+ <c>length(processes())</c>, but more efficient.</p>
</item>
- <tag><c>cpu_topology</c></tag>
+ <tag><marker id="system_info_process_limit"/>
+ <c>process_limit</c></tag>
<item>
- <p>See <seealso
- marker="#system_info_cpu_topology_tags">above</seealso>.</p>
+ <p>Returns the maximum number of simultaneously existing
+ processes at the local node. The value is given as an
+ integer. This limit can be configured at startup by using
+ command-line flag <seealso marker="erl#+P"><c>+P</c></seealso>
+ in <c>erl(1)</c>.</p>
</item>
- <tag><c>creation</c></tag>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_info" arity="1" clause_i="26"
+ anchor="system_info_time"/> <!-- end_time -->
+ <name name="system_info" arity="1" clause_i="49"/> <!-- os_monotonic_time_source -->
+ <name name="system_info" arity="1" clause_i="50"/> <!-- os_system_time_source -->
+ <name name="system_info" arity="1" clause_i="62"/> <!-- start_time -->
+ <name name="system_info" arity="1" clause_i="67"/> <!-- time_correction -->
+ <name name="system_info" arity="1" clause_i="68"/> <!-- time_offset -->
+ <name name="system_info" arity="1" clause_i="69"/> <!-- time_warp_mode -->
+ <name name="system_info" arity="1" clause_i="70"/> <!-- tolerant_timeofday -->
+ <fsummary>Information about system time.</fsummary>
+ <desc>
+ <marker id="system_info_time_tags"/>
+ <p>Returns information about the current system
+ (emulator) time as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <tag><marker id="system_info_end_time"/><c>end_time</c></tag>
<item>
- <p>Returns the creation of the local node as an integer.
- The creation is changed when a node is restarted. The
- creation of a node is stored in process identifiers, port
- identifiers, and references. This makes it (to some
- extent) possible to distinguish between identifiers from
- different incarnations of a node. The valid
- creations are integers in the range 1..3, but this will
- probably change in a future release. If the node is not
- alive, <c>0</c> is returned.</p>
+ <p>The last <seealso marker="#monotonic_time/0">Erlang monotonic
+ time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso> that
+ can be represented internally in the current Erlang runtime system
+ instance. The time between the
+ <seealso marker="#system_info_start_time">start time</seealso> and
+ the end time is at least a quarter of a millennium.</p>
</item>
- <tag><c>debug_compiled</c></tag>
+ <tag><marker id="system_info_os_monotonic_time_source"/>
+ <c>os_monotonic_time_source</c></tag>
<item>
- <p>Returns <c>true</c> if the emulator has been
- debug-compiled, otherwise <c>false</c>.</p>
+ <p>Returns a list containing information about the source of
+ <seealso marker="erts:time_correction#OS_Monotonic_Time">OS
+ monotonic time</seealso> that is used by the runtime system.</p>
+ <p>If <c>[]</c> is returned, no OS monotonic time is
+ available. The list contains two-tuples with <c>Key</c>s
+ as first element, and <c>Value</c>s as second element. The
+ order of these tuples is undefined. The following
+ tuples can be part of the list, but more tuples can be
+ introduced in the future:</p>
+ <taglist>
+ <tag><c>{function, Function}</c></tag>
+ <item><p><c>Function</c> is the name of the function
+ used. This tuple always exists if OS monotonic time is
+ available to the runtime system.</p>
+ </item>
+ <tag><c>{clock_id, ClockId}</c></tag>
+ <item><p>This tuple only exists if <c>Function</c>
+ can be used with different clocks. <c>ClockId</c>
+ corresponds to the clock identifier used when calling
+ <c>Function</c>.</p>
+ </item>
+ <tag><c>{resolution, OsMonotonicTimeResolution}</c></tag>
+ <item><p>Highest possible
+ <seealso marker="time_correction#Time_Resolution">
+ resolution</seealso>
+ of current OS monotonic time source as parts per
+ second. If no resolution information can be retrieved
+ from the OS, <c>OsMonotonicTimeResolution</c> is
+ set to the resolution of the time unit of
+ <c>Function</c>s return value. That is, the actual
+ resolution can be lower than
+ <c>OsMonotonicTimeResolution</c>. Notice that
+ the resolution does not say anything about the
+ <seealso marker="time_correction#Time_Accuracy">
+ accuracy</seealso> or whether the
+ <seealso marker="time_correction#Time_Precision">
+ precision</seealso> aligns with the resolution. You do,
+ however, know that the precision is not better than
+ <c>OsMonotonicTimeResolution</c>.</p>
+ </item>
+ <tag><c>{extended, Extended}</c></tag>
+ <item><p><c>Extended</c> equals <c>yes</c> if
+ the range of time values has been extended;
+ otherwise <c>Extended</c> equals <c>no</c>. The
+ range must be extended if <c>Function</c>
+ returns values that wrap fast. This typically
+ is the case when the return value is a 32-bit value.</p>
+ </item>
+ <tag><c>{parallel, Parallel}</c></tag>
+ <item><p><c>Parallel</c> equals <c>yes</c> if
+ <c>Function</c> is called in parallel from multiple
+ threads. If it is not called in parallel, because
+ calls must be serialized, <c>Parallel</c> equals
+ <c>no</c>.</p>
+ </item>
+ <tag><c>{time, OsMonotonicTime}</c></tag>
+ <item><p><c>OsMonotonicTime</c> equals current OS
+ monotonic time in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>.</p>
+ </item>
+ </taglist>
</item>
- <tag><c>delayed_node_table_gc</c></tag>
+ <tag><marker id="system_info_os_system_time_source"/>
+ <c>os_system_time_source</c></tag>
<item>
- <marker id="system_info_delayed_node_table_gc"></marker>
- <p>Returns the amount of time in seconds garbage collection
- of an entry in a node table is delayed. This limit can be set
- on startup by passing command-line flag
- <seealso marker="erts:erl#+zdntgc"><c>+zdntgc</c></seealso>
- to <c>erl(1)</c>. For more information, see the documentation of
- the command-line flag.</p>
+ <p>Returns a list containing information about the source of
+ <seealso marker="erts:time_correction#OS_System_Time">OS
+ system time</seealso> that is used by the runtime system.</p>
+ <p>The list contains two-tuples with <c>Key</c>s
+ as first element, and <c>Value</c>s as second element. The
+ order if these tuples is undefined. The following
+ tuples can be part of the list, but more tuples can be
+ introduced in the future:</p>
+ <taglist>
+ <tag><c>{function, Function}</c></tag>
+ <item><p><c>Function</c> is the name of the funcion used.</p>
+ </item>
+ <tag><c>{clock_id, ClockId}</c></tag>
+ <item><p>Exists only if <c>Function</c>
+ can be used with different clocks. <c>ClockId</c>
+ corresponds to the clock identifier used when calling
+ <c>Function</c>.</p>
+ </item>
+ <tag><c>{resolution, OsSystemTimeResolution}</c></tag>
+ <item><p>Highest possible
+ <seealso marker="time_correction#Time_Resolution">
+ resolution</seealso>
+ of current OS system time source as parts per
+ second. If no resolution information can be retrieved
+ from the OS, <c>OsSystemTimeResolution</c> is
+ set to the resolution of the time unit of
+ <c>Function</c>s return value. That is, the actual
+ resolution can be lower than
+ <c>OsSystemTimeResolution</c>. Notice that
+ the resolution does not say anything about the
+ <seealso marker="time_correction#Time_Accuracy">
+ accuracy</seealso> or whether the
+ <seealso marker="time_correction#Time_Precision">
+ precision</seealso> do align with the resolution. You do,
+ however, know that the precision is not better than
+ <c>OsSystemTimeResolution</c>.</p>
+ </item>
+ <tag><c>{parallel, Parallel}</c></tag>
+ <item><p><c>Parallel</c> equals <c>yes</c> if
+ <c>Function</c> is called in parallel from multiple
+ threads. If it is not called in parallel, because
+ calls needs to be serialized, <c>Parallel</c> equals
+ <c>no</c>.</p>
+ </item>
+ <tag><c>{time, OsSystemTime}</c></tag>
+ <item><p><c>OsSystemTime</c> equals current OS
+ system time in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_start_time"/><c>start_time</c></tag>
+ <item>
+ <p>The <seealso marker="#monotonic_time/0">Erlang monotonic
+ time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso> at the
+ time when current Erlang runtime system instance started.</p>
+ <p>See also <seealso marker="#system_info_end_time">
+ <c>erlang:system_info(end_time)</c></seealso>.</p>
+ </item>
+ <tag><marker id="system_info_time_correction"/>
+ <c>time_correction</c></tag>
+ <item>
+ <p>Returns a boolean value indicating whether
+ <seealso marker="time_correction#Time_Correction">
+ time correction</seealso> is enabled or not.</p>
+ </item>
+ <tag><marker id="system_info_time_offset"/>
+ <c>time_offset</c></tag>
+ <item>
+ <p>Returns the state of the time offset:</p>
+ <taglist>
+ <tag><c>preliminary</c></tag>
+ <item>
+ <p>The time offset is preliminary, and will be changed
+ and finalized later. The preliminary time offset
+ is used during the preliminary phase of the
+ <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso>.</p>
+ </item>
+ <tag><c>final</c></tag>
+ <item>
+ <p>The time offset is final. This either because
+ <seealso marker="time_correction#No_Time_Warp_Mode">
+ no time warp mode</seealso> is used, or because the time
+ offset have been finalized when
+ <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso> is used.</p>
+ </item>
+ <tag><c>volatile</c></tag>
+ <item>
+ <p>The time offset is volatile. That is, it can
+ change at any time. This is because
+ <seealso marker="time_correction#Multi_Time_Warp_Mode">
+ multi-time warp mode</seealso> is used.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_time_warp_mode"/>
+ <c>time_warp_mode</c></tag>
+ <item>
+ <p>Returns a value identifying the
+ <seealso marker="time_correction#Time_Warp_Modes">
+ time warp mode</seealso> that is used:</p>
+ <taglist>
+ <tag><c>no_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#No_Time_Warp_Mode">
+ no time warp mode</seealso> is used.
+ </item>
+ <tag><c>single_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso> is used.
+ </item>
+ <tag><c>multi_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#Multi_Time_Warp_Mode">
+ multi-time warp mode</seealso> is used.
+ </item>
+ </taglist>
</item>
- <tag><c>dirty_cpu_schedulers</c></tag>
+ <tag><marker id="system_info_tolerant_timeofday"/>
+ <c>tolerant_timeofday</c></tag>
+ <item>
+ <p>Returns whether a pre ERTS 7.0 backwards compatible
+ compensation for sudden changes of system time is <c>enabled</c>
+ or <c>disabled</c>. Such compensation is <c>enabled</c> when the
+ <seealso marker="#system_info_time_offset">time offset</seealso>
+ is <c>final</c>, and
+ <seealso marker="#system_info_time_correction">
+ time correction</seealso> is enabled.</p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_info" arity="1" clause_i="17"
+ anchor="system_info_scheduler"/> <!-- dirty_cpu_schedulers -->
+ <name name="system_info" arity="1" clause_i="18"/> <!-- dirty_cpu_schedulers_online -->
+ <name name="system_info" arity="1" clause_i="19"/> <!-- dirty_io_schedulers -->
+ <name name="system_info" arity="1" clause_i="44"/> <!-- multi_scheduling -->
+ <name name="system_info" arity="1" clause_i="45"/> <!-- multi_scheduling_blockers -->
+ <name name="system_info" arity="1" clause_i="47"/> <!-- normal_multi_scheduling_blockers -->
+ <name name="system_info" arity="1" clause_i="57"/> <!-- scheduler_bind_type -->
+ <name name="system_info" arity="1" clause_i="58"/> <!-- scheduler_bindings -->
+ <name name="system_info" arity="1" clause_i="59"/> <!-- scheduler_id -->
+ <name name="system_info" arity="1" clause_i="60"/> <!-- schedulers -->
+ <name name="system_info" arity="1" clause_i="61"/> <!-- smp_support -->
+ <name name="system_info" arity="1" clause_i="65"/> <!-- threads -->
+ <name name="system_info" arity="1" clause_i="66"/> <!-- thread_pool_size -->
+ <fsummary>Information about system schedulers.</fsummary>
+ <desc>
+ <marker id="system_info_scheduler_tags"/>
+ <p>Returns information about schedulers, scheduling and threads in the
+ current system as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <tag><marker id="system_info_dirty_cpu_schedulers"/>
+ <c>dirty_cpu_schedulers</c></tag>
<item>
- <marker id="system_info_dirty_cpu_schedulers"></marker>
<p>Returns the number of dirty CPU scheduler threads used by
the emulator. Dirty CPU schedulers execute CPU-bound
native functions, such as NIFs, linked-in driver code,
@@ -7989,9 +8411,9 @@ ok
<c>erlang:system_flag(schedulers_online,
SchedulersOnline)</c></seealso>.</p>
</item>
- <tag><c>dirty_cpu_schedulers_online</c></tag>
+ <tag><marker id="system_info_dirty_cpu_schedulers_online"/>
+ <c>dirty_cpu_schedulers_online</c></tag>
<item>
- <marker id="system_info_dirty_cpu_schedulers_online"></marker>
<p>Returns the number of dirty CPU schedulers online.
The return value satisfies
<c><![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]></c>,
@@ -8013,9 +8435,9 @@ ok
<c>erlang:system_flag(dirty_cpu_schedulers_online,
DirtyCPUSchedulersOnline)</c></seealso>.</p>
</item>
- <tag><c>dirty_io_schedulers</c></tag>
+ <tag><marker id="system_info_dirty_io_schedulers"/>
+ <c>dirty_io_schedulers</c></tag>
<item>
- <marker id="system_info_dirty_io_schedulers"></marker>
<p>Returns the number of dirty I/O schedulers as an integer.
Dirty I/O schedulers execute I/O-bound native functions,
such as NIFs and linked-in driver code, which cannot be
@@ -8032,179 +8454,9 @@ ok
<c>erlang:system_flag(dirty_cpu_schedulers_online,
DirtyCPUSchedulersOnline)</c></seealso>.</p>
</item>
- <tag><c>dist</c></tag>
- <item>
- <p>Returns a binary containing a string of distribution
- information formatted as in Erlang crash dumps. For more
- information, see section <seealso marker="erts:crash_dump">
- How to interpret the Erlang crash dumps</seealso>
- in the User's Guide.</p>
- </item>
- <tag><c>dist_buf_busy_limit</c></tag>
- <item>
- <marker id="system_info_dist_buf_busy_limit"></marker>
- <p>Returns the value of the distribution buffer busy limit
- in bytes. This limit can be set at startup by passing
- command-line flag
- <seealso marker="erts:erl#+zdbbl"><c>+zdbbl</c></seealso>
- to <c>erl(1)</c>.</p>
- </item>
- <tag><c>dist_ctrl</c></tag>
- <item>
- <p>Returns a list of tuples
- <c>{<anno>Node</anno>, <anno>ControllingEntity</anno>}</c>,
- one entry for each connected remote node.
- <c><anno>Node</anno></c> is the node name
- and <c><anno>ControllingEntity</anno></c> is the port or process
- identifier responsible for the communication to that node.
- More specifically, <c><anno>ControllingEntity</anno></c> for
- nodes connected through TCP/IP (the normal case) is the socket
- used in communication with the specific node.</p>
- </item>
- <tag><c>driver_version</c></tag>
- <item>
- <p>Returns a string containing the Erlang driver version
- used by the runtime system. It has the form
- <seealso marker="erts:erl_driver#version_management">
- "&lt;major ver&gt;.&lt;minor ver&gt;"</seealso>.</p>
- </item>
- <tag><c>dynamic_trace</c></tag>
- <item>
- <p>Returns an atom describing the dynamic trace framework
- compiled into the virtual machine. It can be
- <c>dtrace</c>, <c>systemtap</c>, or <c>none</c>. For a
- commercial or standard build, it is always <c>none</c>.
- The other return values indicate a custom configuration
- (for example, <c>./configure --with-dynamic-trace=dtrace</c>).
- For more information about dynamic tracing, see
- <seealso marker="runtime_tools:dyntrace">
- <c>dyntrace(3)</c></seealso> manual page and the
- <c>README.dtrace</c>/<c>README.systemtap</c> files in the
- Erlang source code top directory.</p>
- </item>
- <tag><c>dynamic_trace_probes</c></tag>
+ <tag><marker id="system_info_multi_scheduling"/>
+ <c>multi_scheduling</c></tag>
<item>
- <p>Returns a <c>boolean()</c> indicating if dynamic trace
- probes (<c>dtrace</c> or <c>systemtap</c>) are built into
- the emulator. This can only be <c>true</c> if the virtual
- machine was built for dynamic tracing (that is,
- <c>system_info(dynamic_trace)</c> returns
- <c>dtrace</c> or <c>systemtap</c>).</p>
- </item>
- <tag><marker id="system_info_end_time"/><c>end_time</c></tag>
- <item>
- <p>The last <seealso marker="#monotonic_time/0">Erlang monotonic
- time</seealso> in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso> that
- can be represented internally in the current Erlang runtime system
- instance. The time between the
- <seealso marker="#system_info_start_time">start time</seealso> and
- the end time is at least a quarter of a millennium.</p>
- </item>
- <tag><c>elib_malloc</c></tag>
- <item>
- <p>This option will be removed in a future release.
- The return value will always be <c>false</c>, as the
- <c>elib_malloc</c> allocator has been removed.</p>
- </item>
- <tag><c>ets_limit</c></tag>
- <item>
- <p>Returns the maximum number of ETS tables allowed. This
- limit can be increased at startup by passing
- command-line flag
- <seealso marker="erts:erl#+e"><c>+e</c></seealso> to
- <c>erl(1)</c> or by setting environment variable
- <c>ERL_MAX_ETS_TABLES</c> before starting the Erlang
- runtime system.</p>
- </item>
- <tag><c>heap_sizes</c></tag>
- <item>
- <p>Returns a list of integers representing valid heap sizes
- in words. All Erlang heaps are sized from sizes in this
- list.</p>
- </item>
- <tag><c>heap_type</c></tag>
- <item>
- <p>Returns the heap type used by the current emulator. One
- heap type exists:</p>
- <taglist>
- <tag><c>private</c></tag>
- <item>
- Each process has a heap reserved for its use and no
- references between heaps of different processes are
- allowed. Messages passed between processes are copied
- between heaps.
- </item>
- </taglist>
- </item>
- <tag><c>info</c></tag>
- <item>
- <p>Returns a binary containing a string of miscellaneous
- system information formatted as in Erlang crash dumps.
- For more information, see section
- <seealso marker="erts:crash_dump">
- How to interpret the Erlang crash dumps</seealso>
- in the User's Guide.</p>
- </item>
- <tag><c>kernel_poll</c></tag>
- <item>
- <p>Returns <c>true</c> if the emulator uses some kind of
- kernel-poll implementation, otherwise <c>false</c>.</p>
- </item>
- <tag><c>loaded</c></tag>
- <item>
- <p>Returns a binary containing a string of loaded module
- information formatted as in Erlang crash dumps. For more
- information, see section
- <seealso marker="erts:crash_dump">How to interpret the Erlang
- crash dumps</seealso> in the User's Guide.</p>
- </item>
- <tag><c>logical_processors</c></tag>
- <item>
- <marker id="logical_processors"></marker>
- <p>Returns the detected number of logical processors configured
- in the system. The return value is either an integer, or
- the atom <c>unknown</c> if the emulator cannot
- detect the configured logical processors.</p>
- </item>
- <tag><c>logical_processors_available</c></tag>
- <item>
- <marker id="logical_processors_available"></marker>
- <p>Returns the detected number of logical processors available
- to the Erlang runtime system. The return value is either an
- integer, or the atom <c>unknown</c> if the emulator
- cannot detect the available logical processors. The number
- of available logical processors is less than or equal to
- the number of <seealso marker="#logical_processors_online">
- logical processors online</seealso>.</p>
- </item>
- <tag><c>logical_processors_online</c></tag>
- <item>
- <marker id="logical_processors_online"></marker>
- <p>Returns the detected number of logical processors online on
- the system. The return value is either an integer,
- or the atom <c>unknown</c> if the emulator cannot
- detect logical processors online. The number of logical
- processors online is less than or equal to the number of
- <seealso marker="#logical_processors">logical processors
- configured</seealso>.</p>
- </item>
- <tag><c>machine</c></tag>
- <item>
- <p>Returns a string containing the Erlang machine name.</p>
- </item>
- <tag><c>modified_timing_level</c></tag>
- <item>
- <p>Returns the modified timing-level (an integer) if
- modified timing is enabled, otherwise <c>undefined</c>.
- For more information about modified timing, see
- command-line flag
- <seealso marker="erts:erl#+T"><c>+T</c></seealso>
- in <c>erl(1)</c></p>
- </item>
- <tag><c>multi_scheduling</c></tag>
- <item>
- <marker id="system_info_multi_scheduling"></marker>
<p>Returns one of the following:</p>
<taglist>
<tag><c>disabled</c></tag>
@@ -8244,9 +8496,9 @@ ok
and <seealso marker="#system_info_schedulers">
<c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
- <tag><c>multi_scheduling_blockers</c></tag>
+ <tag><marker id="system_info_multi_scheduling_blockers"/>
+ <c>multi_scheduling_blockers</c></tag>
<item>
- <marker id="system_info_multi_scheduling_blockers"></marker>
<p>Returns a list of <c><anno>Pid</anno></c>s when
multi-scheduling is blocked, otherwise the empty list is
returned. The <c><anno>Pid</anno></c>s in the list
@@ -8264,15 +8516,9 @@ ok
and <seealso marker="#system_info_schedulers">
<c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
- <tag><c>nif_version</c></tag>
+ <tag><marker id="system_info_normal_multi_scheduling_blockers"/>
+ <c>normal_multi_scheduling_blockers</c></tag>
<item>
- <p>Returns a string containing the version of the Erlang NIF
- interface used by the runtime system. It is on the form
- "&lt;major ver&gt;.&lt;minor ver&gt;".</p>
- </item>
- <tag><c>normal_multi_scheduling_blockers</c></tag>
- <item>
- <marker id="system_info_normal_multi_scheduling_blockers"></marker>
<p>Returns a list of <c><anno>Pid</anno></c>s when
normal multi-scheduling is blocked (that is, all normal schedulers
but one is blocked), otherwise the empty list is returned.
@@ -8290,192 +8536,9 @@ ok
and <seealso marker="#system_info_schedulers">
<c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
- <tag><marker id="system_info_otp_release"/>
- <c>otp_release</c></tag>
- <item>
- <marker id="system_info_otp_release"></marker>
- <p>Returns a string containing the OTP release number of the
- OTP release that the currently executing ERTS application
- is part of.</p>
- <p>As from Erlang/OTP 17, the OTP release number corresponds to
- the major OTP version number. No
- <c>erlang:system_info()</c> argument gives the exact OTP
- version. This is because the exact OTP version in the general case
- is difficult to determine. For more information, see the
- description of versions in
- <seealso marker="doc/system_principles:versions">
- System principles</seealso> in System Documentation.</p>
- </item>
- <tag><marker id="system_info_os_monotonic_time_source"/>
- <c>os_monotonic_time_source</c></tag>
+ <tag><marker id="system_info_scheduler_bind_type"/>
+ <c>scheduler_bind_type</c></tag>
<item>
- <p>Returns a list containing information about the source of
- <seealso marker="erts:time_correction#OS_Monotonic_Time">OS
- monotonic time</seealso> that is used by the runtime system.</p>
- <p>If <c>[]</c> is returned, no OS monotonic time is
- available. The list contains two-tuples with <c>Key</c>s
- as first element, and <c>Value</c>s as second element. The
- order of these tuples is undefined. The following
- tuples can be part of the list, but more tuples can be
- introduced in the future:</p>
- <taglist>
- <tag><c>{function, Function}</c></tag>
- <item><p><c>Function</c> is the name of the function
- used. This tuple always exists if OS monotonic time is
- available to the runtime system.</p>
- </item>
- <tag><c>{clock_id, ClockId}</c></tag>
- <item><p>This tuple only exists if <c>Function</c>
- can be used with different clocks. <c>ClockId</c>
- corresponds to the clock identifier used when calling
- <c>Function</c>.</p>
- </item>
- <tag><c>{resolution, OsMonotonicTimeResolution}</c></tag>
- <item><p>Highest possible
- <seealso marker="time_correction#Time_Resolution">
- resolution</seealso>
- of current OS monotonic time source as parts per
- second. If no resolution information can be retrieved
- from the OS, <c>OsMonotonicTimeResolution</c> is
- set to the resolution of the time unit of
- <c>Function</c>s return value. That is, the actual
- resolution can be lower than
- <c>OsMonotonicTimeResolution</c>. Notice that
- the resolution does not say anything about the
- <seealso marker="time_correction#Time_Accuracy">
- accuracy</seealso> or whether the
- <seealso marker="time_correction#Time_Precision">
- precision</seealso> aligns with the resolution. You do,
- however, know that the precision is not better than
- <c>OsMonotonicTimeResolution</c>.</p>
- </item>
- <tag><c>{extended, Extended}</c></tag>
- <item><p><c>Extended</c> equals <c>yes</c> if
- the range of time values has been extended;
- otherwise <c>Extended</c> equals <c>no</c>. The
- range must be extended if <c>Function</c>
- returns values that wrap fast. This typically
- is the case when the return value is a 32-bit value.</p>
- </item>
- <tag><c>{parallel, Parallel}</c></tag>
- <item><p><c>Parallel</c> equals <c>yes</c> if
- <c>Function</c> is called in parallel from multiple
- threads. If it is not called in parallel, because
- calls must be serialized, <c>Parallel</c> equals
- <c>no</c>.</p>
- </item>
- <tag><c>{time, OsMonotonicTime}</c></tag>
- <item><p><c>OsMonotonicTime</c> equals current OS
- monotonic time in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>.</p>
- </item>
- </taglist>
- </item>
- <tag><marker id="system_info_os_system_time_source"/>
- <c>os_system_time_source</c></tag>
- <item>
- <p>Returns a list containing information about the source of
- <seealso marker="erts:time_correction#OS_System_Time">OS
- system time</seealso> that is used by the runtime system.</p>
- <p>The list contains two-tuples with <c>Key</c>s
- as first element, and <c>Value</c>s as second element. The
- order if these tuples is undefined. The following
- tuples can be part of the list, but more tuples can be
- introduced in the future:</p>
- <taglist>
- <tag><c>{function, Function}</c></tag>
- <item><p><c>Function</c> is the name of the funcion used.</p>
- </item>
- <tag><c>{clock_id, ClockId}</c></tag>
- <item><p>Exists only if <c>Function</c>
- can be used with different clocks. <c>ClockId</c>
- corresponds to the clock identifier used when calling
- <c>Function</c>.</p>
- </item>
- <tag><c>{resolution, OsSystemTimeResolution}</c></tag>
- <item><p>Highest possible
- <seealso marker="time_correction#Time_Resolution">
- resolution</seealso>
- of current OS system time source as parts per
- second. If no resolution information can be retrieved
- from the OS, <c>OsSystemTimeResolution</c> is
- set to the resolution of the time unit of
- <c>Function</c>s return value. That is, the actual
- resolution can be lower than
- <c>OsSystemTimeResolution</c>. Notice that
- the resolution does not say anything about the
- <seealso marker="time_correction#Time_Accuracy">
- accuracy</seealso> or whether the
- <seealso marker="time_correction#Time_Precision">
- precision</seealso> do align with the resolution. You do,
- however, know that the precision is not better than
- <c>OsSystemTimeResolution</c>.</p>
- </item>
- <tag><c>{parallel, Parallel}</c></tag>
- <item><p><c>Parallel</c> equals <c>yes</c> if
- <c>Function</c> is called in parallel from multiple
- threads. If it is not called in parallel, because
- calls needs to be serialized, <c>Parallel</c> equals
- <c>no</c>.</p>
- </item>
- <tag><c>{time, OsSystemTime}</c></tag>
- <item><p><c>OsSystemTime</c> equals current OS
- system time in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>.</p>
- </item>
- </taglist>
- </item>
- <tag><c>port_parallelism</c></tag>
- <item>
- <marker id="system_info_port_parallelism"></marker>
- <p>Returns the default port parallelism scheduling hint used.
- For more information, see command-line argument
- <seealso marker="erl#+spp"><c>+spp</c></seealso>
- in <c>erl(1)</c>.</p>
- </item>
- <tag><marker id="system_info_port_count"/><c>port_count</c></tag>
- <item>
- <p>Returns the number of ports currently existing at the
- local node. The value is given as an integer. This is
- the same value as returned by
- <c>length(erlang:ports())</c>, but more efficient.</p>
- </item>
- <tag><c>port_limit</c></tag>
- <item>
- <marker id="system_info_port_limit"></marker>
- <p>Returns the maximum number of simultaneously existing
- ports at the local node as an integer. This limit can be
- configured at startup by using command-line flag
- <seealso marker="erl#+Q"><c>+Q</c></seealso> in <c>erl(1)</c>.</p>
- </item>
- <tag><marker id="system_info_process_count"/>
- <c>process_count</c></tag>
- <item>
- <p>Returns the number of processes currently existing at the
- local node. The value is given as an integer. This is
- the same value as returned by
- <c>length(processes())</c>, but more efficient.</p>
- </item>
- <tag><c>process_limit</c></tag>
- <item>
- <marker id="system_info_process_limit"></marker>
- <p>Returns the maximum number of simultaneously existing
- processes at the local node. The value is given as an
- integer. This limit can be configured at startup by using
- command-line flag <seealso marker="erl#+P"><c>+P</c></seealso>
- in <c>erl(1)</c>.</p>
- </item>
- <tag><c>procs</c></tag>
- <item>
- <p>Returns a binary containing a string of process and port
- information formatted as in Erlang crash dumps. For more
- information, see section <seealso marker="erts:crash_dump">
- How to interpret the Erlang crash dumps</seealso>
- in the User's Guide.</p>
- </item>
- <tag><c>scheduler_bind_type</c></tag>
- <item>
- <marker id="system_info_scheduler_bind_type"></marker>
<p>Returns information about how the user has requested
schedulers to be bound or not bound.</p>
<p>Notice that although a user has requested
@@ -8489,9 +8552,9 @@ ok
<seealso marker="#system_info_scheduler_bindings">
<c>erlang:system_info(scheduler_bindings)</c></seealso>.</p>
</item>
- <tag><c>scheduler_bindings</c></tag>
+ <tag><marker id="system_info_scheduler_bindings"/>
+ <c>scheduler_bindings</c></tag>
<item>
- <marker id="system_info_scheduler_bindings"></marker>
<p>Returns information about the currently used scheduler
bindings.</p>
<p>A tuple of a size equal to
@@ -8515,9 +8578,9 @@ ok
<seealso marker="#system_info_schedulers_online">
<c>erlang:system_info(schedulers_online)</c></seealso>.</p>
</item>
- <tag><c>scheduler_id</c></tag>
+ <tag><marker id="system_info_scheduler_id"/>
+ <c>scheduler_id</c></tag>
<item>
- <marker id="system_info_scheduler_id"></marker>
<p>Returns the scheduler ID (<c>SchedulerId</c>) of the
scheduler thread that the calling process is executing
on. <c><anno>SchedulerId</anno></c> is a positive integer,
@@ -8527,9 +8590,9 @@ ok
<seealso marker="#system_info_schedulers">
<c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
- <tag><c>schedulers</c></tag>
+ <tag><marker id="system_info_schedulers"/>
+ <c>schedulers</c></tag>
<item>
- <marker id="system_info_schedulers"></marker>
<p>Returns the number of scheduler threads used by
the emulator. Scheduler threads online schedules Erlang
processes and Erlang ports, and execute Erlang code
@@ -8556,9 +8619,9 @@ ok
<c>erlang:system_info(multi_scheduling_blockers)</c></seealso>.
</p>
</item>
- <tag><c>schedulers_online</c></tag>
+ <tag><marker id="system_info_schedulers_online"/>
+ <c>schedulers_online</c></tag>
<item>
- <marker id="system_info_schedulers_online"></marker>
<p>Returns the number of schedulers online. The scheduler
identifiers of schedulers online satisfy the relationship
<c><![CDATA[1 <= SchedulerId <=
@@ -8570,34 +8633,18 @@ ok
<c>erlang:system_flag(schedulers_online,
SchedulersOnline)</c></seealso>.</p>
</item>
- <tag><c>smp_support</c></tag>
+ <tag><marker id="system_info_smp_support"/>
+ <c>smp_support</c></tag>
<item>
<p>Returns <c>true</c>.</p>
</item>
- <tag><marker id="system_info_start_time"/><c>start_time</c></tag>
- <item>
- <p>The <seealso marker="#monotonic_time/0">Erlang monotonic
- time</seealso> in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso> at the
- time when current Erlang runtime system instance started.</p>
- <p>See also <seealso marker="#system_info_end_time">
- <c>erlang:system_info(end_time)</c></seealso>.</p>
- </item>
- <tag><c>system_version</c></tag>
- <item>
- <p>Returns a string containing version number and
- some important properties, such as the number of schedulers.</p>
- </item>
- <tag><c>system_architecture</c></tag>
- <item>
- <p>Returns a string containing the processor and OS
- architecture the emulator is built for.</p>
- </item>
- <tag><c>threads</c></tag>
+ <tag><marker id="system_info_threads"/>
+ <c>threads</c></tag>
<item>
<p>Returns <c>true</c>.</p>
</item>
- <tag><c>thread_pool_size</c></tag>
+ <tag><marker id="system_info_thread_pool_size"/>
+ <c>thread_pool_size</c></tag>
<item>
<marker id="system_info_thread_pool_size"></marker>
<p>Returns the number of async threads in the async thread
@@ -8606,111 +8653,341 @@ ok
<c>erl_driver:driver_async()</c></seealso>).
The value is given as an integer.</p>
</item>
- <tag><c>time_correction</c></tag>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name name="system_info" arity="1" clause_i="14"
+ anchor="system_info_dist"/> <!-- creation -->
+ <name name="system_info" arity="1" clause_i="16"/> <!-- delayed_node_table_gc -->
+ <name name="system_info" arity="1" clause_i="20"/> <!-- dist -->
+ <name name="system_info" arity="1" clause_i="21"/> <!-- dist_buf_busy_limit -->
+ <name name="system_info" arity="1" clause_i="22"/> <!-- dist_ctrl -->
+ <fsummary>Information about erlang distribution.</fsummary>
+ <desc>
+ <marker id="system_info_dist_tags"/>
+ <p>Returns information about Erlang Distribution in the
+ current system as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <tag><marker id="system_info_creation"/>
+ <c>creation</c></tag>
<item>
- <marker id="system_info_time_correction"></marker>
- <p>Returns a boolean value indicating whether
- <seealso marker="time_correction#Time_Correction">
- time correction</seealso> is enabled or not.</p>
+ <p>Returns the creation of the local node as an integer.
+ The creation is changed when a node is restarted. The
+ creation of a node is stored in process identifiers, port
+ identifiers, and references. This makes it (to some
+ extent) possible to distinguish between identifiers from
+ different incarnations of a node. The valid
+ creations are integers in the range 1..3, but this will
+ probably change in a future release. If the node is not
+ alive, <c>0</c> is returned.</p>
</item>
- <tag><c>time_offset</c></tag>
+ <tag><marker id="system_info_delayed_node_table_gc"/>
+ <c>delayed_node_table_gc</c></tag>
<item>
- <marker id="system_info_time_offset"></marker>
- <p>Returns the state of the time offset:</p>
- <taglist>
- <tag><c>preliminary</c></tag>
- <item>
- <p>The time offset is preliminary, and will be changed
- and finalized later. The preliminary time offset
- is used during the preliminary phase of the
- <seealso marker="time_correction#Single_Time_Warp_Mode">
- single time warp mode</seealso>.</p>
- </item>
- <tag><c>final</c></tag>
- <item>
- <p>The time offset is final. This either because
- <seealso marker="time_correction#No_Time_Warp_Mode">
- no time warp mode</seealso> is used, or because the time
- offset have been finalized when
- <seealso marker="time_correction#Single_Time_Warp_Mode">
- single time warp mode</seealso> is used.</p>
- </item>
- <tag><c>volatile</c></tag>
- <item>
- <p>The time offset is volatile. That is, it can
- change at any time. This is because
- <seealso marker="time_correction#Multi_Time_Warp_Mode">
- multi-time warp mode</seealso> is used.</p>
- </item>
- </taglist>
+ <p>Returns the amount of time in seconds garbage collection
+ of an entry in a node table is delayed. This limit can be set
+ on startup by passing command-line flag
+ <seealso marker="erts:erl#+zdntgc"><c>+zdntgc</c></seealso>
+ to <c>erl(1)</c>. For more information, see the documentation of
+ the command-line flag.</p>
</item>
- <tag><marker id="system_info_time_warp_mode"/>
- <c>time_warp_mode</c></tag>
+ <tag><marker id="system_info_dist"/>
+ <c>dist</c></tag>
<item>
- <p>Returns a value identifying the
- <seealso marker="time_correction#Time_Warp_Modes">
- time warp mode</seealso> that is used:</p>
- <taglist>
- <tag><c>no_time_warp</c></tag>
- <item>The <seealso marker="time_correction#No_Time_Warp_Mode">
- no time warp mode</seealso> is used.
- </item>
- <tag><c>single_time_warp</c></tag>
- <item>The <seealso marker="time_correction#Single_Time_Warp_Mode">
- single time warp mode</seealso> is used.
- </item>
- <tag><c>multi_time_warp</c></tag>
- <item>The <seealso marker="time_correction#Multi_Time_Warp_Mode">
- multi-time warp mode</seealso> is used.
- </item>
- </taglist>
+ <p>Returns a binary containing a string of distribution
+ information formatted as in Erlang crash dumps. For more
+ information, see section <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
+ in the User's Guide.</p>
</item>
- <tag><c>tolerant_timeofday</c></tag>
+ <tag><marker id="system_info_dist_buf_busy_limit"/>
+ <c>dist_buf_busy_limit</c></tag>
<item>
- <marker id="system_info_tolerant_timeofday"></marker>
- <p>Returns whether a pre ERTS 7.0 backwards compatible
- compensation for sudden changes of system time is <c>enabled</c>
- or <c>disabled</c>. Such compensation is <c>enabled</c> when the
- <seealso marker="#system_info_time_offset">time offset</seealso>
- is <c>final</c>, and
- <seealso marker="#system_info_time_correction">
- time correction</seealso> is enabled.</p>
+ <p>Returns the value of the distribution buffer busy limit
+ in bytes. This limit can be set at startup by passing
+ command-line flag
+ <seealso marker="erts:erl#+zdbbl"><c>+zdbbl</c></seealso>
+ to <c>erl(1)</c>.</p>
+ </item>
+ <tag><marker id="system_info_dist_ctrl"/>
+ <c>dist_ctrl</c></tag>
+ <item>
+ <p>Returns a list of tuples
+ <c>{<anno>Node</anno>, <anno>ControllingEntity</anno>}</c>,
+ one entry for each connected remote node.
+ <c><anno>Node</anno></c> is the node name
+ and <c><anno>ControllingEntity</anno></c> is the port or process
+ identifier responsible for the communication to that node.
+ More specifically, <c><anno>ControllingEntity</anno></c> for
+ nodes connected through TCP/IP (the normal case) is the socket
+ used in communication with the specific node.</p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <!-- <name name="system_info" arity="1" clause_i="1"/> allocated_areas -->
+ <!-- <name name="system_info" arity="1" clause_i="2"/> allocated -->
+ <!-- <name name="system_info" arity="1" clause_i="3"/> {allocator, _} -->
+ <!-- <name name="system_info" arity="1" clause_i="4"/> alloc_util_allocators -->
+ <!-- <name name="system_info" arity="1" clause_i="5"/> {allocator_sizes, _} -->
+ <!-- <name name="system_info" arity="1" clause_i="6"/> atom_count -->
+ <!-- <name name="system_info" arity="1" clause_i="7"/> atom_limit -->
+ <name name="system_info" arity="1" clause_i="8"
+ anchor="system_info_misc"/> <!-- build_type -->
+ <name name="system_info" arity="1" clause_i="9"/> <!-- c_compiler_used -->
+ <name name="system_info" arity="1" clause_i="10"/> <!-- check_io -->
+ <name name="system_info" arity="1" clause_i="11"/> <!-- compat_rel -->
+ <!-- <name name="system_info" arity="1" clause_i="12"/> cpu_topology -->
+ <!-- <name name="system_info" arity="1" clause_i="13"/> {cpu_topology, _} -->
+ <!-- <name name="system_info" arity="1" clause_i="14"/> creation -->
+ <name name="system_info" arity="1" clause_i="15"/> <!-- debug_compiled -->
+ <!-- <name name="system_info" arity="1" clause_i="16"/> delayed_node_table_gc -->
+ <!-- <name name="system_info" arity="1" clause_i="17"/> dirty_cpu_schedulers -->
+ <!-- <name name="system_info" arity="1" clause_i="18"/> dirty_cpu_schedulers_online -->
+ <!-- <name name="system_info" arity="1" clause_i="19"/> dirty_io_schedulers -->
+ <!-- <name name="system_info" arity="1" clause_i="20"/> dist -->
+ <!-- <name name="system_info" arity="1" clause_i="21"/> dist_buf_busy_limit -->
+ <!-- <name name="system_info" arity="1" clause_i="22"/> dist_ctrl -->
+ <name name="system_info" arity="1" clause_i="23"/> <!-- driver_version -->
+ <name name="system_info" arity="1" clause_i="24"/> <!-- dynamic_trace -->
+ <name name="system_info" arity="1" clause_i="25"/> <!-- dynamic_trace_probes -->
+ <!-- <name name="system_info" arity="1" clause_i="26"/> end_time -->
+ <!-- <name name="system_info" arity="1" clause_i="27"/> elib_malloc -->
+ <!-- <name name="system_info" arity="1" clause_i="28"/> eager_check_io, removed -->
+ <!-- <name name="system_info" arity="1" clause_i="29"/> ets_limit -->
+ <!-- <name name="system_info" arity="1" clause_i="30"/> fullsweep_after -->
+ <!-- <name name="system_info" arity="1" clause_i="31"/> garbage_collection -->
+ <!-- <name name="system_info" arity="1" clause_i="32"/> heap_sizes -->
+ <!-- <name name="system_info" arity="1" clause_i="33"/> heap_type -->
+ <name name="system_info" arity="1" clause_i="34"/> <!-- info -->
+ <name name="system_info" arity="1" clause_i="35"/> <!-- kernel_poll -->
+ <name name="system_info" arity="1" clause_i="36"/> <!-- loaded -->
+ <!-- <name name="system_info" arity="1" clause_i="37"/> logical_processors -->
+ <name name="system_info" arity="1" clause_i="38"/> <!-- machine -->
+ <!-- <name name="system_info" arity="1" clause_i="39"/> max_heap_size -->
+ <!-- <name name="system_info" arity="1" clause_i="40"/> message_queue_data -->
+ <!-- <name name="system_info" arity="1" clause_i="41"/> min_heap_size -->
+ <!-- <name name="system_info" arity="1" clause_i="42"/> min_bin_vheap_size -->
+ <name name="system_info" arity="1" clause_i="43"/> <!-- modified_timing_level -->
+ <!-- <name name="system_info" arity="1" clause_i="44"/> multi_scheduling -->
+ <!-- <name name="system_info" arity="1" clause_i="45"/> multi_scheduling_blockers -->
+ <name name="system_info" arity="1" clause_i="46"/> <!-- nif_version -->
+ <!-- n<name name="system_info" arity="1" clause_i="47"/> ormal_multi_scheduling_blockers -->
+ <name name="system_info" arity="1" clause_i="48"/> <!-- otp_release -->
+ <!-- <name name="system_info" arity="1" clause_i="49"/> os_monotonic_time_source -->
+ <!-- <name name="system_info" arity="1" clause_i="50"/> os_system_time_source -->
+ <name name="system_info" arity="1" clause_i="51"/> <!-- port_parallelism -->
+ <!-- <name name="system_info" arity="1" clause_i="52"/> port_count -->
+ <!-- <name name="system_info" arity="1" clause_i="53"/> port_limit -->
+ <!-- <name name="system_info" arity="1" clause_i="54"/> process_count -->
+ <!-- <name name="system_info" arity="1" clause_i="55"/> process_limit -->
+ <!-- <name name="system_info" arity="1" clause_i="56"/> procs -->
+ <!-- <name name="system_info" arity="1" clause_i="57"/> scheduler_bind_type -->
+ <!-- <name name="system_info" arity="1" clause_i="58"/> scheduler_bindings -->
+ <!-- <name name="system_info" arity="1" clause_i="59"/> scheduler_id -->
+ <!-- <name name="system_info" arity="1" clause_i="60"/> schedulers -->
+ <!-- <name name="system_info" arity="1" clause_i="61"/> smp_support -->
+ <!-- <name name="system_info" arity="1" clause_i="62"/> start_time -->
+ <name name="system_info" arity="1" clause_i="63"/> <!-- system_version -->
+ <name name="system_info" arity="1" clause_i="64"/> <!-- system_architecture -->
+ <!-- <name name="system_info" arity="1" clause_i="65"/> threads -->
+ <!-- <name name="system_info" arity="1" clause_i="66"/> thread_pool_size -->
+ <!-- <name name="system_info" arity="1" clause_i="67"/> time_correction -->
+ <!-- <name name="system_info" arity="1" clause_i="68"/> time_offset -->
+ <!-- <name name="system_info" arity="1" clause_i="69"/> time_warp_mode -->
+ <!-- <name name="system_info" arity="1" clause_i="70"/> tolerant_timeofday -->
+ <name name="system_info" arity="1" clause_i="71"/> <!-- trace_control_word -->
+ <!-- <name name="system_info" arity="1" clause_i="72"/> update_cpu_info -->
+ <name name="system_info" arity="1" clause_i="73"/> <!-- version -->
+ <name name="system_info" arity="1" clause_i="74"/> <!-- wordsize -->
+ <!-- <name name="system_info" arity="1" clause_i="75"/> overview -->
+ <fsummary>Information about the system.</fsummary>
+ <desc>
+ <marker id="system_info_misc_tags"/>
+ <p>Returns various information about the current system
+ (emulator) as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <tag><marker id="system_info_build_type"/>
+ <c>build_type</c></tag>
+ <item>
+ <p>Returns an atom describing the build type of the runtime
+ system. This is normally the atom <c>opt</c> for optimized.
+ Other possible return values are <c>debug</c>, <c>purify</c>,
+ <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>,
+ <c>gprof</c>, and <c>lcnt</c>. Possible return values
+ can be added or removed at any time without prior notice.</p>
+ </item>
+ <tag><marker id="system_info_c_compiler_used"/>
+ <c>c_compiler_used</c></tag>
+ <item>
+ <p>Returns a two-tuple describing the C compiler used when
+ compiling the runtime system. The first element is an
+ atom describing the name of the compiler, or <c>undefined</c>
+ if unknown. The second element is a term describing the
+ version of the compiler, or <c>undefined</c> if unknown.</p>
</item>
- <tag><c>trace_control_word</c></tag>
+ <tag><marker id="system_info_check_io"/>
+ <c>check_io</c></tag>
+ <item>
+ <p>Returns a list containing miscellaneous information
+ about the emulators internal I/O checking. Notice that
+ the content of the returned list can vary between
+ platforms and over time. It is only guaranteed
+ that a list is returned.</p>
+ </item>
+ <tag><marker id="system_info_compat_rel"/>
+ <c>compat_rel</c></tag>
+ <item>
+ <p>Returns the compatibility mode of the local node as
+ an integer. The integer returned represents the
+ Erlang/OTP release that the current emulator has been
+ set to be backward compatible with. The compatibility
+ mode can be configured at startup by using command-line flag
+ <seealso marker="erts:erl#compat_rel"><c>+R</c></seealso> in
+ <c>erl(1)</c>.</p>
+ </item>
+ <tag><marker id="system_info_debug_compiled"/>
+ <c>debug_compiled</c></tag>
+ <item>
+ <p>Returns <c>true</c> if the emulator has been
+ debug-compiled, otherwise <c>false</c>.</p>
+ </item>
+ <tag><marker id="system_info_driver_version"/>
+ <c>driver_version</c></tag>
+ <item>
+ <p>Returns a string containing the Erlang driver version
+ used by the runtime system. It has the form
+ <seealso marker="erts:erl_driver#version_management">
+ "&lt;major ver&gt;.&lt;minor ver&gt;"</seealso>.</p>
+ </item>
+ <tag><marker id="system_info_dynamic_trace"/>
+ <c>dynamic_trace</c></tag>
+ <item>
+ <p>Returns an atom describing the dynamic trace framework
+ compiled into the virtual machine. It can be
+ <c>dtrace</c>, <c>systemtap</c>, or <c>none</c>. For a
+ commercial or standard build, it is always <c>none</c>.
+ The other return values indicate a custom configuration
+ (for example, <c>./configure --with-dynamic-trace=dtrace</c>).
+ For more information about dynamic tracing, see
+ <seealso marker="runtime_tools:dyntrace">
+ <c>dyntrace(3)</c></seealso> manual page and the
+ <c>README.dtrace</c>/<c>README.systemtap</c> files in the
+ Erlang source code top directory.</p>
+ </item>
+ <tag><marker id="system_info_dynamic_trace_probes"/>
+ <c>dynamic_trace_probes</c></tag>
+ <item>
+ <p>Returns a <c>boolean()</c> indicating if dynamic trace
+ probes (<c>dtrace</c> or <c>systemtap</c>) are built into
+ the emulator. This can only be <c>true</c> if the virtual
+ machine was built for dynamic tracing (that is,
+ <c>system_info(dynamic_trace)</c> returns
+ <c>dtrace</c> or <c>systemtap</c>).</p>
+ </item>
+ <tag><marker id="system_info_info"/>
+ <c>info</c></tag>
+ <item>
+ <p>Returns a binary containing a string of miscellaneous
+ system information formatted as in Erlang crash dumps.
+ For more information, see section
+ <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
+ in the User's Guide.</p>
+ </item>
+ <tag><marker id="system_info_kernel_poll"/>
+ <c>kernel_poll</c></tag>
+ <item>
+ <p>Returns <c>true</c> if the emulator uses some kind of
+ kernel-poll implementation, otherwise <c>false</c>.</p>
+ </item>
+ <tag><marker id="system_info_loaded"/>
+ <c>loaded</c></tag>
+ <item>
+ <p>Returns a binary containing a string of loaded module
+ information formatted as in Erlang crash dumps. For more
+ information, see section
+ <seealso marker="erts:crash_dump">How to interpret the Erlang
+ crash dumps</seealso> in the User's Guide.</p>
+ </item>
+ <tag><marker id="system_info_machine"/>
+ <c>machine</c></tag>
+ <item>
+ <p>Returns a string containing the Erlang machine name.</p>
+ </item>
+ <tag><marker id="system_info_modified_timing_level"/>
+ <c>modified_timing_level</c></tag>
+ <item>
+ <p>Returns the modified timing-level (an integer) if
+ modified timing is enabled, otherwise <c>undefined</c>.
+ For more information about modified timing, see
+ command-line flag
+ <seealso marker="erts:erl#+T"><c>+T</c></seealso>
+ in <c>erl(1)</c></p>
+ </item>
+ <tag><marker id="system_info_nif_version"/>
+ <c>nif_version</c></tag>
+ <item>
+ <p>Returns a string containing the version of the Erlang NIF
+ interface used by the runtime system. It is on the form
+ "&lt;major ver&gt;.&lt;minor ver&gt;".</p>
+ </item>
+ <tag><marker id="system_info_otp_release"/>
+ <c>otp_release</c></tag>
+ <item>
+ <marker id="system_info_otp_release"></marker>
+ <p>Returns a string containing the OTP release number of the
+ OTP release that the currently executing ERTS application
+ is part of.</p>
+ <p>As from Erlang/OTP 17, the OTP release number corresponds to
+ the major OTP version number. No
+ <c>erlang:system_info()</c> argument gives the exact OTP
+ version. This is because the exact OTP version in the general case
+ is difficult to determine. For more information, see the
+ description of versions in
+ <seealso marker="doc/system_principles:versions">
+ System principles</seealso> in System Documentation.</p>
+ </item>
+ <tag><marker id="system_info_port_parallelism"/>
+ <c>port_parallelism</c></tag>
+ <item>
+ <p>Returns the default port parallelism scheduling hint used.
+ For more information, see command-line argument
+ <seealso marker="erl#+spp"><c>+spp</c></seealso>
+ in <c>erl(1)</c>.</p>
+ </item>
+ <tag><marker id="system_info_system_version"/>
+ <c>system_version</c></tag>
+ <item>
+ <p>Returns a string containing version number and
+ some important properties, such as the number of schedulers.</p>
+ </item>
+ <tag><marker id="system_info_system_architecture"/>
+ <c>system_architecture</c></tag>
+ <item>
+ <p>Returns a string containing the processor and OS
+ architecture the emulator is built for.</p>
+ </item>
+ <tag><marker id="system_info_trace_control_word"/>
+ <c>trace_control_word</c></tag>
<item>
<p>Returns the value of the node trace control word. For
more information, see function <c>get_tcw</c> in section
<seealso marker="erts:match_spec#get_tcw">
Match Specifications in Erlang</seealso> in the User's Guide.</p>
</item>
- <tag><c>update_cpu_info</c></tag>
+ <tag><marker id="system_info_version"/>
+ <c>version</c></tag>
<item>
- <marker id="update_cpu_info"></marker>
- <p>The runtime system rereads the CPU information available
- and updates its internally stored information about the
- <seealso marker="#system_info_cpu_topology_detected">detected
- CPU topology</seealso> and the number of logical processors
- <seealso marker="#logical_processors">configured</seealso>,
- <seealso marker="#logical_processors_online">online</seealso>,
- and <seealso marker="#logical_processors_available">
- available</seealso>.</p>
- <p>If the CPU information has changed since the last time
- it was read, the atom <c>changed</c> is returned, otherwise
- the atom <c>unchanged</c>. If the CPU information has changed,
- you probably want to
- <seealso marker="#system_flag_schedulers_online">adjust the
- number of schedulers online</seealso>. You typically want
- to have as many schedulers online as
- <seealso marker="#logical_processors_available">logical
- processors available</seealso>.</p>
- </item>
- <tag><c>version</c></tag>
- <item>
- <marker id="system_info_version"></marker>
<p>Returns a string containing the version number of the
emulator.</p>
</item>
- <tag><c>wordsize</c></tag>
+ <tag><marker id="system_info_wordsize"/>
+ <c>wordsize</c></tag>
<item>
<p>Same as <c>{wordsize, internal}</c>.</p>
</item>
@@ -8732,13 +9009,6 @@ ok
64-bit architecture, 8 is returned.</p>
</item>
</taglist>
- <note>
- <p>Argument <c>scheduler</c> has changed name to
- <c>scheduler_id</c> to avoid mix up with argument
- <c>schedulers</c>. Argument <c>scheduler</c> was
- introduced in ERTS 5.5 and renamed in
- ERTS 5.5.1.</p>
- </note>
</desc>
</func>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 3109da6738..7ef42c2318 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -10964,7 +10964,7 @@
<c>update_cpu_info</c> will make the runtime system
reread and update the internally stored CPU information.
For more information see the documentation of <seealso
- marker="erlang#update_cpu_info">erlang:system_info(update_cpu_info)</seealso>.</p>
+ marker="erlang#system_info_update_cpu_info">erlang:system_info(update_cpu_info)</seealso>.</p>
<p>
The CPU topology is now automatically detected on Windows
systems with less than 33 logical processors. The runtime
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index b6ec3e7ed2..42a368cdd8 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -124,7 +124,6 @@ atom big
atom bif_return_trap
atom bif_timer_server
atom binary
-atom binary_bin_to_list_trap
atom binary_copy_trap
atom binary_find_trap
atom binary_longest_prefix_trap
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index d68ccc3028..bfd572335f 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -1744,7 +1744,6 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
else if (BIF_ARG_1 == am_scheduler) {
ErtsRunQueue *old, *new, *curr;
Sint sched;
- erts_aint32_t state;
if (!is_small(BIF_ARG_2))
goto error;
@@ -1753,23 +1752,23 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
goto error;
if (sched == 0) {
+ old = erts_bind_runq_proc(BIF_P, 0);
new = NULL;
- state = erts_atomic32_read_band_mb(&BIF_P->state,
- ~ERTS_PSFLG_BOUND);
}
else {
+ int bound = !0;
new = erts_schedid2runq(sched);
- erts_atomic_set_nob(&BIF_P->run_queue, (erts_aint_t) new);
- state = erts_atomic32_read_bor_mb(&BIF_P->state,
- ERTS_PSFLG_BOUND);
+ old = erts_set_runq_proc(BIF_P, new, &bound);
+ if (!bound)
+ old = NULL;
}
+ old_value = old ? make_small(old->ix+1) : make_small(0);
+
curr = erts_proc_sched_data(BIF_P)->run_queue;
- old = (ERTS_PSFLG_BOUND & state) ? curr : NULL;
ASSERT(!old || old == curr);
- old_value = old ? make_small(old->ix+1) : make_small(0);
if (new && new != curr)
ERTS_BIF_YIELD_RETURN_X(BIF_P, old_value, am_scheduler);
else
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index c0d5e8ce74..0d1166f6ed 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -551,9 +551,6 @@ bif binary:last/1
bif binary:at/2
bif binary:part/2 binary_binary_part_2
bif binary:part/3 binary_binary_part_3
-bif binary:bin_to_list/1
-bif binary:bin_to_list/2
-bif binary:bin_to_list/3
bif binary:list_to_bin/1
bif binary:copy/1
bif binary:copy/2
@@ -696,3 +693,4 @@ bif erlang:iolist_to_iovec/1
bif erts_internal:new_connection/1
bif erts_internal:abort_connection/2
bif erts_internal:map_next/3
+bif ets:whereis/1
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index d4db1a4188..95d324d2c1 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -953,7 +953,10 @@ HIPE_WRAPPER_BIF_DISABLE_GC(iolist_to_binary, 1)
BIF_RETTYPE iolist_to_binary_1(BIF_ALIST_1)
{
if (is_binary(BIF_ARG_1)) {
- BIF_RET(BIF_ARG_1);
+ if (binary_bitsize(BIF_ARG_1) == 0) {
+ BIF_RET(BIF_ARG_1);
+ }
+ BIF_ERROR(BIF_P, BADARG);
}
return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_iolist_to_binary_1]);
}
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index e7acea0c5f..b9b70cd8ef 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -336,6 +336,12 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p)
erts_print(to, to_arg, "Heap unused: %bpu\n", (p->hend - p->htop));
erts_print(to, to_arg, "OldHeap unused: %bpu\n",
(OLD_HEAP(p) == NULL) ? 0 : (OLD_HEND(p) - OLD_HTOP(p)) );
+ erts_print(to, to_arg, "BinVHeap: %b64u\n", p->off_heap.overhead);
+ erts_print(to, to_arg, "OldBinVHeap: %b64u\n", BIN_OLD_VHEAP(p));
+ erts_print(to, to_arg, "BinVHeap unused: %b64u\n",
+ BIN_VHEAP_SZ(p) - p->off_heap.overhead);
+ erts_print(to, to_arg, "OldBinVHeap unused: %b64u\n",
+ BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p));
erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p, !0));
if (garbing) {
@@ -891,6 +897,21 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
erts_print_scheduler_info(to, to_arg, ERTS_SCHEDULER_IX(i)),
erts_cbprintf(to, to_arg, "** crashed **\n"));
}
+ for (i = 0; i < erts_no_dirty_cpu_schedulers; i++) {
+ ERTS_SYS_TRY_CATCH(
+ erts_print_scheduler_info(to, to_arg, ERTS_DIRTY_CPU_SCHEDULER_IX(i)),
+ erts_cbprintf(to, to_arg, "** crashed **\n"));
+ }
+ erts_cbprintf(to, to_arg, "=dirty_cpu_run_queue\n");
+ erts_print_run_queue_info(to, to_arg, ERTS_DIRTY_CPU_RUNQ);
+
+ for (i = 0; i < erts_no_dirty_io_schedulers; i++) {
+ ERTS_SYS_TRY_CATCH(
+ erts_print_scheduler_info(to, to_arg, ERTS_DIRTY_IO_SCHEDULER_IX(i)),
+ erts_cbprintf(to, to_arg, "** crashed **\n"));
+ }
+ erts_cbprintf(to, to_arg, "=dirty_io_run_queue\n");
+ erts_print_run_queue_info(to, to_arg, ERTS_DIRTY_IO_RUNQ);
#endif
diff --git a/erts/emulator/beam/bs_instrs.tab b/erts/emulator/beam/bs_instrs.tab
index b11903a47b..94e0000c8b 100644
--- a/erts/emulator/beam/bs_instrs.tab
+++ b/erts/emulator/beam/bs_instrs.tab
@@ -990,6 +990,9 @@ ctx_to_bin.execute() {
Uint hole_size;
Uint orig = mb->orig;
ErlSubBin* sb = (ErlSubBin *) boxed_val(context);
+ /* Since we're going to overwrite the match state with the result, an
+ * ErlBinMatchState must be at least as large as an ErlSubBin. */
+ ERTS_CT_ASSERT(sizeof(ErlSubBin) <= sizeof(ErlBinMatchState));
hole_size = 1 + header_arity(sb->thing_word) - ERL_SUB_BIN_SIZE;
sb->thing_word = HEADER_SUB_BIN;
sb->size = BYTE_OFFSET(size);
diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c
index 41c2ae08d3..33bc189182 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -61,8 +61,6 @@ static Export binary_longest_prefix_trap_export;
static BIF_RETTYPE binary_longest_prefix_trap(BIF_ALIST_3);
static Export binary_longest_suffix_trap_export;
static BIF_RETTYPE binary_longest_suffix_trap(BIF_ALIST_3);
-static Export binary_bin_to_list_trap_export;
-static BIF_RETTYPE binary_bin_to_list_trap(BIF_ALIST_3);
static Export binary_copy_trap_export;
static BIF_RETTYPE binary_copy_trap(BIF_ALIST_2);
static Uint max_loop_limit;
@@ -86,10 +84,6 @@ void erts_init_bif_binary(void)
am_erlang, am_binary_longest_suffix_trap, 3,
&binary_longest_suffix_trap);
- erts_init_trap_export(&binary_bin_to_list_trap_export,
- am_erlang, am_binary_bin_to_list_trap, 3,
- &binary_bin_to_list_trap);
-
erts_init_trap_export(&binary_copy_trap_export,
am_erlang, am_binary_copy_trap, 2,
&binary_copy_trap);
@@ -2440,191 +2434,6 @@ BIF_RETTYPE binary_at_2(BIF_ALIST_2)
BIF_ERROR(BIF_P,BADARG);
}
-#define BIN_TO_LIST_OK 0
-#define BIN_TO_LIST_TRAP 1
-/* No badarg, checked before call */
-
-#define BIN_TO_LIST_LOOP_FACTOR 10
-
-static int do_bin_to_list(Process *p, byte *bytes, Uint bit_offs,
- Uint start, Sint *lenp, Eterm *termp)
-{
- Uint reds = get_reds(p, BIN_TO_LIST_LOOP_FACTOR); /* reds can never be 0 */
- Uint len = *lenp;
- Uint loops;
- Eterm *hp;
- Eterm term = *termp;
- Uint n;
-
- ASSERT(reds > 0);
-
- loops = MIN(reds,len);
-
- BUMP_REDS(p, loops / BIN_TO_LIST_LOOP_FACTOR);
-
- hp = HAlloc(p,2*loops);
- while (loops--) {
- --len;
- if (bit_offs) {
- n = ((((Uint) bytes[start+len]) << bit_offs) |
- (((Uint) bytes[start+len+1]) >> (8-bit_offs))) & 0xFF;
- } else {
- n = bytes[start+len];
- }
-
- term = CONS(hp,make_small(n),term);
- hp +=2;
- }
- *termp = term;
- *lenp = len;
- if (len) {
- BUMP_ALL_REDS(p);
- return BIN_TO_LIST_TRAP;
- }
- return BIN_TO_LIST_OK;
-}
-
-
-static BIF_RETTYPE do_trap_bin_to_list(Process *p, Eterm binary,
- Uint start, Sint len, Eterm sofar)
-{
- Eterm *hp;
- Eterm blob;
-
- hp = HAlloc(p,3);
- hp[0] = make_pos_bignum_header(2);
- hp[1] = start;
- hp[2] = (Uint) len;
- blob = make_big(hp);
- BIF_TRAP3(&binary_bin_to_list_trap_export, p, binary, blob, sofar);
-}
-
-static BIF_RETTYPE binary_bin_to_list_trap(BIF_ALIST_3)
-{
- Eterm *ptr;
- Uint start;
- Sint len;
- byte *bytes;
- Uint bit_offs;
- Uint bit_size;
- Eterm res = BIF_ARG_3;
-
- ptr = big_val(BIF_ARG_2);
- start = ptr[1];
- len = (Sint) ptr[2];
-
- ERTS_GET_BINARY_BYTES(BIF_ARG_1,bytes,bit_offs,bit_size);
- if (do_bin_to_list(BIF_P, bytes, bit_offs, start, &len, &res) ==
- BIN_TO_LIST_OK) {
- BIF_RET(res);
- }
- return do_trap_bin_to_list(BIF_P,BIF_ARG_1,start,len,res);
-}
-
-static BIF_RETTYPE binary_bin_to_list_common(Process *p,
- Eterm bin,
- Eterm epos,
- Eterm elen)
-{
- Uint pos;
- Sint len;
- size_t sz;
- byte *bytes;
- Uint bit_offs;
- Uint bit_size;
- Eterm res = NIL;
-
- if (is_not_binary(bin)) {
- goto badarg;
- }
- if (!term_to_Uint(epos, &pos)) {
- goto badarg;
- }
- if (!term_to_Sint(elen, &len)) {
- goto badarg;
- }
- if (len < 0) {
- Uint lentmp = -(Uint)len;
- /* overflow */
- if ((Sint)lentmp < 0) {
- goto badarg;
- }
- len = lentmp;
- if (len > pos) {
- goto badarg;
- }
- pos -= len;
- }
- /* overflow */
- if ((pos + len) < pos || (len > 0 && (pos + len) == pos)) {
- goto badarg;
- }
- sz = binary_size(bin);
-
- if (pos+len > sz) {
- goto badarg;
- }
- ERTS_GET_BINARY_BYTES(bin,bytes,bit_offs,bit_size);
- if (bit_size != 0) {
- goto badarg;
- }
- if(do_bin_to_list(p, bytes, bit_offs, pos, &len, &res) ==
- BIN_TO_LIST_OK) {
- BIF_RET(res);
- }
- return do_trap_bin_to_list(p,bin,pos,len,res);
-
- badarg:
- BIF_ERROR(p,BADARG);
-}
-
-BIF_RETTYPE binary_bin_to_list_3(BIF_ALIST_3)
-{
- return binary_bin_to_list_common(BIF_P,BIF_ARG_1,BIF_ARG_2,BIF_ARG_3);
-}
-
-BIF_RETTYPE binary_bin_to_list_2(BIF_ALIST_2)
-{
- Eterm *tp;
-
- if (is_not_tuple(BIF_ARG_2)) {
- goto badarg;
- }
- tp = tuple_val(BIF_ARG_2);
- if (arityval(*tp) != 2) {
- goto badarg;
- }
- return binary_bin_to_list_common(BIF_P,BIF_ARG_1,tp[1],tp[2]);
- badarg:
- BIF_ERROR(BIF_P,BADARG);
-}
-
-BIF_RETTYPE binary_bin_to_list_1(BIF_ALIST_1)
-{
- Uint pos = 0;
- Sint len;
- byte *bytes;
- Uint bit_offs;
- Uint bit_size;
- Eterm res = NIL;
-
- if (is_not_binary(BIF_ARG_1)) {
- goto badarg;
- }
- len = binary_size(BIF_ARG_1);
- ERTS_GET_BINARY_BYTES(BIF_ARG_1,bytes,bit_offs,bit_size);
- if (bit_size != 0) {
- goto badarg;
- }
- if(do_bin_to_list(BIF_P, bytes, bit_offs, pos, &len, &res) ==
- BIN_TO_LIST_OK) {
- BIF_RET(res);
- }
- return do_trap_bin_to_list(BIF_P,BIF_ARG_1,pos,len,res);
- badarg:
- BIF_ERROR(BIF_P,BADARG);
-}
-
HIPE_WRAPPER_BIF_DISABLE_GC(binary_list_to_bin, 1)
BIF_RETTYPE binary_list_to_bin_1(BIF_ALIST_1)
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 9f0c90ff7b..b184adedee 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -639,6 +639,27 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1)
BIF_RET(res);
}
+Eterm erts_port_data_read(Port* prt)
+{
+ Eterm res;
+ erts_aint_t data;
+
+ data = erts_atomic_read_ddrb(&prt->data);
+ if (data == (erts_aint_t)NULL)
+ return am_undefined; /* Port terminated by racing thread */
+
+ if ((data & 0x3) != 0) {
+ res = (Eterm) (UWord) data;
+ ASSERT(is_immed(res));
+ }
+ else {
+ ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
+ res = pdhp->data;
+ }
+ return res;
+}
+
+
/*
* Open a port. Most of the work is not done here but rather in
* the file io.c.
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 3ba0886464..1ab1c4a363 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -1753,6 +1753,28 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
BIF_RET(ret);
}
+/*
+** Retrieves the tid() of a named ets table.
+*/
+BIF_RETTYPE ets_whereis_1(BIF_ALIST_1)
+{
+ DbTable* tb;
+ Eterm res;
+
+ if (is_not_atom(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ)) == NULL) {
+ BIF_RET(am_undefined);
+ }
+
+ res = make_tid(BIF_P, tb);
+ db_unlock(tb, LCK_READ);
+
+ BIF_RET(res);
+}
+
/*
** The lookup BIF
*/
@@ -3126,7 +3148,8 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
static Eterm fields[] = {am_protection, am_keypos, am_type, am_named_table,
am_node, am_size, am_name, am_heir, am_owner, am_memory, am_compressed,
am_write_concurrency,
- am_read_concurrency};
+ am_read_concurrency,
+ am_id};
Eterm results[sizeof(fields)/sizeof(Eterm)];
DbTable* tb;
Eterm res;
@@ -4016,7 +4039,10 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
ret = is_table_named(tb) ? am_true : am_false;
} else if (What == am_compressed) {
ret = tb->common.compress ? am_true : am_false;
+ } else if (What == am_id) {
+ ret = make_tid(p, tb);
}
+
/*
* For debugging purposes
*/
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index a5a59c8e74..1c64644efc 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -116,6 +116,7 @@ typedef struct {
static Uint setup_rootset(Process*, Eterm*, int, Rootset*);
static void cleanup_rootset(Rootset *rootset);
static Eterm *full_sweep_heaps(Process *p,
+ ErlHeapFragment *live_hf_end,
int hibernate,
Eterm *n_heap, Eterm* n_htop,
char *oh, Uint oh_size,
@@ -142,7 +143,7 @@ static Eterm* sweep_literal_area(Eterm* n_hp, Eterm* n_htop,
static Eterm* sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
char* src, Uint src_size);
static Eterm* collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
- Eterm* heap, Eterm* htop, Eterm* objv, int nobj);
+ Eterm* htop);
static int adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj);
static void shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj);
static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj);
@@ -917,6 +918,7 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
htop = heap;
htop = full_sweep_heaps(p,
+ ERTS_INVALID_HFRAG_PTR,
1,
heap,
htop,
@@ -1474,18 +1476,22 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
n_htop = n_heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
+ n = setup_rootset(p, objv, nobj, &rootset);
+ roots = rootset.roots;
+
+ /*
+ * All allocations done. Start defile heap with move markers.
+ * A crash dump due to allocation failure above will see a healthy heap.
+ */
+
if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
/*
* Move heap frags that we know are completely live
* directly into the new young heap generation.
*/
- n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop,
- objv, nobj);
+ n_htop = collect_live_heap_frags(p, live_hf_end, n_htop);
}
- n = setup_rootset(p, objv, nobj, &rootset);
- roots = rootset.roots;
-
GENSWEEP_NSTACK(p, old_htop, n_htop);
while (n--) {
Eterm* g_ptr = roots->v;
@@ -1722,16 +1728,8 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
n_htop = n_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
- if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
- /*
- * Move heap frags that we know are completely live
- * directly into the heap.
- */
- n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop,
- objv, nobj);
- }
-
- n_htop = full_sweep_heaps(p, 0, n_heap, n_htop, oh, oh_size, objv, nobj);
+ n_htop = full_sweep_heaps(p, live_hf_end, 0, n_heap, n_htop, oh, oh_size,
+ objv, nobj);
/* Move the stack to the end of the heap */
stk_sz = HEAP_END(p) - p->stop;
@@ -1778,6 +1776,7 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
static Eterm *
full_sweep_heaps(Process *p,
+ ErlHeapFragment *live_hf_end,
int hibernate,
Eterm *n_heap, Eterm* n_htop,
char *oh, Uint oh_size,
@@ -1794,6 +1793,19 @@ full_sweep_heaps(Process *p,
n = setup_rootset(p, objv, nobj, &rootset);
+ /*
+ * All allocations done. Start defile heap with move markers.
+ * A crash dump due to allocation failure above will see a healthy heap.
+ */
+
+ if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
+ /*
+ * Move heap frags that we know are completely live
+ * directly into the heap.
+ */
+ n_htop = collect_live_heap_frags(p, live_hf_end, n_htop);
+ }
+
#ifdef HIPE
if (hibernate)
hipe_empty_nstack(p);
@@ -2296,9 +2308,7 @@ move_one_area(Eterm* n_htop, char* src, Uint src_size)
*/
static Eterm*
-collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
- Eterm* n_hstart, Eterm* n_htop,
- Eterm* objv, int nobj)
+collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end, Eterm* n_htop)
{
ErlHeapFragment* qb;
char* frag_begin;
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 4846ccd2d3..e8048cfdfc 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -1746,6 +1746,7 @@ erl_start(int argc, char **argv)
}
else if (has_prefix("ecio", sub_param)) {
/* ignore argument, eager check io no longer used */
+ arg = get_arg(sub_param+4, argv[i+1], &i);
}
else if (has_prefix("pp", sub_param)) {
arg = get_arg(sub_param+2, argv[i+1], &i);
diff --git a/erts/emulator/beam/erl_io_queue.c b/erts/emulator/beam/erl_io_queue.c
index c93b5248d9..d779d1031a 100644
--- a/erts/emulator/beam/erl_io_queue.c
+++ b/erts/emulator/beam/erl_io_queue.c
@@ -801,12 +801,11 @@ static Eterm iol2v_make_sub_bin(iol2v_state_t *state, Eterm bin_term,
ERTS_GET_REAL_BIN(bin_term, orig_pb_term,
byte_offset, bit_offset, bit_size);
- (void)bit_offset;
- (void)bit_size;
+ ASSERT(bit_size == 0);
sb->thing_word = HEADER_SUB_BIN;
+ sb->bitoffs = bit_offset;
sb->bitsize = 0;
- sb->bitoffs = 0;
sb->orig = orig_pb_term;
sb->is_writable = 0;
@@ -975,7 +974,7 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
parent_header = binary_val(parent_binary);
binary_size = binary_size(bin_term);
- if (bit_offset != 0 || bit_size != 0) {
+ if (bit_size != 0) {
return 0;
} else if (binary_size == 0) {
state->bytereds_spent += 1;
@@ -1017,8 +1016,16 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
* then just copy it into the accumulator. */
iol2v_expand_acc(state, binary_size);
- sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
- binary_data, binary_size);
+ if (ERTS_LIKELY(bit_offset == 0)) {
+ sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
+ binary_data, binary_size);
+ } else {
+ ASSERT(binary_size <= ERTS_UWORD_MAX / 8);
+
+ erts_copy_bits(binary_data, bit_offset, 1,
+ (byte*)&(state->acc)->orig_bytes[state->acc_size], 0, 1,
+ binary_size * 8);
+ }
state->acc_size += binary_size;
} else {
@@ -1029,8 +1036,16 @@ static int iol2v_append_binary(iol2v_state_t *state, Eterm bin_term) {
iol2v_expand_acc(state, spill);
- sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
- binary_data, spill);
+ if (ERTS_LIKELY(bit_offset == 0)) {
+ sys_memcpy(&(state->acc)->orig_bytes[state->acc_size],
+ binary_data, spill);
+ } else {
+ ASSERT(binary_size <= ERTS_UWORD_MAX / 8);
+
+ erts_copy_bits(binary_data, bit_offset, 1,
+ (byte*)&(state->acc)->orig_bytes[state->acc_size], 0, 1,
+ spill * 8);
+ }
state->acc_size += spill;
@@ -1179,7 +1194,10 @@ BIF_RETTYPE iolist_to_iovec_1(BIF_ALIST_1) {
if (is_nil(BIF_ARG_1)) {
BIF_RET(NIL);
} else if (is_binary(BIF_ARG_1)) {
- if (binary_size(BIF_ARG_1) != 0) {
+ if (binary_bitsize(BIF_ARG_1) != 0) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, BADARG);
+ } else if (binary_size(BIF_ARG_1) != 0) {
Eterm *hp = HAlloc(BIF_P, 2);
BIF_RET(CONS(hp, BIF_ARG_1, NIL));
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index abf194cf94..6f7c71ef98 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -616,7 +616,7 @@ erts_try_alloc_message_on_heap(Process *pp,
}
else {
in_message_fragment:
- if (!((*psp) & ERTS_PSFLG_ON_HEAP_MSGQ)) {
+ if ((*psp) & ERTS_PSFLG_OFF_HEAP_MSGQ) {
mp = erts_alloc_message(sz, hpp);
*ohpp = sz == 0 ? NULL : &mp->hfrag.off_heap;
}
@@ -1079,8 +1079,6 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state)
case am_on_heap:
c_p->flags |= F_ON_HEAP_MSGQ;
c_p->flags &= ~F_OFF_HEAP_MSGQ;
- erts_atomic32_read_bor_nob(&c_p->state,
- ERTS_PSFLG_ON_HEAP_MSGQ);
/*
* We are not allowed to clear ERTS_PSFLG_OFF_HEAP_MSGQ
* if a off heap change is ongoing. It will be adjusted
@@ -1106,8 +1104,6 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state)
break;
case am_off_heap:
c_p->flags &= ~F_ON_HEAP_MSGQ;
- erts_atomic32_read_band_nob(&c_p->state,
- ~ERTS_PSFLG_ON_HEAP_MSGQ);
goto change_to_off_heap;
default:
res = THE_NON_VALUE; /* badarg */
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 94ebf88b56..d2000aa71e 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -237,9 +237,11 @@ static void cache_env(ErlNifEnv* env);
static void full_flush_env(ErlNifEnv *env);
static void flush_env(ErlNifEnv* env);
-/* Temporary object header, auto-deallocated when NIF returns
- * or when independent environment is cleared.
- */
+/* Temporary object header, auto-deallocated when NIF returns or when
+ * independent environment is cleared.
+ *
+ * The payload can be accessed with &tmp_obj_ptr[1] but keep in mind that its
+ * first element must not require greater alignment than `next`. */
struct enif_tmp_obj_t {
struct enif_tmp_obj_t* next;
void (*dtor)(struct enif_tmp_obj_t*);
@@ -256,6 +258,46 @@ static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env)
}
}
+/* Whether the given environment is bound to a process and will be cleaned up
+ * when the NIF returns. It's safe to use temp_alloc for objects in
+ * env->tmp_obj_list when this is true. */
+static ERTS_INLINE int is_proc_bound(ErlNifEnv *env)
+{
+ return env->mod_nif != NULL;
+}
+
+/* Allocates and attaches an object to the given environment, running its
+ * destructor when the environment is cleared. To avoid temporary variables the
+ * address of the allocated object is returned instead of the enif_tmp_obj_t.
+ *
+ * The destructor *must* call `erts_free(tmp_obj->allocator, tmp_obj)` to free
+ * the object. If the destructor needs to refer to the allocated object its
+ * address will be &tmp_obj[1]. */
+static ERTS_INLINE void *alloc_tmp_obj(ErlNifEnv *env, size_t size,
+ void (*dtor)(struct enif_tmp_obj_t*)) {
+ struct enif_tmp_obj_t *tmp_obj;
+ ErtsAlcType_t allocator;
+
+ allocator = is_proc_bound(env) ? ERTS_ALC_T_TMP : ERTS_ALC_T_NIF;
+
+ tmp_obj = erts_alloc(allocator, sizeof(struct enif_tmp_obj_t) + MAX(1, size));
+
+ tmp_obj->next = env->tmp_obj_list;
+ tmp_obj->allocator = allocator;
+ tmp_obj->dtor = dtor;
+
+ env->tmp_obj_list = tmp_obj;
+
+ return (void*)&tmp_obj[1];
+}
+
+/* Generic destructor for objects allocated through alloc_tmp_obj that don't
+ * care about their payload. */
+static void tmp_alloc_dtor(struct enif_tmp_obj_t *tmp_obj)
+{
+ erts_free(tmp_obj->allocator, tmp_obj);
+}
+
void erts_post_nif(ErlNifEnv* env)
{
erts_unblock_fpe(env->fpe_was_unmasked);
@@ -446,6 +488,7 @@ static void cache_env(ErlNifEnv* env)
env->hp_end = env->heap_frag->mem + env->heap_frag->alloc_size;
}
}
+
void* enif_priv_data(ErlNifEnv* env)
{
return env->mod_nif->priv_data;
@@ -1019,11 +1062,6 @@ int enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)
return is_number(term);
}
-static ERTS_INLINE int is_proc_bound(ErlNifEnv* env)
-{
- return env->mod_nif != NULL;
-}
-
static void aligned_binary_dtor(struct enif_tmp_obj_t* obj)
{
erts_free_aligned_binary_bytes_extra((byte*)obj, obj->allocator);
@@ -1058,22 +1096,14 @@ int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin)
u.tmp->dtor = &aligned_binary_dtor;
env->tmp_obj_list = u.tmp;
}
- bin->bin_term = bin_term;
bin->size = binary_size(bin_term);
bin->ref_bin = NULL;
ADD_READONLY_CHECK(env, bin->data, bin->size);
return 1;
}
-static void tmp_alloc_dtor(struct enif_tmp_obj_t* obj)
-{
- erts_free(obj->allocator, obj);
-}
-
int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
{
- struct enif_tmp_obj_t* tobj;
- ErtsAlcType_t allocator;
ErlDrvSizeT sz;
if (is_binary(term)) {
return enif_inspect_binary(env,term,bin);
@@ -1081,7 +1111,6 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
if (is_nil(term)) {
bin->data = (unsigned char*) &bin->data; /* dummy non-NULL */
bin->size = 0;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
return 1;
}
@@ -1089,16 +1118,8 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
return 0;
}
- allocator = is_proc_bound(env) ? ERTS_ALC_T_TMP : ERTS_ALC_T_NIF;
- tobj = erts_alloc(allocator, sz + sizeof(struct enif_tmp_obj_t));
- tobj->allocator = allocator;
- tobj->next = env->tmp_obj_list;
- tobj->dtor = &tmp_alloc_dtor;
- env->tmp_obj_list = tobj;
-
- bin->data = (unsigned char*) &tobj[1];
+ bin->data = alloc_tmp_obj(env, sz, &tmp_alloc_dtor);
bin->size = sz;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
erts_iolist_to_buf(term, (char*) bin->data, sz);
ADD_READONLY_CHECK(env, bin->data, bin->size);
@@ -1116,7 +1137,6 @@ int enif_alloc_binary(size_t size, ErlNifBinary* bin)
bin->size = size;
bin->data = (unsigned char*) refbin->orig_bytes;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = refbin;
return 1;
}
@@ -1150,12 +1170,10 @@ void enif_release_binary(ErlNifBinary* bin)
{
if (bin->ref_bin != NULL) {
Binary* refbin = bin->ref_bin;
- ASSERT(bin->bin_term == THE_NON_VALUE);
erts_bin_release(refbin);
}
#ifdef DEBUG
bin->data = NULL;
- bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
#endif
}
@@ -1312,29 +1330,51 @@ int enif_get_string(ErlNifEnv *env, ERL_NIF_TERM list, char* buf, unsigned len,
Eterm enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)
{
- if (bin->bin_term != THE_NON_VALUE) {
- return bin->bin_term;
- }
- else if (bin->ref_bin != NULL) {
- Binary* bptr = bin->ref_bin;
- Eterm bin_term;
-
- bin_term = erts_build_proc_bin(&MSO(env->proc),
- alloc_heap(env, PROC_BIN_SIZE),
- bptr);
- if (erts_refc_read(&bptr->intern.refc, 1) == 1) {
- /* Total ownership transfer */
- bin->ref_bin = NULL;
- bin->bin_term = bin_term;
- }
- return bin_term;
- }
- else {
- flush_env(env);
- bin->bin_term = new_binary(env->proc, bin->data, bin->size);
- cache_env(env);
- return bin->bin_term;
+ Eterm bin_term;
+
+ if (bin->ref_bin != NULL) {
+ Binary* binary = bin->ref_bin;
+
+ /* If the binary is smaller than the heap binary limit we'll return a
+ * heap binary to reduce the number of small refc binaries in the
+ * system. We can't simply release the refc binary right away however;
+ * the documentation states that the binary should be considered
+ * read-only from this point on, which implies that it should still be
+ * readable.
+ *
+ * We could keep it alive until we return by adding it to the temporary
+ * object list, but that requires an off-heap allocation which is
+ * potentially quite slow, so we create a dummy ProcBin instead and
+ * rely on the next minor GC to get rid of it. */
+ if (bin->size <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin* hb;
+
+ hb = (ErlHeapBin*)alloc_heap(env, heap_bin_size(bin->size));
+ hb->thing_word = header_heap_bin(bin->size);
+ hb->size = bin->size;
+
+ sys_memcpy(hb->data, bin->data, bin->size);
+
+ erts_build_proc_bin(&MSO(env->proc),
+ alloc_heap(env, PROC_BIN_SIZE),
+ binary);
+
+ bin_term = make_binary(hb);
+ } else {
+ bin_term = erts_build_proc_bin(&MSO(env->proc),
+ alloc_heap(env, PROC_BIN_SIZE),
+ binary);
+ }
+
+ /* Our (possibly shared) ownership has been transferred to the term. */
+ bin->ref_bin = NULL;
+ } else {
+ flush_env(env);
+ bin_term = new_binary(env->proc, bin->data, bin->size);
+ cache_env(env);
}
+
+ return bin_term;
}
Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term,
@@ -3284,8 +3324,8 @@ typedef struct {
Eterm sublist_start;
Eterm sublist_end;
- UWord offheap_size;
- UWord onheap_size;
+ UWord referenced_size;
+ UWord copied_size;
UWord iovec_len;
} iovec_slice_t;
@@ -3295,16 +3335,16 @@ static int examine_iovec_term(Eterm list, UWord max_length, iovec_slice_t *resul
result->sublist_start = list;
result->sublist_length = 0;
- result->offheap_size = 0;
- result->onheap_size = 0;
+ result->referenced_size = 0;
+ result->copied_size = 0;
result->iovec_len = 0;
lookahead = result->sublist_start;
while (is_list(lookahead)) {
- Eterm *binary_header, binary;
+ UWord byte_size;
+ Eterm binary;
Eterm *cell;
- UWord size;
cell = list_val(lookahead);
binary = CAR(cell);
@@ -3313,35 +3353,36 @@ static int examine_iovec_term(Eterm list, UWord max_length, iovec_slice_t *resul
return 0;
}
- size = binary_size(binary);
- binary_header = binary_val(binary);
+ byte_size = binary_size(binary);
+
+ if (byte_size > 0) {
+ int bit_offset, bit_size;
+ Eterm parent_binary;
+ UWord byte_offset;
- if (size > 0) {
- /* If we're a sub-binary we'll need to check our underlying binary
- * to determine whether we're on-heap or not. */
- if (thing_subtag(*binary_header) == SUB_BINARY_SUBTAG) {
- ErlSubBin *sb = (ErlSubBin*)binary_header;
+ int requires_copying;
- /* Reject bitstrings */
- if((sb->bitoffs + sb->bitsize) > 0) {
- return 0;
- }
+ ERTS_GET_REAL_BIN(binary, parent_binary, byte_offset,
+ bit_offset, bit_size);
- ASSERT(size <= binary_size(sb->orig));
- binary_header = binary_val(sb->orig);
+ (void)byte_offset;
+
+ if (bit_size != 0) {
+ return 0;
}
- if (thing_subtag(*binary_header) == HEAP_BINARY_SUBTAG) {
- ASSERT(size <= ERL_ONHEAP_BIN_LIMIT);
+ /* If we're unaligned or an on-heap binary we'll need to copy
+ * ourselves over to a temporary buffer. */
+ requires_copying = (bit_offset != 0) ||
+ thing_subtag(*binary_val(parent_binary)) == HEAP_BINARY_SUBTAG;
- result->iovec_len += 1;
- result->onheap_size += size;
+ if (requires_copying) {
+ result->copied_size += byte_size;
} else {
- ASSERT(thing_subtag(*binary_header) == REFC_BINARY_SUBTAG);
-
- result->iovec_len += 1 + size / MAX_SYSIOVEC_IOVLEN;
- result->offheap_size += size;
+ result->referenced_size += byte_size;
}
+
+ result->iovec_len += 1 + byte_size / MAX_SYSIOVEC_IOVLEN;
}
result->sublist_length += 1;
@@ -3361,7 +3402,9 @@ static int examine_iovec_term(Eterm list, UWord max_length, iovec_slice_t *resul
return 1;
}
-static void inspect_raw_binary_data(Eterm binary, ErlNifBinary *result) {
+static void marshal_iovec_binary(Eterm binary, ErlNifBinary *copy_buffer,
+ UWord *copy_offset, ErlNifBinary *result) {
+
Eterm *parent_header;
Eterm parent_binary;
@@ -3372,10 +3415,11 @@ static void inspect_raw_binary_data(Eterm binary, ErlNifBinary *result) {
ERTS_GET_REAL_BIN(binary, parent_binary, byte_offset, bit_offset, bit_size);
+ ASSERT(bit_size == 0);
+
parent_header = binary_val(parent_binary);
result->size = binary_size(binary);
- result->bin_term = binary;
if (thing_subtag(*parent_header) == REFC_BINARY_SUBTAG) {
ProcBin *pb = (ProcBin*)parent_header;
@@ -3398,24 +3442,48 @@ static void inspect_raw_binary_data(Eterm binary, ErlNifBinary *result) {
result->data = &((unsigned char*)&hb->data)[byte_offset];
result->ref_bin = NULL;
}
+
+ /* If this isn't an *aligned* refc binary, copy its contents to the buffer
+ * and reference that instead. */
+
+ if (result->ref_bin == NULL || bit_offset != 0) {
+ ASSERT(copy_buffer->ref_bin != NULL && copy_buffer->data != NULL);
+ ASSERT(result->size <= (copy_buffer->size - *copy_offset));
+
+ if (bit_offset == 0) {
+ sys_memcpy(&copy_buffer->data[*copy_offset],
+ result->data, result->size);
+ } else {
+ erts_copy_bits(result->data, bit_offset, 1,
+ (byte*)&copy_buffer->data[*copy_offset], 0, 1,
+ result->size * 8);
+ }
+
+ result->data = &copy_buffer->data[*copy_offset];
+ result->ref_bin = copy_buffer->ref_bin;
+
+ *copy_offset += result->size;
+ }
}
static int fill_iovec_with_slice(ErlNifEnv *env,
iovec_slice_t *slice,
ErlNifIOVec *iovec) {
- UWord onheap_offset, iovec_idx;
- ErlNifBinary onheap_data;
+ ErlNifBinary copy_buffer = {0};
+ UWord copy_offset, iovec_idx;
Eterm sublist_iterator;
- /* Set up a common refc binary for all on-heap binaries. */
- if (slice->onheap_size > 0) {
- if (!enif_alloc_binary(slice->onheap_size, &onheap_data)) {
+ /* Set up a common refc binary for all on-heap and unaligned binaries. */
+ if (slice->copied_size > 0) {
+ if (!enif_alloc_binary(slice->copied_size, &copy_buffer)) {
return 0;
}
+
+ ASSERT(copy_buffer.ref_bin != NULL);
}
sublist_iterator = slice->sublist_start;
- onheap_offset = 0;
+ copy_offset = 0;
iovec_idx = 0;
while (sublist_iterator != slice->sublist_end) {
@@ -3423,20 +3491,7 @@ static int fill_iovec_with_slice(ErlNifEnv *env,
Eterm *cell;
cell = list_val(sublist_iterator);
- inspect_raw_binary_data(CAR(cell), &raw_data);
-
- /* If this isn't a refc binary, copy its contents to the onheap buffer
- * and reference that instead. */
- if (raw_data.size > 0 && raw_data.ref_bin == NULL) {
- ASSERT(onheap_offset < onheap_data.size);
- ASSERT(slice->onheap_size > 0);
-
- sys_memcpy(&onheap_data.data[onheap_offset],
- raw_data.data, raw_data.size);
-
- raw_data.data = &onheap_data.data[onheap_offset];
- raw_data.ref_bin = onheap_data.ref_bin;
- }
+ marshal_iovec_binary(CAR(cell), &copy_buffer, &copy_offset, &raw_data);
while (raw_data.size > 0) {
UWord chunk_len = MIN(raw_data.size, MAX_SYSIOVEC_IOVLEN);
@@ -3467,16 +3522,18 @@ static int fill_iovec_with_slice(ErlNifEnv *env,
erts_refc_inc(&refc_binary->intern.refc, 1);
}
- if (slice->onheap_size > 0) {
+ if (slice->copied_size > 0) {
/* Transfer ownership to the iovec; we've taken references to it in
* the above loop. */
- enif_release_binary(&onheap_data);
+ enif_release_binary(&copy_buffer);
}
} else {
- if (slice->onheap_size > 0) {
- /* Attach the binary to our environment and let the GC take care of
- * it after returning. */
- enif_make_binary(env, &onheap_data);
+ if (slice->copied_size > 0) {
+ /* Attach the binary to our environment and let the next minor GC
+ * get rid of it. This is slightly faster than using the tmp object
+ * list since it avoids off-heap allocations. */
+ erts_build_proc_bin(&MSO(env->proc),
+ alloc_heap(env, PROC_BIN_SIZE), copy_buffer.ref_bin);
}
}
@@ -3502,19 +3559,14 @@ static int create_iovec_from_slice(ErlNifEnv *env,
alloc_size = binv_offset;
alloc_size += slice->iovec_len * sizeof(Binary*);
- /* If we have an environment we'll attach the allocated data to it. The
- * GC will take care of releasing it later on. */
+ /* When the user passes an environment, we attach the iovec to it so
+ * the user won't have to bother managing it (similar to
+ * enif_inspect_binary). It'll disappear once the environment is
+ * cleaned up. */
if (env != NULL) {
- ErlNifBinary gc_bin;
-
- if (!enif_alloc_binary(alloc_size, &gc_bin)) {
- return 0;
- }
-
- alloc_base = (char*)gc_bin.data;
- enif_make_binary(env, &gc_bin);
+ alloc_base = alloc_tmp_obj(env, alloc_size, &tmp_alloc_dtor);
} else {
- alloc_base = enif_alloc(alloc_size);
+ alloc_base = erts_alloc(ERTS_ALC_T_NIF, alloc_size);
}
iovec = (ErlNifIOVec*)alloc_base;
@@ -3523,12 +3575,12 @@ static int create_iovec_from_slice(ErlNifEnv *env,
iovec->flags = 0;
}
- iovec->size = slice->offheap_size + slice->onheap_size;
+ iovec->size = slice->referenced_size + slice->copied_size;
iovec->iovcnt = slice->iovec_len;
if(!fill_iovec_with_slice(env, slice, iovec)) {
if (env == NULL && !(iovec->flags & ERL_NIF_IOVEC_FLAGS_PREALLOC)) {
- enif_free(iovec);
+ erts_free(ERTS_ALC_T_NIF, iovec);
}
return 0;
@@ -4218,34 +4270,31 @@ static unsigned calc_checksum(unsigned char* ptr, unsigned size);
struct readonly_check_t
{
- struct enif_tmp_obj_t hdr;
unsigned char* ptr;
unsigned size;
unsigned checksum;
};
static void add_readonly_check(ErlNifEnv* env, unsigned char* ptr, unsigned sz)
{
- ErtsAlcType_t allocator = is_proc_bound(env) ? ERTS_ALC_T_TMP : ERTS_ALC_T_NIF;
- struct readonly_check_t* obj = erts_alloc(allocator,
- sizeof(struct readonly_check_t));
- obj->hdr.allocator = allocator;
- obj->hdr.next = env->tmp_obj_list;
- env->tmp_obj_list = &obj->hdr;
- obj->hdr.dtor = &readonly_check_dtor;
+ struct readonly_check_t* obj;
+
+ obj = alloc_tmp_obj(env, sizeof(struct readonly_check_t),
+ &readonly_check_dtor);
+
obj->ptr = ptr;
obj->size = sz;
- obj->checksum = calc_checksum(ptr, sz);
+ obj->checksum = calc_checksum(ptr, sz);
}
-static void readonly_check_dtor(struct enif_tmp_obj_t* o)
+static void readonly_check_dtor(struct enif_tmp_obj_t* tmp_obj)
{
- struct readonly_check_t* obj = (struct readonly_check_t*) o;
- unsigned chksum = calc_checksum(obj->ptr, obj->size);
- if (chksum != obj->checksum) {
+ struct readonly_check_t* ro_check = (struct readonly_check_t*)&tmp_obj[1];
+ unsigned chksum = calc_checksum(ro_check->ptr, ro_check->size);
+ if (chksum != ro_check->checksum) {
fprintf(stderr, "\r\nReadonly data written by NIF, checksums differ"
- " %x != %x\r\nABORTING\r\n", chksum, obj->checksum);
+ " %x != %x\r\nABORTING\r\n", chksum, ro_check->checksum);
abort();
}
- erts_free(obj->hdr.allocator, obj);
+ erts_free(tmp_obj->allocator, tmp_obj);
}
static unsigned calc_checksum(unsigned char* ptr, unsigned size)
{
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index 053f7673c4..a99b4db705 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -137,8 +137,9 @@ typedef struct
unsigned char* data;
/* Internals (avert your eyes) */
- ERL_NIF_TERM bin_term;
void* ref_bin;
+ /* for future additions to be ABI compatible (same struct size) */
+ void* __spare__[2];
}ErlNifBinary;
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h
index 9117eb1f72..0d148ee048 100644
--- a/erts/emulator/beam/erl_port.h
+++ b/erts/emulator/beam/erl_port.h
@@ -180,6 +180,7 @@ void erts_init_port_data(Port *);
void erts_cleanup_port_data(Port *);
Uint erts_port_data_size(Port *);
ErlOffHeap *erts_port_data_offheap(Port *);
+Eterm erts_port_data_read(Port* prt);
#define ERTS_PORT_GET_CONNECTED(PRT) \
((Eterm) erts_atomic_read_nob(&(PRT)->connected))
@@ -195,26 +196,52 @@ struct erl_drv_port_data_lock {
Port *prt;
};
+ERTS_GLB_INLINE void erts_init_runq_port(Port *prt, ErtsRunQueue *runq);
+ERTS_GLB_INLINE void erts_set_runq_port(Port *prt, ErtsRunQueue *runq);
+ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_port(Port *prt);
ERTS_GLB_INLINE ErtsRunQueue *erts_port_runq(Port *prt);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE void
+erts_init_runq_port(Port *prt, ErtsRunQueue *runq)
+{
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ erts_atomic_init_nob(&prt->run_queue, (erts_aint_t) runq);
+}
+
+ERTS_GLB_INLINE void
+erts_set_runq_port(Port *prt, ErtsRunQueue *runq)
+{
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ erts_atomic_set_nob(&prt->run_queue, (erts_aint_t) runq);
+}
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_get_runq_port(Port *prt)
+{
+ ErtsRunQueue *runq;
+ runq = (ErtsRunQueue *) erts_atomic_read_nob(&prt->run_queue);
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ return runq;
+}
+
+
ERTS_GLB_INLINE ErtsRunQueue *
erts_port_runq(Port *prt)
{
ErtsRunQueue *rq1, *rq2;
- rq1 = (ErtsRunQueue *) erts_atomic_read_nob(&prt->run_queue);
- if (!rq1)
- return NULL;
+ rq1 = erts_get_runq_port(prt);
while (1) {
erts_runq_lock(rq1);
- rq2 = (ErtsRunQueue *) erts_atomic_read_nob(&prt->run_queue);
+ rq2 = erts_get_runq_port(prt);
if (rq1 == rq2)
return rq1;
erts_runq_unlock(rq1);
rq1 = rq2;
- if (!rq1)
- return NULL;
}
}
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index a588477320..4a3671df0c 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -84,11 +84,10 @@ static void chk_task_queues(Port *pp, ErtsPortTask *execq, int processing_busy_q
#define LTTNG_DRIVER(TRACEPOINT, PP) do {} while(0)
#endif
-#define ERTS_LC_VERIFY_RQ(RQ, PP) \
- do { \
+#define ERTS_LC_VERIFY_RQ(RQ, PP) \
+ do { \
ERTS_LC_ASSERT(erts_lc_runq_is_locked(runq)); \
- ERTS_LC_ASSERT((RQ) == ((ErtsRunQueue *) \
- erts_atomic_read_nob(&(PP)->run_queue))); \
+ ERTS_LC_ASSERT((RQ) == erts_get_runq_port((PP))); \
} while (0)
#define ERTS_PT_STATE_SCHEDULED 0
@@ -1520,19 +1519,15 @@ erts_port_task_schedule(Eterm id,
/* Enqueue port on run-queue */
runq = erts_port_runq(pp);
- if (!runq)
- ERTS_INTERNAL_ERROR("Missing run-queue");
xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL);
ERTS_LC_ASSERT(runq != xrunq);
ERTS_LC_VERIFY_RQ(runq, pp);
if (xrunq) {
/* Emigrate port ... */
- erts_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
+ erts_set_runq_port(pp, xrunq);
erts_runq_unlock(runq);
runq = erts_port_runq(pp);
- if (!runq)
- ERTS_INTERNAL_ERROR("Missing run-queue");
}
enqueue_port(runq, pp);
@@ -1593,8 +1588,6 @@ erts_port_task_free_port(Port *pp)
ASSERT(!(erts_atomic32_read_nob(&pp->state) & ERTS_PORT_SFLGS_DEAD));
runq = erts_port_runq(pp);
- if (!runq)
- ERTS_INTERNAL_ERROR("Missing run-queue");
erts_port_task_sched_lock(&pp->sched);
flags = erts_atomic32_read_bor_relb(&pp->sched.flags,
ERTS_PTS_FLG_EXIT);
@@ -1805,7 +1798,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
erts_unblock_fpe(fpe_was_unmasked);
ERTS_MSACC_POP_STATE_M();
- ASSERT(runq == (ErtsRunQueue *) erts_atomic_read_nob(&pp->run_queue));
+ ASSERT(runq == erts_get_runq_port(pp));
active = finalize_exec(pp, &execq, processing_busy_q);
@@ -1831,11 +1824,10 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
}
else {
/* Emigrate port... */
- erts_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
+ erts_set_runq_port(pp, xrunq);
erts_runq_unlock(runq);
xrunq = erts_port_runq(pp);
- ASSERT(xrunq);
enqueue_port(xrunq, pp);
erts_runq_unlock(xrunq);
erts_notify_inc_runq(xrunq);
@@ -2069,7 +2061,7 @@ void
erts_enqueue_port(ErtsRunQueue *rq, Port *pp)
{
ERTS_LC_ASSERT(erts_lc_runq_is_locked(rq));
- ASSERT(rq == (ErtsRunQueue *) erts_atomic_read_nob(&pp->run_queue));
+ ASSERT(rq == erts_get_runq_port(pp));
ASSERT(erts_atomic32_read_nob(&pp->sched.flags) & ERTS_PTS_FLG_IN_RUNQ);
enqueue_port(rq, pp);
}
@@ -2080,8 +2072,7 @@ erts_dequeue_port(ErtsRunQueue *rq)
Port *pp;
ERTS_LC_ASSERT(erts_lc_runq_is_locked(rq));
pp = pop_port(rq);
- ASSERT(!pp
- || rq == (ErtsRunQueue *) erts_atomic_read_nob(&pp->run_queue));
+ ASSERT(!pp || rq == erts_get_runq_port(pp));
ASSERT(!pp || (erts_atomic32_read_nob(&pp->sched.flags)
& ERTS_PTS_FLG_IN_RUNQ));
return pp;
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index a807d60ec7..b19659f496 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -137,36 +137,6 @@ runq_got_work_to_execute(ErtsRunQueue *rq)
return runq_got_work_to_execute_flags(ERTS_RUNQ_FLGS_GET_NOB(rq));
}
-#undef RUNQ_READ_RQ
-#undef RUNQ_SET_RQ
-#define RUNQ_READ_RQ(X) ((ErtsRunQueue *) erts_atomic_read_nob((X)))
-#define RUNQ_SET_RQ(X, RQ) erts_atomic_set_nob((X), (erts_aint_t) (RQ))
-
-#ifdef DEBUG
-# if defined(ARCH_64)
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
- (RUNQ_SET_RQ((RQP), (0xdeadbeefdead0003LL | ((N) << 4)))
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
-do { \
- ASSERT((RQP) != NULL); \
- ASSERT(((((Uint) (RQP)) & ((Uint) 0x3))) == ((Uint) 0)); \
- ASSERT((((Uint) (RQP)) & ~((Uint) 0xffff)) != ((Uint) 0xdeadbeefdead0000LL));\
-} while (0)
-# else
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
- (RUNQ_SET_RQ((RQP), (0xdead0003 | ((N) << 4))))
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
-do { \
- ASSERT((RQP) != NULL); \
- ASSERT(((((UWord) (RQP)) & ((UWord) 1))) == ((UWord) 0)); \
- ASSERT((((UWord) (RQP)) & ~((UWord) 0xffff)) != ((UWord) 0xdead0000)); \
-} while (0)
-# endif
-#else
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N)
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP)
-#endif
-
const Process erts_invalid_process = {{ERTS_INVALID_PID}};
extern BeamInstr beam_apply[];
@@ -1538,6 +1508,20 @@ erts_proclist_destroy(ErtsProcList *plp)
proclist_destroy(plp);
}
+void
+erts_proclist_dump(fmtfn_t to, void *to_arg, ErtsProcList *plp)
+{
+ ErtsProcList *first = plp;
+
+ while (plp) {
+ erts_print(to, to_arg, "%T", plp->pid);
+ plp = plp->next;
+ if (plp == first)
+ break;
+ }
+ erts_print(to, to_arg, "\n");
+}
+
void *
erts_psd_set_init(Process *p, int ix, void *data)
{
@@ -3926,21 +3910,16 @@ immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp)
Port *prt;
prt = erts_dequeue_port(rq);
if (prt)
- RUNQ_SET_RQ(&prt->run_queue, c_rq);
+ erts_set_runq_port(prt, c_rq);
erts_runq_unlock(rq);
if (prt) {
- /* port might terminate while we have no lock... */
rq = erts_port_runq(prt);
- if (rq) {
- if (rq != c_rq)
- erts_exit(ERTS_ABORT_EXIT,
- "%s:%d:%s(): Internal error",
- __FILE__, __LINE__, __func__);
- erts_enqueue_port(c_rq, prt);
- if (!iflag)
- return; /* done */
- erts_runq_unlock(c_rq);
- }
+ if (rq != c_rq)
+ ERTS_INTERNAL_ERROR("Unexpected run-queue");
+ erts_enqueue_port(c_rq, prt);
+ if (!iflag)
+ return; /* done */
+ erts_runq_unlock(c_rq);
}
}
else {
@@ -3954,12 +3933,11 @@ immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp)
while (proc) {
erts_aint32_t state;
state = erts_atomic32_read_acqb(&proc->state);
- if (!(ERTS_PSFLG_BOUND & state)
- && (prio == (int) ERTS_PSFLGS_GET_PRQ_PRIO(state))) {
+ if (prio == (int) ERTS_PSFLGS_GET_PRQ_PRIO(state)
+ && erts_try_change_runq_proc(proc, c_rq)) {
ErtsRunQueueInfo *rqi = &rq->procs.prio_info[prio];
unqueue_process(rq, rpq, rqi, prio, prev_proc, proc);
erts_runq_unlock(rq);
- RUNQ_SET_RQ(&proc->run_queue, c_rq);
rq_locked = 0;
erts_runq_lock(c_rq);
@@ -4140,21 +4118,13 @@ evacuate_run_queue(ErtsRunQueue *rq,
while (prt) {
ErtsRunQueue *prt_rq;
prt = erts_dequeue_port(rq);
- RUNQ_SET_RQ(&prt->run_queue, to_rq);
+ erts_set_runq_port(prt, to_rq);
erts_runq_unlock(rq);
- /*
- * The port might terminate while
- * we have no lock on it...
- */
prt_rq = erts_port_runq(prt);
- if (prt_rq) {
- if (prt_rq != to_rq)
- erts_exit(ERTS_ABORT_EXIT,
- "%s:%d:%s() internal error\n",
- __FILE__, __LINE__, __func__);
- erts_enqueue_port(to_rq, prt);
- erts_runq_unlock(to_rq);
- }
+ if (prt_rq != to_rq)
+ ERTS_INTERNAL_ERROR("Unexpected run-queue");
+ erts_enqueue_port(to_rq, prt);
+ erts_runq_unlock(to_rq);
erts_runq_lock(rq);
prt = rq->ports.start;
}
@@ -4177,14 +4147,13 @@ evacuate_run_queue(ErtsRunQueue *rq,
while (proc) {
Process *real_proc;
int prio;
- erts_aint32_t max_qbit, qbit, real_state;
+ erts_aint32_t max_qbit, qbit;
prio = ERTS_PSFLGS_GET_PRQ_PRIO(state);
qbit = ((erts_aint32_t) 1) << prio;
if (!(state & ERTS_PSFLG_PROXY)) {
real_proc = proc;
- real_state = state;
}
else {
real_proc = erts_proc_lookup_raw(proc->common.id);
@@ -4192,7 +4161,6 @@ evacuate_run_queue(ErtsRunQueue *rq,
free_proxy_proc(proc);
goto handle_next_proc;
}
- real_state = erts_atomic32_read_acqb(&real_proc->state);
}
max_qbit = (state >> ERTS_PSFLGS_IN_PRQ_MASK_OFFSET);
@@ -4227,7 +4195,7 @@ evacuate_run_queue(ErtsRunQueue *rq,
goto handle_next_proc;
}
- if (ERTS_PSFLG_BOUND & real_state) {
+ if (!erts_try_change_runq_proc(proc, to_rq)) {
/* Bound processes get stuck here... */
proc->next = NULL;
if (sbpp->last)
@@ -4241,7 +4209,6 @@ evacuate_run_queue(ErtsRunQueue *rq,
erts_runq_unlock(rq);
to_rq = mp->prio[prio].runq;
- RUNQ_SET_RQ(&proc->run_queue, to_rq);
erts_runq_lock(to_rq);
enqueue_process(to_rq, prio, proc);
@@ -4309,14 +4276,13 @@ try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq,
proc = rpq->first;
while (proc) {
- erts_aint32_t state = erts_atomic32_read_acqb(&proc->state);
- if (!(ERTS_PSFLG_BOUND & state)) {
+ if (erts_try_change_runq_proc(proc, rq)) {
+ erts_aint32_t state = erts_atomic32_read_acqb(&proc->state);
/* Steal process */
int prio = (int) ERTS_PSFLGS_GET_PRQ_PRIO(state);
ErtsRunQueueInfo *rqi = &vrq->procs.prio_info[prio];
unqueue_process(vrq, rpq, rqi, prio, prev_proc, proc);
erts_runq_unlock(vrq);
- RUNQ_SET_RQ(&proc->run_queue, rq);
erts_runq_lock(rq);
*rq_lockedp = 1;
@@ -4341,26 +4307,14 @@ no_procs:
if (vrq->ports.start) {
ErtsRunQueue *prt_rq;
Port *prt = erts_dequeue_port(vrq);
- RUNQ_SET_RQ(&prt->run_queue, rq);
+ erts_set_runq_port(prt, rq);
erts_runq_unlock(vrq);
-
- /*
- * The port might terminate while
- * we have no lock on it...
- */
-
prt_rq = erts_port_runq(prt);
- if (!prt_rq)
- return 0;
- else {
- if (prt_rq != rq)
- erts_exit(ERTS_ABORT_EXIT,
- "%s:%d:%s() internal error\n",
- __FILE__, __LINE__, __func__);
- *rq_lockedp = 1;
- erts_enqueue_port(rq, prt);
- return !0;
- }
+ if (prt_rq != rq)
+ ERTS_INTERNAL_ERROR("Unexpected run-queue");
+ *rq_lockedp = 1;
+ erts_enqueue_port(rq, prt);
+ return !0;
}
erts_runq_unlock(vrq);
@@ -6116,7 +6070,8 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio)
{
erts_aint32_t state;
Process *proxy;
- ErtsRunQueue *rq = RUNQ_READ_RQ(&proc->run_queue);
+ int bound;
+ ErtsRunQueue *rq = erts_get_runq_proc(proc, &bound);
state = (ERTS_PSFLG_PROXY
| ERTS_PSFLG_IN_RUNQ
@@ -6129,7 +6084,7 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio)
proxy = prev_proxy;
ASSERT(erts_atomic32_read_nob(&proxy->state) & ERTS_PSFLG_PROXY);
erts_atomic32_set_nob(&proxy->state, state);
- RUNQ_SET_RQ(&proc->run_queue, rq);
+ (void) erts_set_runq_proc(proc, rq, &bound);
}
else {
proxy = erts_alloc(ERTS_ALC_T_PROC, sizeof(Process));
@@ -6142,8 +6097,7 @@ make_proxy_proc(Process *prev_proxy, Process *proc, erts_aint32_t prio)
}
#endif
erts_atomic32_init_nob(&proxy->state, state);
- erts_atomic_init_nob(&proxy->run_queue,
- erts_atomic_read_nob(&proc->run_queue));
+ erts_init_runq_proc(proc, rq, bound);
}
proxy->common.id = proc->common.id;
@@ -6334,18 +6288,21 @@ select_enqueue_run_queue(int enqueue, int enq_prio, Process *p, erts_aint32_t st
default: {
ErtsRunQueue* runq;
+ int bound;
ASSERT(enqueue == ERTS_ENQUEUE_NORMAL_QUEUE
|| enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE);
- runq = erts_get_runq_proc(p);
+ runq = erts_get_runq_proc(p, &bound);
- if (!(ERTS_PSFLG_BOUND & state)) {
+ if (!bound) {
ErtsRunQueue *new_runq = erts_check_emigration_need(runq, enq_prio);
- if (new_runq) {
- RUNQ_SET_RQ(&p->run_queue, new_runq);
- runq = new_runq;
- }
+ if (new_runq) {
+ if (erts_try_change_runq_proc(p, new_runq))
+ runq = new_runq;
+ else
+ runq = erts_get_runq_proc(p, NULL);
+ }
}
ASSERT(runq);
@@ -11462,6 +11419,7 @@ typedef struct {
Process *proc;
erts_aint32_t state;
ErtsRunQueue *run_queue;
+ int bound;
} ErtsEarlyProcInit;
static void early_init_process_struct(void *varg, Eterm data)
@@ -11472,10 +11430,9 @@ static void early_init_process_struct(void *varg, Eterm data)
proc->common.id = make_internal_pid(data);
erts_atomic32_init_nob(&proc->dirty_state, 0);
proc->dirty_sys_tasks = NULL;
+ erts_init_runq_proc(proc, arg->run_queue, arg->bound);
erts_atomic32_init_relb(&proc->state, arg->state);
- RUNQ_SET_RQ(&proc->run_queue, arg->run_queue);
-
erts_proc_lock_init(proc); /* All locks locked */
}
@@ -11484,7 +11441,7 @@ static void early_init_process_struct(void *varg, Eterm data)
** Allocate process and find out where to place next process.
*/
static Process*
-alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
+alloc_process(ErtsRunQueue *rq, int bound, erts_aint32_t state)
{
ErtsEarlyProcInit init_arg;
Process *p;
@@ -11493,9 +11450,12 @@ alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
if (!p)
return NULL;
+ ASSERT(rq);
+
init_arg.proc = (Process *) p;
- init_arg.run_queue = rq;
init_arg.state = state;
+ init_arg.run_queue = rq;
+ init_arg.bound = bound;
ERTS_CT_ASSERT(offsetof(Process,common) == 0);
@@ -11530,6 +11490,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
Eterm args, /* Arguments for function (must be well-formed list). */
ErlSpawnOpts* so) /* Options for spawn. */
{
+ int bound = 0;
Uint flags = 0;
ErtsRunQueue *rq = NULL;
Process *p;
@@ -11566,7 +11527,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
ASSERT(0 <= ix && ix < erts_no_run_queues);
rq = ERTS_RUNQ_IX(ix);
/* Unsupported feature... */
- state |= ERTS_PSFLG_BOUND;
+ bound = !0;
}
prio = (erts_aint32_t) so->priority;
}
@@ -11579,17 +11540,16 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
flags |= F_OFF_HEAP_MSGQ;
}
else if (so->flags & SPO_ON_HEAP_MSGQ) {
- state |= ERTS_PSFLG_ON_HEAP_MSGQ;
flags |= F_ON_HEAP_MSGQ;
}
ASSERT((flags & F_ON_HEAP_MSGQ) || (flags & F_OFF_HEAP_MSGQ));
if (!rq)
- rq = erts_get_runq_proc(parent);
+ rq = erts_get_runq_proc(parent, NULL);
- p = alloc_process(rq, state); /* All proc locks are locked by this thread
- on success */
+ p = alloc_process(rq, bound, state); /* All proc locks are locked by this thread
+ on success */
if (!p) {
erts_send_error_to_logger_str(parent->group_leader,
"Too many processes\n");
@@ -11597,11 +11557,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
goto error;
}
- ASSERT((erts_atomic32_read_nob(&p->state)
- & ERTS_PSFLG_ON_HEAP_MSGQ)
- || (erts_atomic32_read_nob(&p->state)
- & ERTS_PSFLG_OFF_HEAP_MSGQ));
-
#ifdef SHCOPY_SPAWN
arg_size = copy_shared_calculate(args, &info);
#else
@@ -11976,7 +11931,7 @@ void erts_init_empty_process(Process *p)
p->pending_exit.bp = NULL;
erts_proc_lock_init(p);
erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
- RUNQ_SET_RQ(&p->run_queue, ERTS_RUNQ_IX(0));
+ erts_init_runq_proc(p, ERTS_RUNQ_IX(0), 0);
#if !defined(NO_FPE_SIGNALS) || defined(HIPE)
p->fp_exception = 0;
@@ -12298,7 +12253,7 @@ save_pending_exiter(Process *p, ErtsProcList *plp)
ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- rq = RUNQ_READ_RQ(&p->run_queue);
+ rq = erts_get_runq_proc(p, NULL);
ASSERT(rq && !ERTS_RUNQ_IX_IS_DIRTY(rq->ix));
if (!plp)
@@ -13398,16 +13353,33 @@ stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg)
return yreg;
}
+static void print_current_process_info(fmtfn_t, void *to_arg, ErtsSchedulerData*);
+
/*
* Print scheduler information
*/
void
-erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
+erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp)
+{
int i;
erts_aint32_t flg;
- Process *p;
- erts_print(to, to_arg, "=scheduler:%u\n", esdp->no);
+ switch (esdp->type) {
+ case ERTS_SCHED_NORMAL:
+ erts_print(to, to_arg, "=scheduler:%u\n", esdp->no);
+ break;
+ case ERTS_SCHED_DIRTY_CPU:
+ erts_print(to, to_arg, "=dirty_cpu_scheduler:%u\n",
+ (esdp->dirty_no + erts_no_schedulers));
+ break;
+ case ERTS_SCHED_DIRTY_IO:
+ erts_print(to, to_arg, "=dirty_io_scheduler:%u\n",
+ (esdp->dirty_no + erts_no_schedulers + erts_no_dirty_cpu_schedulers));
+ break;
+ default:
+ erts_print(to, to_arg, "=unknown_scheduler_type:%u\n", esdp->type);
+ break;
+ }
flg = erts_atomic32_read_dirty(&esdp->ssi->flags);
erts_print(to, to_arg, "Scheduler Sleep Info Flags: ");
@@ -13453,10 +13425,24 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
}
erts_print(to, to_arg, "\n");
- erts_print(to, to_arg, "Current Port: ");
- if (esdp->current_port)
- erts_print(to, to_arg, "%T", esdp->current_port->common.id);
- erts_print(to, to_arg, "\n");
+ if (esdp->type == ERTS_SCHED_NORMAL) {
+ erts_print(to, to_arg, "Current Port: ");
+ if (esdp->current_port)
+ erts_print(to, to_arg, "%T", esdp->current_port->common.id);
+ erts_print(to, to_arg, "\n");
+
+ erts_print_run_queue_info(to, to_arg, esdp->run_queue);
+ }
+
+ /* This *MUST* to be the last information in scheduler block */
+ print_current_process_info(to, to_arg, esdp);
+}
+
+void erts_print_run_queue_info(fmtfn_t to, void *to_arg,
+ ErtsRunQueue *run_queue)
+{
+ erts_aint32_t flg;
+ int i;
for (i = 0; i < ERTS_NO_PROC_PRIO_LEVELS; i++) {
erts_print(to, to_arg, "Run Queue ");
@@ -13478,12 +13464,12 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
break;
}
erts_print(to, to_arg, "Length: %d\n",
- erts_atomic32_read_dirty(&esdp->run_queue->procs.prio_info[i].len));
+ erts_atomic32_read_dirty(&run_queue->procs.prio_info[i].len));
}
erts_print(to, to_arg, "Run Queue Port Length: %d\n",
- erts_atomic32_read_dirty(&esdp->run_queue->ports.info.len));
+ erts_atomic32_read_dirty(&run_queue->ports.info.len));
- flg = erts_atomic32_read_dirty(&esdp->run_queue->flags);
+ flg = erts_atomic32_read_dirty(&run_queue->flags);
erts_print(to, to_arg, "Run Queue Flags: ");
for (i = 0; i < ERTS_RUNQ_FLG_MAX && flg; i++) {
erts_aint32_t chk = (1 << i);
@@ -13550,9 +13536,15 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
}
}
erts_print(to, to_arg, "\n");
+}
+
+
+static void print_current_process_info(fmtfn_t to, void *to_arg,
+ ErtsSchedulerData* esdp)
+{
+ Process *p = esdp->current_process;
+ erts_aint32_t flg;
- /* This *MUST* to be the last information in scheduler block */
- p = esdp->current_process;
erts_print(to, to_arg, "Current Process: ");
if (esdp->current_process && !(ERTS_TRACE_FLAGS(p) & F_SENSITIVE)) {
flg = erts_atomic32_read_dirty(&p->state);
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 55c020d47b..e585fabb51 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -244,6 +244,9 @@ extern int erts_dio_sched_thread_suggested_stack_size;
(erts_aint32_t) (MSK), \
(erts_aint32_t) (FLGS)))
+#define ERTS_RUNQ_POINTER_MASK (~((erts_aint_t) 3))
+#define ERTS_RUNQ_BOUND_FLAG ((erts_aint_t) 1)
+
typedef enum {
ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED,
ERTS_SCHDLR_SSPND_DONE_NMSCHED_BLOCKED,
@@ -1168,14 +1171,14 @@ void erts_check_for_holes(Process* p);
#define ERTS_PSFLG_RUNNING ERTS_PSFLG_BIT(9)
#define ERTS_PSFLG_SUSPENDED ERTS_PSFLG_BIT(10)
#define ERTS_PSFLG_GC ERTS_PSFLG_BIT(11)
-#define ERTS_PSFLG_BOUND ERTS_PSFLG_BIT(12)
+/* #define ERTS_PSFLG_ ERTS_PSFLG_BIT(12) */
#define ERTS_PSFLG_TRAP_EXIT ERTS_PSFLG_BIT(13)
#define ERTS_PSFLG_ACTIVE_SYS ERTS_PSFLG_BIT(14)
#define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15)
#define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16)
#define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17)
#define ERTS_PSFLG_OFF_HEAP_MSGQ ERTS_PSFLG_BIT(18)
-#define ERTS_PSFLG_ON_HEAP_MSGQ ERTS_PSFLG_BIT(19)
+/* #define ERTS_PSFLG_ ERTS_PSFLG_BIT(19) */
#define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(20)
#define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(21)
#define ERTS_PSFLG_DIRTY_ACTIVE_SYS ERTS_PSFLG_BIT(22)
@@ -1546,6 +1549,7 @@ Uint64 erts_step_proc_interval(void);
ErtsProcList *erts_proclist_create(Process *);
ErtsProcList *erts_proclist_copy(ErtsProcList *);
void erts_proclist_destroy(ErtsProcList *);
+void erts_proclist_dump(fmtfn_t to, void *to_arg, ErtsProcList*);
ERTS_GLB_INLINE int erts_proclist_same(ErtsProcList *, Process *);
ERTS_GLB_INLINE void erts_proclist_store_first(ErtsProcList **, ErtsProcList *);
@@ -1792,6 +1796,7 @@ void erts_stack_dump(fmtfn_t to, void *to_arg, Process *);
void erts_limited_stack_trace(fmtfn_t to, void *to_arg, Process *);
void erts_program_counter_info(fmtfn_t to, void *to_arg, Process *);
void erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp);
+void erts_print_run_queue_info(fmtfn_t, void *to_arg, ErtsRunQueue*);
void erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
@@ -2167,7 +2172,12 @@ ERTS_GLB_INLINE int erts_is_scheduler_bound(ErtsSchedulerData *esdp);
ERTS_GLB_INLINE Process *erts_get_current_process(void);
ERTS_GLB_INLINE Eterm erts_get_current_pid(void);
ERTS_GLB_INLINE Uint erts_get_scheduler_id(void);
-ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_proc(Process *p);
+ERTS_GLB_INLINE void erts_init_runq_proc(Process *p, ErtsRunQueue *rq, int bnd);
+ERTS_GLB_INLINE ErtsRunQueue *erts_set_runq_proc(Process *p, ErtsRunQueue *rq, int *boundp);
+ERTS_GLB_INLINE int erts_try_change_runq_proc(Process *p, ErtsRunQueue *rq);
+ERTS_GLB_INLINE ErtsRunQueue *erts_bind_runq_proc(Process *p, int bind);
+ERTS_GLB_INLINE int erts_proc_runq_is_bound(Process *p);
+ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_proc(Process *p, int *boundp);
ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_current(ErtsSchedulerData *esdp);
ERTS_GLB_INLINE void erts_runq_lock(ErtsRunQueue *rq);
ERTS_GLB_INLINE int erts_runq_trylock(ErtsRunQueue *rq);
@@ -2247,11 +2257,143 @@ Uint erts_get_scheduler_id(void)
return esdp ? esdp->no : (Uint) 0;
}
+/**
+ * Init run-queue of process.
+ *
+ * @param p[in,out] Process
+ * @param rq[in] Run-queue that process will be assigned to
+ * @param bnd[in,out] If non-zero binds process to run-queue.
+ */
+
+ERTS_GLB_INLINE void
+erts_init_runq_proc(Process *p, ErtsRunQueue *rq, int bnd)
+{
+ erts_aint_t rqint = (erts_aint_t) rq;
+ if (bnd)
+ rqint |= ERTS_RUNQ_BOUND_FLAG;
+ erts_atomic_init_nob(&p->run_queue, rqint);
+}
+
+/**
+ * Forcibly set run-queue of process.
+ *
+ * @param p[in,out] Process
+ * @param rq[in] Run-queue that process will be assigned to
+ * @param bndp[in,out] Pointer to integer. On input non-zero
+ * value causes the process to be bound to
+ * the run-queue. On output, indicating
+ * wether process previously was bound or
+ * not.
+ * @return Previous run-queue.
+ */
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_set_runq_proc(Process *p, ErtsRunQueue *rq, int *bndp)
+{
+ erts_aint_t rqint = (erts_aint_t) rq;
+ ASSERT(bndp);
+ if (*bndp)
+ rqint |= ERTS_RUNQ_BOUND_FLAG;
+ rqint = erts_atomic_xchg_nob(&p->run_queue, rqint);
+ *bndp = (int) (rqint & ERTS_RUNQ_BOUND_FLAG);
+ return (ErtsRunQueue *) (rqint & ERTS_RUNQ_POINTER_MASK);
+}
+
+/**
+ * Try to change run-queue assignment of a process.
+ *
+ * @param p[in,out] Process
+ * @param rq[int] Run-queue that process will be assigned to
+ * @return Non-zero if the run-queue assignment was
+ * successfully changed.
+ */
+
+ERTS_GLB_INLINE int
+erts_try_change_runq_proc(Process *p, ErtsRunQueue *rq)
+{
+ erts_aint_t old_rqint, new_rqint;
+
+ new_rqint = (erts_aint_t) rq;
+ old_rqint = (erts_aint_t) erts_atomic_read_nob(&p->run_queue);
+ while (1) {
+ erts_aint_t act_rqint;
+
+ if (old_rqint & ERTS_RUNQ_BOUND_FLAG)
+ return 0;
+
+ act_rqint = erts_atomic_cmpxchg_nob(&p->run_queue,
+ new_rqint,
+ old_rqint);
+ if (act_rqint == old_rqint)
+ return !0;
+ }
+}
+
+/**
+ *
+ * Bind or unbind process to/from currently used run-queue.
+ *
+ * @param p Process
+ * @param bind Bind if non-zero; otherwise unbind
+ * @return Pointer to previously bound run-queue,
+ * or NULL if previously unbound
+ */
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_bind_runq_proc(Process *p, int bind)
+{
+ erts_aint_t rqint;
+ if (bind)
+ rqint = erts_atomic_read_bor_nob(&p->run_queue,
+ ERTS_RUNQ_BOUND_FLAG);
+ else
+ rqint = erts_atomic_read_band_nob(&p->run_queue,
+ ~ERTS_RUNQ_BOUND_FLAG);
+ if (rqint & ERTS_RUNQ_BOUND_FLAG)
+ return (ErtsRunQueue *) (rqint & ERTS_RUNQ_POINTER_MASK);
+ else
+ return NULL;
+}
+
+/**
+ * Determine wether a process is bound to a run-queue or not.
+ *
+ * @return Returns a non-zero value if bound,
+ * and zero of not bound.
+ */
+
+ERTS_GLB_INLINE int
+erts_proc_runq_is_bound(Process *p)
+{
+ erts_aint_t rqint = erts_atomic_read_nob(&p->run_queue);
+ return (int) (rqint & ERTS_RUNQ_BOUND_FLAG);
+}
+
+/**
+ * Set run-queue of process.
+ *
+ * @param p[in,out] Process
+ * @param bndp[out] Pointer to integer. If non-NULL pointer,
+ * the integer will be set to a non-zero
+ * value if the process is bound to the
+ * run-queue.
+ * @return Pointer to the normal run-queue that
+ * the process currently is assigend to.
+ * A process is always assigned to a
+ * normal run-queue.
+ */
+
ERTS_GLB_INLINE ErtsRunQueue *
-erts_get_runq_proc(Process *p)
+erts_get_runq_proc(Process *p, int *bndp)
{
- ASSERT(ERTS_AINT_NULL != erts_atomic_read_nob(&p->run_queue));
- return (ErtsRunQueue *) erts_atomic_read_nob(&p->run_queue);
+ erts_aint_t rqint = erts_atomic_read_nob(&p->run_queue);
+ ErtsRunQueue *rq;
+ if (bndp)
+ *bndp = (int) (rqint & ERTS_RUNQ_BOUND_FLAG);
+ rqint &= ERTS_RUNQ_POINTER_MASK;
+ rq = (ErtsRunQueue *) rqint;
+ ASSERT(rq);
+ return rq;
}
ERTS_GLB_INLINE ErtsRunQueue *
diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c
index f562fc961b..05e7bcdea2 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -78,8 +78,17 @@ erts_deep_process_dump(fmtfn_t to, void *to_arg)
Process *p = erts_pix2proc(i);
if (p && p->i != ENULL) {
erts_aint32_t state = erts_atomic32_read_acqb(&p->state);
- if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_GC)))
- dump_process_info(to, to_arg, p);
+ if (state & ERTS_PSFLG_EXITING)
+ continue;
+ if (state & ERTS_PSFLG_GC) {
+ ErtsSchedulerData *sdp = erts_get_scheduler_data();
+ if (!sdp || p != sdp->current_process)
+ continue;
+
+ /* We want to dump the garbing process that caused the dump */
+ }
+
+ dump_process_info(to, to_arg, p);
}
}
@@ -135,9 +144,12 @@ dump_process_info(fmtfn_t to, void *to_arg, Process *p)
ErtsMessage* mp;
int yreg = -1;
+ if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE)
+ return;
+
ERTS_MSGQ_MV_INQ2PRIVQ(p);
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0 && p->msg.first) {
+ if (p->msg.first) {
erts_print(to, to_arg, "=proc_messages:%T\n", p->common.id);
for (mp = p->msg.first; mp != NULL; mp = mp->next) {
Eterm mesg = ERL_MESSAGE_TERM(mp);
@@ -152,38 +164,34 @@ dump_process_info(fmtfn_t to, void *to_arg, Process *p)
}
}
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
- if (p->dictionary) {
- erts_print(to, to_arg, "=proc_dictionary:%T\n", p->common.id);
- erts_deep_dictionary_dump(to, to_arg,
- p->dictionary, dump_element_nl);
- }
+ if (p->dictionary) {
+ erts_print(to, to_arg, "=proc_dictionary:%T\n", p->common.id);
+ erts_deep_dictionary_dump(to, to_arg,
+ p->dictionary, dump_element_nl);
}
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
- erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id);
- for (sp = p->stop; sp < STACK_START(p); sp++) {
- yreg = stack_element_dump(to, to_arg, sp, yreg);
- }
+ erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id);
+ for (sp = p->stop; sp < STACK_START(p); sp++) {
+ yreg = stack_element_dump(to, to_arg, sp, yreg);
+ }
- erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id);
- for (sp = p->stop; sp < STACK_START(p); sp++) {
- Eterm term = *sp;
-
- if (!is_catch(term) && !is_CP(term)) {
- heap_dump(to, to_arg, term);
- }
- }
- for (mp = p->msg.first; mp != NULL; mp = mp->next) {
- Eterm mesg = ERL_MESSAGE_TERM(mp);
- if (is_value(mesg))
- heap_dump(to, to_arg, mesg);
- mesg = ERL_MESSAGE_TOKEN(mp);
- heap_dump(to, to_arg, mesg);
- }
- if (p->dictionary) {
- erts_deep_dictionary_dump(to, to_arg, p->dictionary, heap_dump);
- }
+ erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id);
+ for (sp = p->stop; sp < STACK_START(p); sp++) {
+ Eterm term = *sp;
+
+ if (!is_catch(term) && !is_CP(term)) {
+ heap_dump(to, to_arg, term);
+ }
+ }
+ for (mp = p->msg.first; mp != NULL; mp = mp->next) {
+ Eterm mesg = ERL_MESSAGE_TERM(mp);
+ if (is_value(mesg))
+ heap_dump(to, to_arg, mesg);
+ mesg = ERL_MESSAGE_TOKEN(mp);
+ heap_dump(to, to_arg, mesg);
+ }
+ if (p->dictionary) {
+ erts_deep_dictionary_dump(to, to_arg, p->dictionary, heap_dump);
}
}
@@ -1001,8 +1009,6 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
erts_print(to, to_arg, "SUSPENDED"); break;
case ERTS_PSFLG_GC:
erts_print(to, to_arg, "GC"); break;
- case ERTS_PSFLG_BOUND:
- erts_print(to, to_arg, "BOUND"); break;
case ERTS_PSFLG_TRAP_EXIT:
erts_print(to, to_arg, "TRAP_EXIT"); break;
case ERTS_PSFLG_ACTIVE_SYS:
@@ -1015,8 +1021,6 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
erts_print(to, to_arg, "DELAYED_SYS"); break;
case ERTS_PSFLG_OFF_HEAP_MSGQ:
erts_print(to, to_arg, "OFF_HEAP_MSGQ"); break;
- case ERTS_PSFLG_ON_HEAP_MSGQ:
- erts_print(to, to_arg, "ON_HEAP_MSGQ"); break;
case ERTS_PSFLG_DIRTY_CPU_PROC:
erts_print(to, to_arg, "DIRTY_CPU_PROC"); break;
case ERTS_PSFLG_DIRTY_IO_PROC:
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 4b996d8fc2..d2495479ab 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -2783,24 +2783,28 @@ is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks,
ASSERT(0);
}
- /* Only remove tracer on self() and ports */
+ /* Only remove tracer on (self() or ports) AND we are on a normal scheduler */
if (is_internal_port(t_p->id) || (c_p && c_p->common.id == t_p->id)) {
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
ErtsProcLocks c_p_xlocks = 0;
- if (is_internal_pid(t_p->id)) {
- ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
- if (c_p_locks != ERTS_PROC_LOCKS_ALL) {
- c_p_xlocks = ~c_p_locks & ERTS_PROC_LOCKS_ALL;
- if (erts_proc_trylock(c_p, c_p_xlocks) == EBUSY) {
- erts_proc_unlock(c_p, c_p_locks & ~ERTS_PROC_LOCK_MAIN);
- erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ if (esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ if (is_internal_pid(t_p->id)) {
+ ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
+ if (c_p_locks != ERTS_PROC_LOCKS_ALL) {
+ c_p_xlocks = ~c_p_locks & ERTS_PROC_LOCKS_ALL;
+ if (erts_proc_trylock(c_p, c_p_xlocks) == EBUSY) {
+ erts_proc_unlock(c_p, c_p_locks & ~ERTS_PROC_LOCK_MAIN);
+ erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
}
}
- }
- erts_tracer_replace(t_p, erts_tracer_nil);
- t_p->trace_flags &= ~TRACEE_FLAGS;
- if (c_p_xlocks)
- erts_proc_unlock(c_p, c_p_xlocks);
+ erts_tracer_replace(t_p, erts_tracer_nil);
+ t_p->trace_flags &= ~TRACEE_FLAGS;
+
+ if (c_p_xlocks)
+ erts_proc_unlock(c_p, c_p_xlocks);
+ }
}
return 0;
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 2c1b7871c4..3e8f6263bb 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -298,7 +298,6 @@ static Port *create_port(char *name,
erts_aint32_t state = ERTS_PORT_SFLG_CONNECTED;
erts_aint32_t x_pts_flgs = 0;
- ErtsRunQueue *runq;
if (!driver_lock) {
/* Align size for mutex following port struct */
port_size = size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port));
@@ -347,11 +346,16 @@ static Port *create_port(char *name,
p += sizeof(erts_mtx_t);
state |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK;
}
- if (erts_get_scheduler_data())
- runq = erts_get_runq_current(NULL);
- else
- runq = ERTS_RUNQ_IX(0);
- erts_atomic_set_nob(&prt->run_queue, (erts_aint_t) runq);
+
+ {
+ ErtsRunQueue *runq;
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ if (esdp)
+ runq = erts_get_runq_current(esdp);
+ else
+ runq = ERTS_RUNQ_IX(0);
+ erts_init_runq_port(prt, runq);
+ }
prt->xports = NULL;
@@ -5075,6 +5079,93 @@ static void prt_one_lnk(ErtsLink *lnk, void *vprtd)
erts_print(prtd->to, prtd->arg, "%T", lnk->pid);
}
+static void dump_port_state(fmtfn_t to, void *arg, erts_aint32_t state)
+{
+ erts_aint32_t rest;
+ int unknown = 0;
+ char delim = ' ';
+
+ erts_print(to, arg, "State:");
+
+ rest = state;
+ while (rest) {
+ erts_aint32_t chk = (rest ^ (rest-1)) & rest; /* lowest set bit */
+ char* s;
+
+ rest &= ~chk;
+ switch (chk) {
+ case ERTS_PORT_SFLG_CONNECTED: s = "CONNECTED"; break;
+ case ERTS_PORT_SFLG_EXITING: s = "EXITING"; break;
+ case ERTS_PORT_SFLG_DISTRIBUTION: s = "DISTR"; break;
+ case ERTS_PORT_SFLG_BINARY_IO: s = "BINARY_IO"; break;
+ case ERTS_PORT_SFLG_SOFT_EOF: s = "SOFT_EOF"; break;
+ case ERTS_PORT_SFLG_CLOSING: s = "CLOSING"; break;
+ case ERTS_PORT_SFLG_SEND_CLOSED: s = "SEND_CLOSED"; break;
+ case ERTS_PORT_SFLG_LINEBUF_IO: s = "LINEBUF_IO"; break;
+ case ERTS_PORT_SFLG_FREE: s = "FREE"; break;
+ case ERTS_PORT_SFLG_INITIALIZING: s = "INITIALIZING"; break;
+ case ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK: s = "PORT_LOCK"; break;
+ case ERTS_PORT_SFLG_INVALID: s = "INVALID"; break;
+ case ERTS_PORT_SFLG_HALT: s = "HALT"; break;
+#ifdef DEBUG
+ case ERTS_PORT_SFLG_PORT_DEBUG: s = "DEBUG"; break;
+#endif
+ default:
+ unknown = 1;
+ continue;
+ }
+ erts_print(to, arg, "%c%s", delim, s);
+ delim = '|';
+ }
+ if (unknown || !state)
+ erts_print(to, arg, "%c0x%x\n", delim, state);
+ else
+ erts_print(to, arg, "\n");
+}
+
+static void dump_port_task_flags(fmtfn_t to, void *arg, Port* p)
+{
+ erts_aint32_t flags = erts_atomic32_read_nob(&p->sched.flags);
+ erts_aint32_t unknown = 0;
+ char delim = ' ';
+
+ if (!flags)
+ return;
+
+ erts_print(to, arg, "Task Flags:");
+
+ while (flags) {
+ erts_aint32_t chk = (flags ^ (flags-1)) & flags; /* lowest set bit */
+ char* s;
+
+ flags &= ~chk;
+ switch (chk) {
+ case ERTS_PTS_FLG_IN_RUNQ: s = "IN_RUNQ"; break;
+ case ERTS_PTS_FLG_EXEC: s = "EXEC"; break;
+ case ERTS_PTS_FLG_HAVE_TASKS: s = "HAVE_TASKS"; break;
+ case ERTS_PTS_FLG_EXIT: s = "EXIT"; break;
+ case ERTS_PTS_FLG_BUSY_PORT: s = "BUSY_PORT"; break;
+ case ERTS_PTS_FLG_BUSY_PORT_Q: s = "BUSY_Q"; break;
+ case ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q: s = "CHK_UNSET_BUSY_Q"; break;
+ case ERTS_PTS_FLG_HAVE_BUSY_TASKS: s = "BUSY_TASKS"; break;
+ case ERTS_PTS_FLG_HAVE_NS_TASKS: s = "NS_TASKS"; break;
+ case ERTS_PTS_FLG_PARALLELISM: s = "PARALLELISM"; break;
+ case ERTS_PTS_FLG_FORCE_SCHED: s = "FORCE_SCHED"; break;
+ case ERTS_PTS_FLG_EXITING: s = "EXITING"; break;
+ case ERTS_PTS_FLG_EXEC_IMM: s = "EXEC_IMM"; break;
+ default:
+ unknown |= chk;
+ continue;
+ }
+ erts_print(to, arg, "%c%s", delim, s);
+ delim = '|';
+ }
+ if (unknown)
+ erts_print(to, arg, "%cUNKNOWN(0x%x)\n", delim, unknown);
+ else
+ erts_print(to, arg, "\n");
+}
+
void
print_port_info(Port *p, fmtfn_t to, void *arg)
{
@@ -5084,6 +5175,8 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
return;
erts_print(to, arg, "=port:%T\n", p->common.id);
+ dump_port_state(to, arg, state);
+ dump_port_task_flags(to, arg, p);
erts_print(to, arg, "Slot: %d\n", internal_port_index(p->common.id));
if (state & ERTS_PORT_SFLG_CONNECTED) {
erts_print(to, arg, "Connected: %T", ERTS_PORT_GET_CONNECTED(p));
@@ -5106,6 +5199,10 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
erts_doforall_monitors(ERTS_P_MONITORS(p), &prt_one_monitor, &prtd);
erts_print(to, arg, "\n");
}
+ if (p->suspended) {
+ erts_print(to, arg, "Suspended: ");
+ erts_proclist_dump(to, arg, p->suspended);
+ }
if (p->common.u.alive.reg != NULL)
erts_print(to, arg, "Registered as: %T\n", p->common.u.alive.reg->name);
@@ -5123,6 +5220,14 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
} else {
erts_print(to, arg, "Port controls linked-in driver: %s\n",p->name);
}
+ erts_print(to, arg, "Input: %beu\n", p->bytes_in);
+ erts_print(to, arg, "Output: %beu\n", p->bytes_out);
+ erts_print(to, arg, "Queue: %beu\n", erts_ioq_size(&p->ioq));
+ {
+ Eterm port_data = erts_port_data_read(p);
+ if (port_data != am_undefined)
+ erts_print(to, arg, "Port Data: %T\n", port_data);
+ }
}
void
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index fe9f1c7606..4bf60619ba 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -3152,6 +3152,9 @@ tailrecur_ne:
int cmp;
byte* a_ptr;
byte* b_ptr;
+ if (eq_only && a_size != b_size) {
+ RETURN_NEQ(a_size - b_size);
+ }
ERTS_GET_BINARY_BYTES(a, a_ptr, a_bitoffs, a_bitsize);
ERTS_GET_BINARY_BYTES(b, b_ptr, b_bitoffs, b_bitsize);
if ((a_bitsize | b_bitsize | a_bitoffs | b_bitoffs) == 0) {
diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c
index 57c8ef62e1..4a6c476882 100644
--- a/erts/emulator/nifs/unix/unix_prim_file.c
+++ b/erts/emulator/nifs/unix/unix_prim_file.c
@@ -125,24 +125,11 @@ static int open_file_type_check(const efile_path_t *path, int fd) {
* immediately in a read within the call, but the new implementation
* never does that. */
return 1;
- } else {
- /* The old driver tolerated opening /dev/null despite the "no devices"
- * limitation. It provided no explanation for this but we still need
- * to match the behavior. We're checking through stat(2) instead of
- * comparing the name to account for links. */
- struct stat null_device_info;
- int is_dev_null;
-
- is_dev_null = (stat("/dev/null", &null_device_info) == 0);
- is_dev_null &= (file_info.st_ino == null_device_info.st_ino);
- is_dev_null &= (file_info.st_dev == null_device_info.st_dev);
-
- if(is_dev_null) {
- return 1;
- }
}
- if(!S_ISREG(file_info.st_mode)) {
+ /* Allow everything that isn't a directory, and error out on the next call
+ * if it's unsupported. */
+ if(S_ISDIR(file_info.st_mode)) {
return 0;
}
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 4bc1838139..a3c3daac15 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -262,6 +262,7 @@ test_deep_bitstr(List) ->
{Bin,bitstring_to_list(Bin)}.
bad_list_to_binary(Config) when is_list(Config) ->
+ test_bad_bin(<<1:1>>),
test_bad_bin(atom),
test_bad_bin(42),
test_bad_bin([1|2]),
diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl
index 16d581a567..821381bf0d 100644
--- a/erts/emulator/test/efile_SUITE.erl
+++ b/erts/emulator/test/efile_SUITE.erl
@@ -46,14 +46,14 @@ iter_max_files_1(Config) ->
DataDir = proplists:get_value(data_dir,Config),
TestFile = filename:join(DataDir, "existing_file"),
N = 10,
- %% Run on a different node in order to set the max ports
+ %% Run on a different node in order to make the test more stable.
Dir = filename:dirname(code:which(?MODULE)),
{ok,Node} = test_server:start_node(test_iter_max_files,slave,
- [{args,"+Q 1524 -pa " ++ Dir}]),
+ [{args,"-pa " ++ Dir}]),
L = rpc:call(Node,?MODULE,do_iter_max_files,[N, TestFile]),
test_server:stop_node(Node),
io:format("Number of files opened in each test:~n~w\n", [L]),
- all_equal(L),
+ verify_max_files(L),
Head = hd(L),
if Head >= 2 -> ok;
true -> ct:fail(too_few_files)
@@ -65,12 +65,15 @@ do_iter_max_files(N, Name) when N > 0 ->
do_iter_max_files(_, _) ->
[].
-all_equal([E, E| T]) ->
- all_equal([E| T]);
-all_equal([_]) ->
- ok;
-all_equal([]) ->
- ok.
+%% The attempts shouldn't vary too much; we used to require that they were all
+%% exactly equal, but after we reimplemented the file driver as a NIF we
+%% noticed that the only reason it was stable on Darwin was because the port
+%% limit was hit before ulimit.
+verify_max_files(Attempts) ->
+ N = length(Attempts),
+ Mean = lists:sum(Attempts) / N,
+ Variance = lists:sum([(X - Mean) * (X - Mean) || X <- Attempts]) / N,
+ true = math:sqrt(Variance) =< 1 + (Mean / 1000).
max_files(Name) ->
Fds = open_files(Name),
diff --git a/erts/emulator/test/iovec_SUITE.erl b/erts/emulator/test/iovec_SUITE.erl
index 49dc64b0d2..963b7e2501 100644
--- a/erts/emulator/test/iovec_SUITE.erl
+++ b/erts/emulator/test/iovec_SUITE.erl
@@ -25,7 +25,8 @@
-export([integer_lists/1, binary_lists/1, empty_lists/1, empty_binary_lists/1,
mixed_lists/1, improper_lists/1, illegal_lists/1, cons_bomb/1,
sub_binary_lists/1, iolist_to_iovec_idempotence/1,
- iolist_to_iovec_correctness/1]).
+ iolist_to_iovec_correctness/1, unaligned_sub_binaries/1,
+ direct_binary_arg/1]).
-include_lib("common_test/include/ct.hrl").
@@ -36,7 +37,8 @@ suite() ->
all() ->
[integer_lists, binary_lists, empty_lists, empty_binary_lists, mixed_lists,
sub_binary_lists, illegal_lists, improper_lists, cons_bomb,
- iolist_to_iovec_idempotence, iolist_to_iovec_correctness].
+ iolist_to_iovec_idempotence, iolist_to_iovec_correctness,
+ unaligned_sub_binaries, direct_binary_arg].
init_per_suite(Config) ->
Config.
@@ -78,7 +80,7 @@ illegal_lists(Config) when is_list(Config) ->
BitStrs = gen_variations(["gurka", <<1:1>>, "gaffel"]),
BadInts = gen_variations(["gurka", 890, "gaffel"]),
Atoms = gen_variations([gurka, "gaffel"]),
- BadTails = [["test" | 0], ["gurka", gaffel]],
+ BadTails = [["test" | 0], ["gurka" | gaffel], ["gaffel" | <<1:1>>]],
Variations =
BitStrs ++ BadInts ++ Atoms ++ BadTails,
@@ -98,14 +100,7 @@ cons_bomb(Config) when is_list(Config) ->
BinBase = gen_variations([<<I:8>> || I <- lists:seq(1, 255)]),
MixBase = gen_variations([<<12, 45, 78>>, lists:seq(1, 255)]),
- Rounds =
- case system_mem_size() of
- Mem when Mem >= (16 bsl 30) -> 32;
- Mem when Mem >= (3 bsl 30) -> 28;
- _ -> 20
- end,
-
- Variations = gen_variations([IntBase, BinBase, MixBase], Rounds),
+ Variations = gen_variations([IntBase, BinBase, MixBase], 16),
equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
iolist_to_iovec_idempotence(Config) when is_list(Config) ->
@@ -130,6 +125,21 @@ iolist_to_iovec_correctness(Config) when is_list(Config) ->
true = is_iolist_equal(Optimized, Variations),
ok.
+unaligned_sub_binaries(Config) when is_list(Config) ->
+ UnalignedBins = [gen_unaligned_binary(I) || I <- lists:seq(32, 4 bsl 10, 512)],
+ UnalignedVariations = gen_variations(UnalignedBins),
+
+ Optimized = erlang:iolist_to_iovec(UnalignedVariations),
+
+ true = is_iolist_equal(Optimized, UnalignedVariations),
+ ok.
+
+direct_binary_arg(Config) when is_list(Config) ->
+ {'EXIT',{badarg, _}} = (catch erlang:iolist_to_iovec(<<1:1>>)),
+ [<<1>>] = erlang:iolist_to_iovec(<<1>>),
+ [] = erlang:iolist_to_iovec(<<>>),
+ ok.
+
illegality_test(Fun, Variations) ->
[{'EXIT',{badarg, _}} = (catch Fun(Variation)) || Variation <- Variations],
ok.
@@ -145,11 +155,18 @@ equivalence_test(Fun, [Head | _] = Variations) ->
is_iolist_equal(A, B) ->
iolist_to_binary(A) =:= iolist_to_binary(B).
+gen_unaligned_binary(Size) ->
+ Bin0 = << <<I>> || I <- lists:seq(1, Size) >>,
+ <<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>),
+ Bin.
+
+id(I) -> I.
+
%% Generates a bunch of lists whose contents will be equal to Base repeated a
%% few times. The lists only differ by their structure, so their reduction to
%% a simpler format should yield the same result.
gen_variations(Base) ->
- gen_variations(Base, 16).
+ gen_variations(Base, 12).
gen_variations(Base, N) ->
[gen_flat_list(Base, N),
gen_nested_list(Base, N),
@@ -169,8 +186,3 @@ gen_nasty_list_1([Head | Base], Result) when is_list(Head) ->
gen_nasty_list_1(Base, [[Result], [gen_nasty_list_1(Head, [])]]);
gen_nasty_list_1([Head | Base], Result) ->
gen_nasty_list_1(Base, [[Result], [Head]]).
-
-system_mem_size() ->
- application:ensure_all_started(os_mon),
- {Tot,_Used,_} = memsup:get_memory_data(),
- Tot.
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 85c302e310..b6e15ababb 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -3049,8 +3049,24 @@ nif_ioq(Config) ->
{peek_head, a},
{peek, a},
- %% Test to enqueue a bunch of refc binaries
+ %% Ensure that enqueued refc binaries are intact after a roundtrip.
+ %%
+ %% This test and the ones immediately following it does not go through
+ %% erlang:iolist_to_iovec/1
{enqv, a, [nif_ioq_payload(refcbin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
+
+ %% ... heap binaries
+ {enqv, a, [nif_ioq_payload(heapbin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
+
+ %% ... plain sub-binaries
+ {enqv, a, [nif_ioq_payload(subbin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
+
+ %% ... unaligned binaries
+ {enqv, a, [nif_ioq_payload(unaligned_bin) || _ <- lists:seq(1,20)], 0},
+ {peek, a},
%% Enq stuff to destroy with data in queue
{enqv, a, 2, 100},
@@ -3206,13 +3222,16 @@ nif_ioq_payload(N) when is_integer(N) ->
Tail = if N > 3 -> nif_ioq_payload(N-3); true -> [] end,
Head = element(1, lists:split(N,[nif_ioq_payload(subbin),
nif_ioq_payload(heapbin),
- nif_ioq_payload(refcbin) | Tail])),
+ nif_ioq_payload(refcbin),
+ nif_ioq_payload(unaligned_bin) | Tail])),
erlang:iolist_to_iovec(Head);
nif_ioq_payload(subbin) ->
Bin = nif_ioq_payload(refcbin),
Sz = size(Bin) - 1,
<<_:8,SubBin:Sz/binary,_/bits>> = Bin,
SubBin;
+nif_ioq_payload(unaligned_bin) ->
+ make_unaligned_binary(<< <<I>> || I <- lists:seq(1, 255) >>);
nif_ioq_payload(heapbin) ->
<<"a literal heap binary">>;
nif_ioq_payload(refcbin) ->
@@ -3220,6 +3239,13 @@ nif_ioq_payload(refcbin) ->
nif_ioq_payload(Else) ->
Else.
+make_unaligned_binary(Bin0) ->
+ Size = byte_size(Bin0),
+ <<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>),
+ Bin.
+
+id(I) -> I.
+
%% The NIFs:
lib_version() -> undefined.
call_history() -> ?nif_stub.
diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c
index b746487668..4fc7a93348 100644
--- a/erts/etc/common/inet_gethost.c
+++ b/erts/etc/common/inet_gethost.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1770,7 +1770,7 @@ static int worker_loop(void)
}
#elif defined(HAVE_GETIPNODEBYNAME) /*#ifdef HAVE_GETADDRINFO */
DEBUGF(5,("Starting getipnodebyname(%s)",data));
- he = getipnodebyname(data, AF_INET6, AI_DEFAULT, &error_num);
+ he = getipnodebyname(data, AF_INET6, 0, &error_num);
if (he) {
free_he = 1;
error_num = 0;
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index 95e065b156..02ace9126c 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -1693,7 +1693,7 @@ define etp-proc-state-int
printf "dirty-cpu-proc | "
end
if ($arg0 & 0x2000000)
- printf "on-heap-msgq | "
+ printf "GARBAGE<0x2000000> | "
end
if ($arg0 & 0x1000000)
printf "off-heap-msgq | "
@@ -1717,7 +1717,7 @@ define etp-proc-state-int
printf "trapping-exit | "
end
if ($arg0 & 0x40000)
- printf "bound | "
+ printf "GARBAGE<0x40000> | "
end
if ($arg0 & 0x20000)
printf "garbage-collecting | "
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 2d86dd3843..02e77bfbb2 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index d16fe5a0bc..b82b9f5b46 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -56,7 +56,7 @@
-export([purge_archive_cache/0]).
%% Used by init and the code server.
--export([get_modules/2,get_modules/3]).
+-export([get_modules/2,get_modules/3, is_basename/1]).
-include_lib("kernel/include/file.hrl").
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index d352e11751..370ecdc3f6 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -2563,10 +2563,10 @@ tuple_to_list(_Tuple) ->
Settings :: [{Subsystem :: atom(),
[{Parameter :: atom(),
Value :: term()}]}];
- (alloc_util_allocators) -> [Alloc] when
- Alloc :: atom();
({allocator, Alloc}) -> [_] when %% More or less anything
Alloc :: atom();
+ (alloc_util_allocators) -> [Alloc] when
+ Alloc :: atom();
({allocator_sizes, Alloc}) -> [_] when %% More or less anything
Alloc :: atom();
(atom_count) -> pos_integer();
@@ -2593,6 +2593,7 @@ tuple_to_list(_Tuple) ->
(driver_version) -> string();
(dynamic_trace) -> none | dtrace | systemtap;
(dynamic_trace_probes) -> boolean();
+ (end_time) -> non_neg_integer();
(elib_malloc) -> false;
(eager_check_io) -> boolean();
(ets_limit) -> pos_integer();
@@ -2620,6 +2621,7 @@ tuple_to_list(_Tuple) ->
(otp_release) -> string();
(os_monotonic_time_source) -> [{atom(),term()}];
(os_system_time_source) -> [{atom(),term()}];
+ (port_parallelism) -> boolean();
(port_count) -> non_neg_integer();
(port_limit) -> pos_integer();
(process_count) -> pos_integer();
@@ -2649,7 +2651,8 @@ tuple_to_list(_Tuple) ->
(trace_control_word) -> non_neg_integer();
(update_cpu_info) -> changed | unchanged;
(version) -> string();
- (wordsize | {wordsize, internal} | {wordsize, external}) -> 4 | 8.
+ (wordsize | {wordsize, internal} | {wordsize, external}) -> 4 | 8;
+ (overview) -> boolean().
system_info(_Item) ->
erlang:nif_error(undefined).
diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl
index b3d0ba20bc..bb1afecafc 100644
--- a/erts/test/z_SUITE.erl
+++ b/erts/test/z_SUITE.erl
@@ -40,7 +40,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {minutes, 5}}].
+ {timetrap, {minutes, 10}}].
all() ->
[core_files].
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index 82e9326294..c09b0f47d1 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -47,14 +47,20 @@ dialyzer_suppressions(#gen{erule=per,aligned=Aligned}) ->
false -> uper;
true -> per
end,
- case asn1ct_func:is_used({Mod,complete,1}) of
+ suppress({Mod,complete,1}),
+ suppress({per_common,to_bitstring,2}),
+ emit([" ok.",nl]).
+
+suppress({M,F,A}=MFA) ->
+ case asn1ct_func:is_used(MFA) of
false ->
ok;
true ->
- emit([" _ = complete(Arg),",nl])
- end,
- emit([" ok.",nl]).
-
+ Args =
+ [lists:concat(["element(",I,", Arg)"])
+ || I <- lists:seq(1, A)],
+ emit([" ",{call,M,F,Args},com,nl])
+ end.
gen_encode(Erules,Type) when is_record(Type,typedef) ->
gen_encode_user(Erules,Type).
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 7183381334..47a2be8ab5 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -36,13 +36,11 @@ module({Mod,Exp,Attr,Fs0,Lc}, Opts) ->
function({function,Name,Arity,CLabel,Is0}, Blockify) ->
try
%% Collect basic blocks and optimize them.
- Is2 = case Blockify of
- true ->
- Is1 = blockify(Is0),
- embed_lines(Is1);
- false ->
- Is0
+ Is1 = case Blockify of
+ false -> Is0;
+ true -> blockify(Is0)
end,
+ Is2 = embed_lines(Is1),
Is3 = local_cse(Is2),
Is4 = beam_utils:anno_defs(Is3),
Is5 = move_allocates(Is4),
@@ -139,6 +137,11 @@ embed_lines([{block,B2},{line,_}=Line,{block,B1}|T], Acc) ->
embed_lines([{block,B1},{line,_}=Line|T], Acc) ->
B = {block,[{set,[],[],Line}|B1]},
embed_lines([B|T], Acc);
+embed_lines([{block,B2},{block,B1}|T], Acc) ->
+ %% This can only happen when beam_block is run for
+ %% the second time.
+ B = {block,B1++B2},
+ embed_lines([B|T], Acc);
embed_lines([I|Is], Acc) ->
embed_lines(Is, [I|Acc]);
embed_lines([], Acc) -> Acc.
@@ -628,7 +631,13 @@ cse_find(Expr, Es) ->
end.
cse_expr({set,[D],Ss,{bif,N,_}}) ->
- {ok,D,{{bif,N},Ss}};
+ case D of
+ {fr,_} ->
+ %% There are too many things that can go wrong.
+ none;
+ _ ->
+ {ok,D,{{bif,N},Ss}}
+ end;
cse_expr({set,[D],Ss,{alloc,_,{gc_bif,N,_}}}) ->
{ok,D,{{gc_bif,N},Ss}};
cse_expr({set,[D],Ss,put_list}) ->
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index e094c2c320..7ddf9fa2e2 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -24,7 +24,7 @@
-export([module/2]).
-export([bs_clean_saves/1]).
-export([clean_labels/1]).
--import(lists, [foldl/3,reverse/1,filter/2]).
+-import(lists, [foldl/3,reverse/1]).
-spec module(beam_utils:module_code(), [compile:option()]) ->
{'ok',beam_utils:module_code()}.
@@ -303,8 +303,21 @@ maybe_remove_lines(Fs, Opts) ->
end.
remove_lines([{function,N,A,Lbl,Is0}|T]) ->
- Is = filter(fun({line,_}) -> false;
- (_) -> true
- end, Is0),
+ Is = remove_lines_fun(Is0),
[{function,N,A,Lbl,Is}|remove_lines(T)];
remove_lines([]) -> [].
+
+remove_lines_fun([{line,_}|Is]) ->
+ remove_lines_fun(Is);
+remove_lines_fun([{block,Bl0}|Is]) ->
+ Bl = remove_lines_block(Bl0),
+ [{block,Bl}|remove_lines_fun(Is)];
+remove_lines_fun([I|Is]) ->
+ [I|remove_lines_fun(Is)];
+remove_lines_fun([]) -> [].
+
+remove_lines_block([{set,_,_,{line,_}}|Is]) ->
+ remove_lines_block(Is);
+remove_lines_block([I|Is]) ->
+ [I|remove_lines_block(Is)];
+remove_lines_block([]) -> [].
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index 4045ab6dc5..c60211f516 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -73,7 +73,8 @@ norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) ->
{put_map,F,Op,S,D,R,{list,Puts}};
norm({set,[],[],remove_message}) -> remove_message;
norm({set,[],[],fclearerror}) -> fclearerror;
-norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}}.
+norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}};
+norm({set,[],[],{line,_}=Line}) -> Line.
norm_allocate({_Zero,nostack,Nh,[]}, Regs) ->
[{test_heap,Nh,Regs}];
diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl
index d041f18806..52dd89b5bb 100644
--- a/lib/compiler/src/beam_split.erl
+++ b/lib/compiler/src/beam_split.erl
@@ -50,8 +50,9 @@ split_block([{set,[R],[_,_,_]=As,{bif,is_record,{f,Lbl}}}|Is], Bl, Acc) ->
split_block(Is, [], [{bif,is_record,{f,Lbl},As,R}|make_block(Bl, Acc)]);
split_block([{set,[R],As,{bif,N,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 ->
split_block(Is, [], [{bif,N,Fail,As,R}|make_block(Bl, Acc)]);
-split_block([{set,[R],As,{bif,raise,{f,_}=Fail}}|Is], Bl, Acc) ->
- split_block(Is, [], [{bif,raise,Fail,As,R}|make_block(Bl, Acc)]);
+split_block([{set,[],[],{line,_}=Line},
+ {set,[R],As,{bif,raise,{f,_}=Fail}}|Is], Bl, Acc) ->
+ split_block(Is, [], [{bif,raise,Fail,As,R},Line|make_block(Bl, Acc)]);
split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc)
when Lbl =/= 0 ->
split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]);
@@ -61,8 +62,6 @@ split_block([{set,[D],[S|Puts],{alloc,R,{put_map,Op,{f,Lbl}=Fail}}}|Is],
make_block(Bl, Acc)]);
split_block([{set,[R],[],{try_catch,Op,L}}|Is], Bl, Acc) ->
split_block(Is, [], [{Op,R,L}|make_block(Bl, Acc)]);
-split_block([{set,[],[],{line,_}=Line}|Is], Bl, Acc) ->
- split_block(Is, [], [Line|make_block(Bl, Acc)]);
split_block([I|Is], Bl, Acc) ->
split_block(Is, [I|Bl], Acc);
split_block([], Bl, Acc) -> make_block(Bl, Acc).
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index a57dbbbc2f..814cfb8265 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -118,7 +118,7 @@ is_killed(R, Is, D) ->
St = #live{lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{killed,_} -> true;
- {exit_not_used,_} -> true;
+ {exit_not_used,_} -> false;
{_,_} -> false
end.
@@ -131,7 +131,7 @@ is_killed_at(R, Lbl, D) when is_integer(Lbl) ->
St0 = #live{lbl=D,res=gb_trees:empty()},
case check_liveness_at(R, Lbl, St0) of
{killed,_} -> true;
- {exit_not_used,_} -> true;
+ {exit_not_used,_} -> false;
{_,_} -> false
end.
@@ -148,7 +148,7 @@ is_not_used(R, Is, D) ->
St = #live{lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{used,_} -> false;
- {exit_not_used,_} -> false;
+ {exit_not_used,_} -> true;
{_,_} -> true
end.
@@ -1004,47 +1004,52 @@ live_opt([{recv_mark,_}=I|Is], Regs, D, Acc) ->
live_opt([], _, _, Acc) -> Acc.
-live_opt_block([{set,Ds,Ss,Op0}|Is], Regs0, D, Acc) ->
- Regs1 = x_live(Ss, x_dead(Ds, Regs0)),
- {Op, Regs} = live_opt_block_op(Op0, Regs1, D),
- I = {set, Ds, Ss, Op},
-
- case Ds of
- [{x,X}] ->
- case (not is_live(X, Regs0)) andalso Op =:= move of
- true ->
- live_opt_block(Is, Regs0, D, Acc);
- false ->
- live_opt_block(Is, Regs, D, [I|Acc])
- end;
- _ ->
- live_opt_block(Is, Regs, D, [I|Acc])
+live_opt_block([{set,[{x,X}]=Ds,Ss,move}=I|Is], Regs0, D, Acc) ->
+ Regs = x_live(Ss, x_dead(Ds, Regs0)),
+ case is_live(X, Regs0) of
+ true ->
+ live_opt_block(Is, Regs, D, [I|Acc]);
+ false ->
+ %% Useless move, will never be used.
+ live_opt_block(Is, Regs, D, Acc)
end;
-live_opt_block([{'%anno',_}|Is], Regs, D, Acc) ->
- live_opt_block(Is, Regs, D, Acc);
-live_opt_block([], Regs, _, Acc) -> {Acc,Regs}.
-
-live_opt_block_op({alloc,Live0,AllocOp}, Regs0, D) ->
- Regs =
- case AllocOp of
- {Kind, _N, Fail} when Kind =:= gc_bif; Kind =:= put_map ->
- live_join_label(Fail, D, Regs0);
- _ ->
- Regs0
- end,
+live_opt_block([{set,Ds,Ss,{alloc,Live0,AllocOp}}|Is], Regs0, D, Acc) ->
+ %% Calculate liveness from the point of view of the GC.
+ %% There will never be a GC if the instruction fails, so we should
+ %% ignore the failure branch.
+ GcRegs1 = x_dead(Ds, Regs0),
+ GcRegs = x_live(Ss, GcRegs1),
+ Live = live_regs(GcRegs),
%% The life-time analysis used by the code generator is sometimes too
%% conservative, so it may be possible to lower the number of live
%% registers based on the exact liveness information. The main benefit is
%% that more optimizations that depend on liveness information (such as the
- %% beam_bool and beam_dead passes) may be applied.
- Live = live_regs(Regs),
- true = Live =< Live0,
- {{alloc,Live,AllocOp}, live_call(Live)};
-live_opt_block_op({bif,_N,Fail} = Op, Regs, D) ->
- {Op, live_join_label(Fail, D, Regs)};
-live_opt_block_op(Op, Regs, _D) ->
- {Op, Regs}.
+ %% beam_dead pass) may be applied.
+ true = Live =< Live0, %Assertion.
+ I = {set,Ds,Ss,{alloc,Live,AllocOp}},
+
+ %% Calculate liveness from the point of view of the preceding instruction.
+ %% The liveness is the union of live registers in the GC and the live
+ %% registers at the failure label.
+ Regs1 = live_call(Live),
+ Regs = live_join_alloc(AllocOp, D, Regs1),
+ live_opt_block(Is, Regs, D, [I|Acc]);
+live_opt_block([{set,Ds,Ss,{bif,_,Fail}}=I|Is], Regs0, D, Acc) ->
+ Regs1 = x_dead(Ds, Regs0),
+ Regs2 = x_live(Ss, Regs1),
+ Regs = live_join_label(Fail, D, Regs2),
+ live_opt_block(Is, Regs, D, [I|Acc]);
+live_opt_block([{set,Ds,Ss,_}=I|Is], Regs0, D, Acc) ->
+ Regs = x_live(Ss, x_dead(Ds, Regs0)),
+ live_opt_block(Is, Regs, D, [I|Acc]);
+live_opt_block([{'%anno',_}|Is], Regs, D, Acc) ->
+ live_opt_block(Is, Regs, D, Acc);
+live_opt_block([], Regs, _, Acc) -> {Acc,Regs}.
+
+live_join_alloc({Kind,_Name,Fail}, D, Regs) when Kind =:= gc_bif; Kind =:= put_map ->
+ live_join_label(Fail, D, Regs);
+live_join_alloc(_, _, Regs) -> Regs.
live_join_labels([{f,L}|T], D, Regs0) when L =/= 0 ->
Regs = gb_trees:get(L, D) bor Regs0,
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 9de773542e..c30ab34ac7 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1153,6 +1153,7 @@ set_type_y(Type, {y,Y}=Reg, #vst{current=#st{y=Ys0}=St}=Vst)
{value,_} ->
gb_trees:update(Y, Type, Ys0)
end,
+ check_try_catch_tags(Type, Y, Ys0),
Vst#vst{current=St#st{y=Ys}};
set_type_y(Type, Reg, #vst{}) -> error({invalid_store,Reg,Type}).
@@ -1160,6 +1161,29 @@ set_catch_end({y,Y}, #vst{current=#st{y=Ys0}=St}=Vst) ->
Ys = gb_trees:update(Y, initialized, Ys0),
Vst#vst{current=St#st{y=Ys}}.
+check_try_catch_tags(Type, LastY, Ys) ->
+ case is_try_catch_tag(Type) of
+ false ->
+ ok;
+ true ->
+ %% Every catch or try/catch must use a lower Y register
+ %% number than any enclosing catch or try/catch. That will
+ %% ensure that when the stack is scanned when an
+ %% exception occurs, the innermost try/catch tag is found
+ %% first.
+ Bad = [{{y,Y},Tag} || {Y,Tag} <- gb_trees:to_list(Ys),
+ Y < LastY, is_try_catch_tag(Tag)],
+ case Bad of
+ [] ->
+ ok;
+ [_|_] ->
+ error({bad_try_catch_nesting,{y,LastY},Bad})
+ end
+ end.
+
+is_try_catch_tag({catchtag,_}) -> true;
+is_try_catch_tag({trytag,_}) -> true;
+is_try_catch_tag(_) -> false.
is_reg_defined({x,_}=Reg, Vst) -> is_type_defined_x(Reg, Vst);
is_reg_defined({y,_}=Reg, Vst) -> is_type_defined_y(Reg, Vst);
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 63a13281a8..b8fff7b100 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -33,7 +33,7 @@
state_after_fault_in_catch/1,no_exception_in_catch/1,
undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1,
map_field_lists/1,cover_bin_opt/1,
- val_dsetel/1,bad_tuples/1]).
+ val_dsetel/1,bad_tuples/1,bad_try_catch_nesting/1]).
-include_lib("common_test/include/ct.hrl").
@@ -62,7 +62,7 @@ groups() ->
state_after_fault_in_catch,no_exception_in_catch,
undef_label,illegal_instruction,failing_gc_guard_bif,
map_field_lists,cover_bin_opt,val_dsetel,
- bad_tuples]}].
+ bad_tuples,bad_try_catch_nesting]}].
init_per_suite(Config) ->
Config.
@@ -523,6 +523,14 @@ bad_tuples(Config) ->
ok.
+bad_try_catch_nesting(Config) ->
+ Errors = do_val(bad_try_catch_nesting, Config),
+ [{{bad_try_catch_nesting,main,2},
+ {{'try',{y,2},{f,3}},
+ 7,
+ {bad_try_catch_nesting,{y,2},[{{y,1},{trytag,[5]}}]}}}] = Errors,
+ ok.
+
%%%-------------------------------------------------------------------------
transform_remove(Remove, Module) ->
diff --git a/lib/compiler/test/beam_validator_SUITE_data/bad_try_catch_nesting.S b/lib/compiler/test/beam_validator_SUITE_data/bad_try_catch_nesting.S
new file mode 100644
index 0000000000..9f1b21a17b
--- /dev/null
+++ b/lib/compiler/test/beam_validator_SUITE_data/bad_try_catch_nesting.S
@@ -0,0 +1,64 @@
+{module, bad_try_catch_nesting}. %% version = 0
+
+{exports, [{main,2},{module_info,0},{module_info,1}]}.
+
+{attributes, []}.
+
+{labels, 11}.
+
+
+{function, main, 2, 2}.
+ {label,1}.
+ {line,[{location,"bad_try_catch_nesting.erl",4}]}.
+ {func_info,{atom,bad_try_catch_nesting},{atom,main},2}.
+ {label,2}.
+ {allocate_zero,3,2}.
+ {'try',{y,1},{f,5}}.
+ {move,{x,1},{y,0}}.
+ {'try',{y,2},{f,3}}.
+ {line,[{location,"bad_try_catch_nesting.erl",7}]}.
+ {call_fun,0}.
+ {try_end,{y,2}}.
+ {jump,{f,4}}.
+ {label,3}.
+ {try_case,{y,2}}.
+ {test,is_ne_exact,{f,4},[{x,0},{atom,error}]}.
+ {line,[]}.
+ {bif,raise,{f,0},[{x,2},{x,1}],{x,0}}.
+ {label,4}.
+ {move,{y,0},{x,0}}.
+ {kill,{y,0}}.
+ {line,[{location,"bad_try_catch_nesting.erl",12}]}.
+ {call_fun,0}.
+ {try_end,{y,1}}.
+ {deallocate,3}.
+ return.
+ {label,5}.
+ {try_case,{y,1}}.
+ {test,is_eq_exact,{f,6},[{x,0},{atom,throw}]}.
+ {deallocate,3}.
+ return.
+ {label,6}.
+ {line,[]}.
+ {bif,raise,{f,0},[{x,2},{x,1}],{x,0}}.
+
+
+{function, module_info, 0, 8}.
+ {label,7}.
+ {line,[]}.
+ {func_info,{atom,bad_try_catch_nesting},{atom,module_info},0}.
+ {label,8}.
+ {move,{atom,bad_try_catch_nesting},{x,0}}.
+ {line,[]}.
+ {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
+
+
+{function, module_info, 1, 10}.
+ {label,9}.
+ {line,[]}.
+ {func_info,{atom,bad_try_catch_nesting},{atom,module_info},1}.
+ {label,10}.
+ {move,{x,0},{x,1}}.
+ {move,{atom,bad_try_catch_nesting},{x,0}}.
+ {line,[]}.
+ {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl
index f206f967c7..f410542f72 100644
--- a/lib/crypto/test/engine_SUITE.erl
+++ b/lib/crypto/test/engine_SUITE.erl
@@ -72,7 +72,12 @@ groups() ->
init_per_suite(Config) ->
try crypto:start() of
ok ->
- Config;
+ case crypto:info_lib() of
+ [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}] ->
+ {skip, "Problem with engine on OpenSSL 1.0.1s-freebsd"};
+ _ ->
+ Config
+ end;
{error,{already_started,crypto}} ->
Config
catch _:_ ->
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 6bd3c2f9a6..c5f93a3392 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -3444,19 +3444,19 @@ state__fun_info(Fun, #state{callgraph = CG, fun_tab = FunTab, plt = PLT}) ->
{Fun, Sig, Contract, LocalRet}.
forward_args(Fun, ArgTypes, #state{work = Work, fun_tab = FunTab} = State) ->
- {OldArgTypes, OldOut, Fixpoint} =
+ {NewArgTypes, OldOut, Fixpoint} =
case dict:find(Fun, FunTab) of
- {ok, {not_handled, {OldArgTypes0, OldOut0}}} ->
- {OldArgTypes0, OldOut0, false};
+ {ok, {not_handled, {_OldArgTypesAreNone, OldOut0}}} ->
+ {ArgTypes, OldOut0, false};
{ok, {OldArgTypes0, OldOut0}} ->
- {OldArgTypes0, OldOut0,
- t_is_subtype(t_product(ArgTypes), t_product(OldArgTypes0))}
+ NewArgTypes0 = [t_sup(X, Y) ||
+ {X, Y} <- lists:zip(ArgTypes, OldArgTypes0)],
+ {NewArgTypes0, OldOut0,
+ t_is_equal(t_product(NewArgTypes0), t_product(OldArgTypes0))}
end,
case Fixpoint of
true -> State;
false ->
- NewArgTypes = [t_sup(X, Y) ||
- {X, Y} <- lists:zip(ArgTypes, OldArgTypes)],
NewWork = add_work(Fun, Work),
?debug("~tw: forwarding args ~ts\n",
[state__lookup_name(Fun, State),
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl
index 95c8b5ebce..2af4534396 100644
--- a/lib/dialyzer/src/dialyzer_plt.erl
+++ b/lib/dialyzer/src/dialyzer_plt.erl
@@ -531,17 +531,19 @@ compute_md5_from_files(Files) ->
lists:keysort(1, [{F, compute_md5_from_file(F)} || F <- Files]).
compute_md5_from_file(File) ->
- case filelib:is_regular(File) of
- false ->
+ case beam_lib:all_chunks(File) of
+ {ok, _, Chunks} ->
+ %% We cannot use beam_lib:md5 because it does not consider
+ %% the debug_info chunk, where typespecs are likely stored.
+ %% So we consider almost all chunks except the useless ones.
+ Filtered = [[ID, Chunk] || {ID, Chunk} <- Chunks, ID =/= "CInf", ID =/= "Docs"],
+ erlang:md5(lists:sort(Filtered));
+ {error, beam_lib, {file_error, _, enoent}} ->
Msg = io_lib:format("Not a regular file: ~ts\n", [File]),
throw({dialyzer_error, Msg});
- true ->
- case dialyzer_utils:get_core_from_beam(File) of
- {error, Error} ->
- throw({dialyzer_error, Error});
- {ok, Core} ->
- erlang:md5(term_to_binary(Core))
- end
+ {error, beam_lib, _} ->
+ Msg = io_lib:format("Could not compute MD5 for .beam: ~ts\n", [File]),
+ throw({dialyzer_error, Msg})
end.
init_diff_list(RemoveFiles, AddFiles) ->
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl
new file mode 100644
index 0000000000..44149f4199
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same.erl
@@ -0,0 +1,15 @@
+-module(same).
+
+-export([baz/1]).
+
+-record(bar, {
+ a :: same_type:st(integer()),
+ b :: same_type:st(atom())
+ }).
+
+baz(Bar) ->
+ _ = wrap_find(0, Bar#bar.a),
+ wrap_find(0, Bar#bar.b).
+
+wrap_find(K, D) ->
+ same_type:t(K, D).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl
new file mode 100644
index 0000000000..855a5d30be
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para_bug/same_type.erl
@@ -0,0 +1,13 @@
+-module(same_type).
+
+-export([t/2]).
+
+-export_type([st/1]).
+
+%% When unopaqued all specializations of st/1 are equal.
+-opaque st(_A) :: {st, tuple()}.
+
+-spec t(_, st(_)) -> _.
+
+t(K, V) ->
+ {K, V}.
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 6b84b22eb5..6bc7d147c0 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -1865,8 +1865,8 @@ An example return value with for a client service with Origin-Host
{raddr,{127,0,0,1}},
{rport,3868},
{reuseaddr,true}]}]},
- {watchdog,{&lt;0.66.0>,{1346,171491,996448},okay}},
- {peer,{&lt;0.67.0>,{1346,171491,999906}}},
+ {watchdog,{&lt;0.66.0>,-576460736368485571,okay}},
+ {peer,{&lt;0.67.0>,-576460736357885808}},
{apps,[{0,common}]},
{caps,[{origin_host,{"client.example.com","server.example.com"}},
{origin_realm,{"example.com","example.com"}},
@@ -1946,8 +1946,8 @@ connection might look as follows.</p>
{transport_config,[{reuseaddr,true},
{ip,{127,0,0,1}},
{port,3868}]}]},
- {accept,[[{watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
- {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {accept,[[{watchdog,{&lt;0.56.0>,-576460739249514012,okay}},
+ {peer,{&lt;0.58.0>,-576460638229179167}},
{apps,[{0,common}]},
{caps,[{origin_host,{"server.example.com","client.example.com"}},
{origin_realm,{"example.com","example.com"}},
@@ -1976,7 +1976,7 @@ connection might look as follows.</p>
{send_max,148},
{send_avg,87},
{send_pend,0}]}]}],
- [{watchdog,{&lt;0.72.0>,{1346,171491,998404},initial}}]]},
+ [{watchdog,{&lt;0.72.0>,-576460638229717546,initial}}]]},
{statistics,[{{{0,280,0},recv},7},
{{{0,280,1},send},7},
{{{0,280,0},recv,{'Result-Code',2001}},7},
@@ -2024,8 +2024,8 @@ A return value for the server above might look as follows.</p>
{transport_config,[{reuseaddr,true},
{ip,{127,0,0,1}},
{port,3868}]}]},
- {watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
- {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {watchdog,{&lt;0.56.0>,-576460739249514012,okay}},
+ {peer,{&lt;0.58.0>,-576460638229179167}},
{apps,[{0,common}]},
{caps,[{origin_host,{"server.example.com","client.example.com"}},
{origin_realm,{"example.com","example.com"}},
diff --git a/lib/diameter/src/base/diameter_reg.erl b/lib/diameter/src/base/diameter_reg.erl
index 5b7cfab31a..c1762a07e3 100644
--- a/lib/diameter/src/base/diameter_reg.erl
+++ b/lib/diameter/src/base/diameter_reg.erl
@@ -246,8 +246,11 @@ handle_call({add, Uniq, Key}, {Pid, _}, S) ->
handle_call({remove, Key}, {Pid, _}, S) ->
Rec = {Key, Pid},
- ets:delete_object(?TABLE, Rec),
- {reply, true, notify(remove, Rec, S)};
+ {reply, true, try
+ notify(remove, Rec, S)
+ after
+ ets:delete_object(?TABLE, Rec)
+ end};
handle_call({wait, Pat}, {Pid, _} = From, S) ->
NS = add_monitor(Pid, S),
@@ -370,10 +373,12 @@ send({_,_} = From, add, Rec) ->
down(Pid, #state{monitors = Ps} = S) ->
Recs = match('_', Pid),
- ets:match_delete(?TABLE, {'_', Pid}),
- lists:foldl(fun(R,NS) -> notify(remove, R, NS) end,
- flush(Pid, S#state{monitors = sets:del_element(Pid, Ps)}),
- Recs).
+ Acc0 = flush(Pid, S#state{monitors = sets:del_element(Pid, Ps)}),
+ try
+ lists:foldl(fun(R,NS) -> notify(remove, R, NS) end, Acc0, Recs)
+ after
+ ets:match_delete(?TABLE, {'_', Pid})
+ end.
%% flush/3
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 31dd92f878..cbe66ef27a 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -151,7 +151,7 @@
apps :: match([{0..16#FFFFFFFF, diameter:app_alias()}] %% {Id, Alias}
| [diameter:app_alias()]), %% remote
caps :: match(#diameter_caps{}),
- started = diameter_lib:now(), %% at process start or sharing
+ started = diameter_lib:now(), %% at connection_up
watchdog :: match(pid() %% key into watchdogT
| undefined)}). %% undefined if remote
@@ -554,15 +554,25 @@ terminate(Reason, #state{service_name = Name, local = {PeerT, _, _}} = S) ->
%% wait for watchdog state changes to take care of if. That this
%% takes place after deleting the state entry ensures that the
%% resulting failover by request processes accomplishes nothing.
- ets:foldl(fun(#peer{pid = TPid}, _) ->
- diameter_traffic:peer_down(TPid)
- end,
- ok,
- PeerT),
+ ets:foldl(fun peer_down/2, ok, PeerT),
shutdown == Reason %% application shutdown
andalso shutdown(application, S).
+%% peer_down/1
+%%
+%% Entries with watchdog state SUSPECT are already down: ignore the
+%% expected failure. This assumes the current implementation, but
+%% double the number of lookups (in the typical case) could be the
+%% greater evil if there are many peer connections.
+
+peer_down(#peer{pid = TPid}, _) ->
+ try
+ diameter_traffic:peer_down(TPid)
+ catch
+ error: {badmatch, []} -> ok
+ end.
+
%% ---------------------------------------------------------------------------
%% # code_change/3
%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 7da59f8b25..05a8c9378e 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -54,10 +54,10 @@
{"1.12.1", [{restart_application, diameter}]}, %% 19.1
{"1.12.2", [{restart_application, diameter}]}, %% 19.3
{"2.0", [{restart_application, diameter}]}, %% 20.0
- {"2.1", [{load_module, diameter_gen}, %% 20.1
- {update, diameter_reg, {advanced, "2.1"}}]},
- {"2.1.1", [{load_module, diameter_gen}]}, %% 20.1.2
- {"2.1.2", []} %% 20.1.3
+ {"2.1", [{restart_application, diameter}]}, %% 20.1
+ {"2.1.1", [{restart_application, diameter}]}, %% 20.1.2
+ {"2.1.2", [{restart_application, diameter}]}, %% 20.1.3
+ {"2.1.3", [{restart_application, diameter}]} %% 20.2
],
[
{"0.9", [{restart_application, diameter}]},
@@ -94,7 +94,8 @@
{"1.12.2", [{restart_application, diameter}]},
{"2.0", [{restart_application, diameter}]},
{"2.1", [{restart_application, diameter}]},
- {"2.1.1", [{load_module, diameter_gen}]},
- {"2.1.2", []}
+ {"2.1.1", [{restart_application, diameter}]},
+ {"2.1.2", [{restart_application, diameter}]},
+ {"2.1.3", [{restart_application, diameter}]}
]
}.
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index 0c852d75cd..b0fb4ada28 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -1,6 +1,6 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2017. All Rights Reserved.
+# Copyright Ericsson AB 2010-2018. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,5 +17,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 2.1.3
+DIAMETER_VSN = 2.1.4
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/hipe/icode/hipe_icode_inline_bifs.erl b/lib/hipe/icode/hipe_icode_inline_bifs.erl
index 7a6947f190..16a95991e7 100644
--- a/lib/hipe/icode/hipe_icode_inline_bifs.erl
+++ b/lib/hipe/icode/hipe_icode_inline_bifs.erl
@@ -24,8 +24,9 @@
%% Currently inlined BIFs:
%% and, or, xor, not, <, >, >=, =<, ==, /=, =/=, =:=
-%% is_atom, is_boolean, is_binary, is_float, is_function,
-%% is_integer, is_list, is_pid, is_port, is_reference, is_tuple
+%% is_atom, is_binary, is_bitstring, is_boolean, is_float,
+%% is_function, is_integer, is_list, is_map, is_number,
+%% is_pid, is_port, is_reference, is_tuple
-module(hipe_icode_inline_bifs).
@@ -116,17 +117,20 @@ try_type_tests(I) -> I.
is_type_test(Name) ->
case Name of
- is_integer -> {true, integer};
+ is_atom -> {true, atom};
+ is_binary -> {true, binary};
+ is_bitstring -> {true, bitstr};
+ is_boolean -> {true, boolean};
is_float -> {true, float};
- is_tuple -> {true, tuple};
- is_binary -> {true, binary};
+ is_function -> {true, function};
+ is_integer -> {true, integer};
is_list -> {true, list};
+ is_map -> {true, map};
+ is_number -> {true, number};
is_pid -> {true, pid};
- is_atom -> {true, atom};
- is_boolean -> {true, boolean};
- is_function -> {true, function};
- is_reference -> {true, reference};
is_port -> {true, port};
+ is_reference -> {true, reference};
+ is_tuple -> {true, tuple};
_ -> false
end.
diff --git a/lib/inets/doc/src/http_client.xml b/lib/inets/doc/src/http_client.xml
index 212958f17f..15e383ec77 100644
--- a/lib/inets/doc/src/http_client.xml
+++ b/lib/inets/doc/src/http_client.xml
@@ -97,27 +97,32 @@
7 > {ok, {{NewVersion, 200, NewReasonPhrase}, NewHeaders, NewBody}} =
httpc:request(get, {"http://www.erlang.org", [{"connection", "close"}]},
[], []).</code>
-
+ <p>This sends an HTTP request over a unix domain socket (experimental):</p>
+ <code type="erl">
+ 8 > httpc:set_options([{ipfamily, local},
+ {unix_socket,"/tmp/unix_socket/consul_http.sock"}]).
+ 9 > {ok, {{NewVersion, 200, NewReasonPhrase}, NewHeaders, NewBody}} =
+ httpc:request(put, {"http:///v1/kv/foo", [], [], "hello"}, [], []).</code>
<p>Start an HTTP client profile:</p>
<code><![CDATA[
- 8 > {ok, Pid} = inets:start(httpc, [{profile, foo}]).
+ 10 > {ok, Pid} = inets:start(httpc, [{profile, foo}]).
{ok, <0.45.0>}
]]></code>
<p>The new profile has no proxy settings, so the connection is refused:</p>
<code type="erl">
- 9 > httpc:request("http://www.erlang.org", foo).
+ 11 > httpc:request("http://www.erlang.org", foo).
{error, econnrefused}</code>
<p>Stop the HTTP client profile:</p>
<code type="erl">
- 10 > inets:stop(httpc, foo).
+ 12 > inets:stop(httpc, foo).
ok</code>
<p>Alternative way to stop the HTTP client profile:</p>
<code type="erl">
- 10 > inets:stop(httpc, Pid).
+ 13 > inets:stop(httpc, Pid).
ok</code>
</section>
diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml
index 20c042c202..f57214a7ce 100644
--- a/lib/inets/doc/src/http_uri.xml
+++ b/lib/inets/doc/src/http_uri.xml
@@ -45,7 +45,6 @@
this module:</p>
<p><c>boolean() = true | false</c></p>
<p><c>string()</c> = list of ASCII characters</p>
- <p><c>unicode_binary()</c> = binary() with characters encoded in the UTF-8 coding standard</p>
</section>
@@ -54,22 +53,22 @@
<p>Type definitions that are related to URI:</p>
<taglist>
- <tag><c>uri() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>uri() = string() | binary()</c></tag>
<item><p>Syntax according to the URI definition in RFC 3986,
for example, "http://www.erlang.org/"</p></item>
- <tag><c>user_info() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>user_info() = string() | binary()</c></tag>
<item><p></p></item>
<tag><c>scheme() = atom()</c></tag>
<item><p>Example: http, https</p></item>
- <tag><c>host() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>host() = string() | binary()</c></tag>
<item><p></p></item>
- <tag><c>port() = pos_integer()</c></tag>
+ <tag><c>port() = inet:port_number()</c></tag>
<item><p></p></item>
- <tag><c>path() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>path() = string() | binary()</c></tag>
<item><p>Represents a file path or directory path</p></item>
- <tag><c>query() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>query() = string() | binary()</c></tag>
<item><p></p></item>
- <tag><c>fragment() = string() | unicode:unicode_binary()</c></tag>
+ <tag><c>fragment() = string() | binary()</c></tag>
<item><p></p></item>
</taglist>
@@ -84,7 +83,7 @@
<fsummary>Decodes a hexadecimal encoded URI.</fsummary>
<type>
- <v>HexEncodedURI = string() | unicode:unicode_binary() - A possibly hexadecimal encoded URI</v>
+ <v>HexEncodedURI = string() | binary() - A possibly hexadecimal encoded URI</v>
<v>URI = uri()</v>
</type>
@@ -99,7 +98,7 @@
<fsummary>Encodes a hexadecimal encoded URI.</fsummary>
<type>
<v>URI = uri()</v>
- <v>HexEncodedURI = string() | unicode:unicode_binary() - Hexadecimal encoded URI</v>
+ <v>HexEncodedURI = string() | binary() - Hexadecimal encoded URI</v>
</type>
<desc>
@@ -119,12 +118,13 @@
<v>Option = {ipv6_host_with_brackets, boolean()} |
{scheme_defaults, scheme_defaults()} |
{fragment, boolean()} |
- {scheme_validation_fun, fun()}]</v>
+ {scheme_validation_fun, fun()}</v>
<v>Result = {Scheme, UserInfo, Host, Port, Path, Query} |
{Scheme, UserInfo, Host, Port, Path, Query, Fragment}</v>
+ <v>Scheme = scheme()</v>
<v>UserInfo = user_info()</v>
<v>Host = host()</v>
- <v>Port = pos_integer()</v>
+ <v>Port = inet:port_number()</v>
<v>Path = path()</v>
<v>Query = query()</v>
<v>Fragment = fragment()</v>
@@ -146,13 +146,20 @@
<p>Scheme validation fun is to be defined as follows:</p>
<code>
-fun(SchemeStr :: string() | unicode:unicode_binary()) ->
+fun(SchemeStr :: string() | binary()) ->
valid | {error, Reason :: term()}.
</code>
<p>It is called before scheme string gets converted into scheme atom and
thus possible atom leak could be prevented</p>
+ <warning>
+ <p>The scheme portion of the URI gets converted into atom,
+ meaning that atom leak may occur. Specifying a scheme
+ validation fun is recommended unless the URI is already
+ sanitized.</p>
+ </warning>
+
<marker id="encode"></marker>
</desc>
</func>
@@ -162,7 +169,7 @@ fun(SchemeStr :: string() | unicode:unicode_binary()) ->
<fsummary>A list of the scheme and their default ports.</fsummary>
<type>
<v>SchemeDefaults = [{scheme(), default_scheme_port_number()}] </v>
- <v>default_scheme_port_number() = pos_integer()</v>
+ <v>default_scheme_port_number() = inet:port_number()</v>
</type>
<desc>
<p>Provides a list of the scheme and their default
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 58714328c5..14662f257c 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -210,7 +210,8 @@
ip |
port |
socket_opts |
- verbose</v>
+ verbose |
+ unix_socket</v>
<v>Profile = profile() | pid()</v>
<d>When started <c>stand_alone</c> only the pid can used.</d>
<v>Values = [{option_item(), term()}]</v>
@@ -297,8 +298,8 @@
{full_result, boolean()} |
{headers_as_is, boolean() |
{socket_opts, socket_opts()} |
- {receiver, receiver()},
- {ipv6_host_with_brackets, boolean()}}</v>
+ {receiver, receiver()} |
+ {ipv6_host_with_brackets, boolean()}</v>
<v>stream_to() = none | self | {self, once} | filename()</v>
<v>socket_opts() = [socket_opt()]</v>
<v>receiver() = pid() | function()/1 | {Module, Function, Args}</v>
@@ -533,7 +534,8 @@
<v>| {ip, IpAddress}</v>
<v>| {port, Port}</v>
<v>| {socket_opts, socket_opts()}</v>
- <v>| {verbose, VerboseMode}</v>
+ <v>| {verbose, VerboseMode}</v>
+ <v>| {unix_socket, UnixSocket}</v>
<v>Proxy = {Hostname, Port}</v>
<v>Hostname = string()</v>
<d>Example: "localhost" or "foo.bar.se"</d>
@@ -576,7 +578,7 @@
If option <c>verify</c> is used, function <c>store_cookies/2</c>
has to be called for the cookies to be saved.
Default is <c>disabled</c>.</d>
- <v>IpFamily = inet | inet6 </v>
+ <v>IpFamily = inet | inet6 | local</v>
<d>Default is <c>inet</c>.</d>
<v>IpAddress = ip_address()</v>
<d>If the host has several network interfaces, this option specifies
@@ -601,6 +603,12 @@
It is a debug feature.</d>
<v>Profile = profile() | pid()</v>
<d>When started <c>stand_alone</c> only the pid can be used.</d>
+ <v>UnixSocket = path()</v>
+ <d>
+ Experimental option for sending HTTP requests over a unix domain socket. The value
+ of <c>unix_socket</c> shall be the full path to a unix domain socket file with read/write
+ permissions for the erlang process. Default is <c>undefined</c>.
+ </d>
</type>
<desc>
<p>Sets options to be used for subsequent requests.</p>
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index 821eb7f02f..a73503a5ce 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -171,6 +171,7 @@ request(Method,
HTTPOptions, Options, Profile)
when (Method =:= options) orelse
(Method =:= get) orelse
+ (Method =:= put) orelse
(Method =:= head) orelse
(Method =:= delete) orelse
(Method =:= trace) andalso
@@ -531,6 +532,7 @@ handle_request(Method, Url,
Stream = proplists:get_value(stream, Options),
Receiver = proplists:get_value(receiver, Options),
SocketOpts = proplists:get_value(socket_opts, Options),
+ UnixSocket = proplists:get_value(unix_socket, Options),
BracketedHost = proplists:get_value(ipv6_host_with_brackets,
Options),
@@ -558,6 +560,7 @@ handle_request(Method, Url,
headers_as_is = headers_as_is(Headers0, Options),
socket_opts = SocketOpts,
started = Started,
+ unix_socket = UnixSocket,
ipv6_host_with_brackets = BracketedHost},
case httpc_manager:request(Request, profile_name(Profile)) of
{ok, RequestId} ->
@@ -823,7 +826,7 @@ request_options_defaults() ->
error
end,
- VerifyBrackets = VerifyBoolean,
+ VerifyBrackets = VerifyBoolean,
[
{sync, true, VerifySync},
@@ -894,11 +897,36 @@ request_options_sanity_check(Opts) ->
end,
ok.
-validate_options(Options) ->
- (catch validate_options(Options, [])).
-
-validate_options([], ValidateOptions) ->
- {ok, lists:reverse(ValidateOptions)};
+validate_ipfamily_unix_socket(Options0) ->
+ IpFamily = proplists:get_value(ipfamily, Options0, inet),
+ UnixSocket = proplists:get_value(unix_socket, Options0, undefined),
+ Options1 = proplists:delete(ipfamily, Options0),
+ Options2 = proplists:delete(ipfamily, Options1),
+ validate_ipfamily_unix_socket(IpFamily, UnixSocket, Options2,
+ [{ipfamily, IpFamily}, {unix_socket, UnixSocket}]).
+%%
+validate_ipfamily_unix_socket(local, undefined, _Options, _Acc) ->
+ bad_option(unix_socket, undefined);
+validate_ipfamily_unix_socket(IpFamily, UnixSocket, _Options, _Acc)
+ when IpFamily =/= local, UnixSocket =/= undefined ->
+ bad_option(ipfamily, IpFamily);
+validate_ipfamily_unix_socket(IpFamily, UnixSocket, Options, Acc) ->
+ validate_ipfamily(IpFamily),
+ validate_unix_socket(UnixSocket),
+ {Options, Acc}.
+
+
+validate_options(Options0) ->
+ try
+ {Options, Acc} = validate_ipfamily_unix_socket(Options0),
+ validate_options(Options, Acc)
+ catch
+ error:Reason ->
+ {error, Reason}
+ end.
+%%
+validate_options([], ValidOptions) ->
+ {ok, lists:reverse(ValidOptions)};
validate_options([{proxy, Proxy} = Opt| Tail], Acc) ->
validate_proxy(Proxy),
@@ -958,6 +986,10 @@ validate_options([{verbose, Value} = Opt| Tail], Acc) ->
validate_verbose(Value),
validate_options(Tail, [Opt | Acc]);
+validate_options([{unix_socket, Value} = Opt| Tail], Acc) ->
+ validate_unix_socket(Value),
+ validate_options(Tail, [Opt | Acc]);
+
validate_options([{_, _} = Opt| _], _Acc) ->
{error, {not_an_option, Opt}}.
@@ -1026,7 +1058,8 @@ validate_ipv6(BadValue) ->
bad_option(ipv6, BadValue).
validate_ipfamily(Value)
- when (Value =:= inet) orelse (Value =:= inet6) orelse (Value =:= inet6fb4) ->
+ when (Value =:= inet) orelse (Value =:= inet6) orelse
+ (Value =:= inet6fb4) orelse (Value =:= local) ->
Value;
validate_ipfamily(BadValue) ->
bad_option(ipfamily, BadValue).
@@ -1056,6 +1089,15 @@ validate_verbose(Value)
validate_verbose(BadValue) ->
bad_option(verbose, BadValue).
+validate_unix_socket(Value)
+ when (Value =:= undefined) ->
+ Value;
+validate_unix_socket(Value)
+ when is_list(Value) andalso length(Value) > 0 ->
+ Value;
+validate_unix_socket(BadValue) ->
+ bad_option(unix_socket, BadValue).
+
bad_option(Option, BadValue) ->
throw({error, {bad_option, Option, BadValue}}).
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 1d569bd2ff..9b09832eb8 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -711,9 +711,9 @@ do_handle_info({'EXIT', _, _}, State = #state{request = undefined}) ->
%% can retry requests in the pipeline.
do_handle_info({'EXIT', _, _}, State) ->
{noreply, State#state{status = close}}.
-
+
call(Msg, Pid) ->
- try gen_server:call(Pid, Msg)
+ try gen_server:call(Pid, Msg, infinity)
catch
exit:{noproc, _} ->
{error, closed};
@@ -754,6 +754,7 @@ connect(SocketType, ToAddress,
#options{ipfamily = IpFamily,
ip = FromAddress,
port = FromPort,
+ unix_socket = UnixSocket,
socket_opts = Opts0}, Timeout) ->
Opts1 =
case FromPort of
@@ -789,6 +790,16 @@ connect(SocketType, ToAddress,
OK ->
OK
end;
+ local ->
+ Opts3 = [IpFamily | Opts2],
+ SocketAddr = {local, UnixSocket},
+ case http_transport:connect(SocketType, {SocketAddr, 0}, Opts3, Timeout) of
+ {error, Reason} ->
+ {error, {failed_connect, [{to_address, SocketAddr},
+ {IpFamily, Opts3, Reason}]}};
+ Else ->
+ Else
+ end;
_ ->
Opts3 = [IpFamily | Opts2],
case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of
@@ -800,9 +811,23 @@ connect(SocketType, ToAddress,
end
end.
-connect_and_send_first_request(Address, Request, #state{options = Options} = State) ->
+handle_unix_socket_options(#request{unix_socket = UnixSocket}, Options)
+ when UnixSocket =:= undefined ->
+ Options;
+
+handle_unix_socket_options(#request{unix_socket = UnixSocket},
+ Options = #options{ipfamily = IpFamily}) ->
+ case IpFamily of
+ local ->
+ Options#options{unix_socket = UnixSocket};
+ Else ->
+ error({badarg, [{ipfamily, Else}, {unix_socket, UnixSocket}]})
+ end.
+
+connect_and_send_first_request(Address, Request, #state{options = Options0} = State) ->
SocketType = socket_type(Request),
ConnTimeout = (Request#request.settings)#http_options.connect_timeout,
+ Options = handle_unix_socket_options(Request, Options0),
case connect(SocketType, Address, Options, ConnTimeout) of
{ok, Socket} ->
ClientClose =
@@ -841,9 +866,10 @@ connect_and_send_first_request(Address, Request, #state{options = Options} = Sta
{ok, State#state{request = Request}}
end.
-connect_and_send_upgrade_request(Address, Request, #state{options = Options} = State) ->
+connect_and_send_upgrade_request(Address, Request, #state{options = Options0} = State) ->
ConnTimeout = (Request#request.settings)#http_options.connect_timeout,
SocketType = ip_comm,
+ Options = handle_unix_socket_options(Request, Options0),
case connect(SocketType, Address, Options, ConnTimeout) of
{ok, Socket} ->
SessionType = httpc_manager:session_type(Options),
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index 5f8c70f28d..c5fe439722 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -83,10 +83,11 @@
max_sessions = ?HTTP_MAX_TCP_SESSIONS,
cookies = disabled, % enabled | disabled | verify
verbose = false, % boolean(),
- ipfamily = inet, % inet | inet6 | inet6fb4
+ ipfamily = inet, % inet | inet6 | inet6fb4 | local
ip = default, % specify local interface
port = default, % specify local port
- socket_opts = [] % other socket options
+ socket_opts = [], % other socket options
+ unix_socket = undefined % Local unix socket
}
).
-type options() :: #options{}.
@@ -115,6 +116,7 @@
% request
timer :: undefined | reference(),
socket_opts, % undefined | [socket_option()]
+ unix_socket, % undefined | string()
ipv6_host_with_brackets % boolean()
}
).
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index ffdf1603b3..7b8d7875de 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -553,7 +553,8 @@ handle_cast({set_options, Options}, State = #state{options = OldOptions}) ->
ip = get_ip(Options, OldOptions),
port = get_port(Options, OldOptions),
verbose = get_verbose(Options, OldOptions),
- socket_opts = get_socket_opts(Options, OldOptions)
+ socket_opts = get_socket_opts(Options, OldOptions),
+ unix_socket = get_unix_socket_opts(Options, OldOptions)
},
case {OldOptions#options.verbose, NewOptions#options.verbose} of
{Same, Same} ->
@@ -963,7 +964,10 @@ get_option(ip, #options{ip = IP}) ->
get_option(port, #options{port = Port}) ->
Port;
get_option(socket_opts, #options{socket_opts = SocketOpts}) ->
- SocketOpts.
+ SocketOpts;
+get_option(unix_socket, #options{unix_socket = UnixSocket}) ->
+ UnixSocket.
+
get_proxy(Opts, #options{proxy = Default}) ->
proplists:get_value(proxy, Opts, Default).
@@ -1016,6 +1020,8 @@ get_verbose(Opts, #options{verbose = Default}) ->
get_socket_opts(Opts, #options{socket_opts = Default}) ->
proplists:get_value(socket_opts, Opts, Default).
+get_unix_socket_opts(Opts, #options{unix_socket = Default}) ->
+ proplists:get_value(unix_socket, Opts, Default).
handle_verbose(debug) ->
dbg:p(self(), [call]),
diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl
index 7f1ca02014..d02913121c 100644
--- a/lib/inets/src/http_lib/http_uri.erl
+++ b/lib/inets/src/http_lib/http_uri.erl
@@ -61,19 +61,35 @@
scheme_defaults/0,
encode/1, decode/1]).
--export_type([scheme/0, default_scheme_port_number/0]).
+-export_type([uri/0,
+ user_info/0,
+ scheme/0, default_scheme_port_number/0,
+ host/0,
+ path/0,
+ query/0,
+ fragment/0]).
+-type uri() :: string() | binary().
+-type user_info() :: string() | binary().
+-type scheme() :: atom().
+-type host() :: string() | binary().
+-type path() :: string() | binary().
+-type query() :: string() | binary().
+-type fragment() :: string() | binary().
+-type port_number() :: inet:port_number().
+-type default_scheme_port_number() :: port_number().
+-type hex_uri() :: string() | binary(). %% Hexadecimal encoded URI.
+-type maybe_hex_uri() :: string() | binary(). %% A possibly hexadecimal encoded URI.
+
+-type scheme_defaults() :: [{scheme(), default_scheme_port_number()}].
+-type scheme_validation_fun() :: fun((SchemeStr :: string() | binary()) ->
+ valid | {error, Reason :: term()}).
%%%=========================================================================
%%% API
%%%=========================================================================
--type scheme() :: atom().
--type default_scheme_port_number() :: pos_integer().
-
--spec scheme_defaults() ->
- [{scheme(), default_scheme_port_number()}].
-
+-spec scheme_defaults() -> scheme_defaults().
scheme_defaults() ->
[{http, 80},
{https, 443},
@@ -82,9 +98,20 @@ scheme_defaults() ->
{sftp, 22},
{tftp, 69}].
+-type parse_result() ::
+ {scheme(), user_info(), host(), port_number(), path(), query()} |
+ {scheme(), user_info(), host(), port_number(), path(), query(),
+ fragment()}.
+
+-spec parse(uri()) -> {ok, parse_result()} | {error, term()}.
parse(AbsURI) ->
parse(AbsURI, []).
+-spec parse(uri(), [Option]) -> {ok, parse_result()} | {error, term()} when
+ Option :: {ipv6_host_with_brackets, boolean()} |
+ {scheme_defaults, scheme_defaults()} |
+ {fragment, boolean()} |
+ {scheme_validation_fun, scheme_validation_fun() | none}.
parse(AbsURI, Opts) ->
case parse_scheme(AbsURI, Opts) of
{error, Reason} ->
@@ -105,6 +132,7 @@ reserved() ->
$#, $[, $], $<, $>, $\", ${, $}, $|, %"
$\\, $', $^, $%, $ ]).
+-spec encode(uri()) -> hex_uri().
encode(URI) when is_list(URI) ->
Reserved = reserved(),
lists:append([uri_encode(Char, Reserved) || Char <- URI]);
@@ -112,6 +140,7 @@ encode(URI) when is_binary(URI) ->
Reserved = reserved(),
<< <<(uri_encode_binary(Char, Reserved))/binary>> || <<Char>> <= URI >>.
+-spec decode(maybe_hex_uri()) -> uri().
decode(String) when is_list(String) ->
do_decode(String);
decode(String) when is_binary(String) ->
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index fdf4cc6e07..a86413147c 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,14 +18,10 @@
%% %CopyrightEnd%
{"%VSN%",
[
- {<<"6.4.3">>, [{load_module, httpd_esi,
- soft_purge, soft_purge, []}]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
],
[
- {<<"6.4.3">>, [{load_module, httpd_esi,
- soft_purge, soft_purge, []}]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
]
diff --git a/lib/inets/test/http_test_lib.erl b/lib/inets/test/http_test_lib.erl
index 38e9e4976e..4e119cce04 100644
--- a/lib/inets/test/http_test_lib.erl
+++ b/lib/inets/test/http_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -55,6 +55,25 @@ dummy_server_init(Caller, ip_comm, Inet, Extra) ->
]]},
[], ContentCb, Conf, ListenSocket);
+dummy_server_init(Caller, unix_socket, Inet, Extra) ->
+ ContentCb = proplists:get_value(content_cb, Extra),
+ UnixSocket = proplists:get_value(unix_socket, Extra),
+ SocketAddr = {local, UnixSocket},
+ BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}, {nodelay, true},
+ {ifaddr, SocketAddr}],
+ Conf = proplists:get_value(conf, Extra),
+ {ok, ListenSocket} = gen_tcp:listen(0, [Inet | BaseOpts]),
+ {ok, Port} = inet:port(ListenSocket),
+ Caller ! {port, Port},
+ dummy_ipcomm_server_loop({httpd_request, parse, [[{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]},
+ [], ContentCb, Conf, ListenSocket);
+
dummy_server_init(Caller, ssl, Inet, Extra) ->
ContentCb = proplists:get_value(content_cb, Extra),
SSLOptions = proplists:get_value(ssl, Extra),
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 0533b9ab70..38705372c9 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -37,6 +37,10 @@
-define(TLS_URL_START, "https://").
-define(NOT_IN_USE_PORT, 8997).
+%% Using hardcoded file path to keep it below 107 charaters
+%% (maximum length supported by erlang)
+-define(UNIX_SOCKET, "/tmp/inets_httpc_SUITE.sock").
+
-record(sslsocket, {fd = nil, pid = nil}).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -50,6 +54,8 @@ all() ->
[
{group, http},
{group, sim_http},
+ {group, http_internal},
+ {group, http_unix_socket},
{group, https},
{group, sim_https},
{group, misc}
@@ -62,6 +68,8 @@ groups() ->
%% and it shall be the last test case in the suite otherwise cookie
%% will fail.
{sim_http, [], only_simulated() ++ [process_leak_on_keepalive]},
+ {http_internal, [], real_requests_esi()},
+ {http_unix_socket, [], simulated_unix_socket()},
{https, [], real_requests()},
{sim_https, [], only_simulated()},
{misc, [], misc()}
@@ -97,6 +105,12 @@ real_requests()->
invalid_body
].
+real_requests_esi() ->
+ [slow_connection].
+
+simulated_unix_socket() ->
+ [unix_domain_socket].
+
only_simulated() ->
[
cookie,
@@ -182,15 +196,29 @@ init_per_group(Group, Config0) when Group =:= sim_https; Group =:= https->
_:_ ->
{skip, "Crypto did not start"}
end;
-
+init_per_group(http_unix_socket = Group, Config0) ->
+ case os:type() of
+ {win32,_} ->
+ {skip, "Unix Domain Sockets are not supported on Windows"};
+ _ ->
+ file:delete(?UNIX_SOCKET),
+ start_apps(Group),
+ Config = proplists:delete(port, Config0),
+ Port = server_start(Group, server_config(Group, Config)),
+ [{port, Port} | Config]
+ end;
init_per_group(Group, Config0) ->
start_apps(Group),
Config = proplists:delete(port, Config0),
Port = server_start(Group, server_config(Group, Config)),
[{port, Port} | Config].
+end_per_group(http_unix_socket,_Config) ->
+ file:delete(?UNIX_SOCKET),
+ ok;
end_per_group(_, _Config) ->
ok.
+
do_init_per_group(Group, Config0) ->
Config = proplists:delete(port, Config0),
Port = server_start(Group, server_config(Group, Config)),
@@ -1245,7 +1273,40 @@ stream_fun_server_close(Config) when is_list(Config) ->
after 13000 ->
ct:fail(did_not_receive_close)
end.
-
+
+%%--------------------------------------------------------------------
+slow_connection() ->
+ [{doc, "Test that a request on a slow keep-alive connection won't crash the httpc_manager"}].
+slow_connection(Config) when is_list(Config) ->
+ BodyFun = fun(0) -> eof;
+ (LenLeft) -> timer:sleep(1000),
+ {ok, lists:duplicate(10, "1"), LenLeft - 10}
+ end,
+ Request = {url(group_name(Config), "/httpc_SUITE:esi_post", Config),
+ [{"content-length", "100"}],
+ "text/plain",
+ {BodyFun, 100}},
+ {ok, _} = httpc:request(post, Request, [], []),
+ %% Second request causes a crash if gen_server timeout is not set to infinity
+ %% in httpc_handler.
+ {ok, _} = httpc:request(post, Request, [], []).
+
+%%-------------------------------------------------------------------------
+unix_domain_socket() ->
+ [{"doc, Test HTTP requests over unix domain sockets"}].
+unix_domain_socket(Config) when is_list(Config) ->
+
+ URL = "http:///v1/kv/foo",
+
+ {ok,[{unix_socket,?UNIX_SOCKET}]} =
+ httpc:get_options([unix_socket]),
+ {ok, {{_,200,_}, [_ | _], _}}
+ = httpc:request(put, {URL, [], [], ""}, [], []),
+ {ok, {{_,200,_}, [_ | _], _}}
+ = httpc:request(get, {URL, []}, [], []).
+
+
+
%%--------------------------------------------------------------------
%% Internal Functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -1339,6 +1400,8 @@ url(https, End, Config) ->
?TLS_URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End;
url(sim_http, End, Config) ->
url(http, End, Config);
+url(http_internal, End, Config) ->
+ url(http, End, Config);
url(sim_https, End, Config) ->
url(https, End, Config).
url(http, UserInfo, End, Config) ->
@@ -1358,19 +1421,28 @@ group_name(Config) ->
server_start(sim_http, _) ->
Inet = inet_version(),
- ok = httpc:set_options([{ipfamily, Inet}]),
+ ok = httpc:set_options([{ipfamily, Inet},{unix_socket, undefined}]),
{_Pid, Port} = http_test_lib:dummy_server(ip_comm, Inet, [{content_cb, ?MODULE}]),
Port;
server_start(sim_https, SslConfig) ->
Inet = inet_version(),
- ok = httpc:set_options([{ipfamily, Inet}]),
+ ok = httpc:set_options([{ipfamily, Inet},{unix_socket, undefined}]),
{_Pid, Port} = http_test_lib:dummy_server(ssl, Inet, [{ssl, SslConfig}, {content_cb, ?MODULE}]),
Port;
+server_start(http_unix_socket, Config) ->
+ Inet = local,
+ Socket = proplists:get_value(unix_socket, Config),
+ ok = httpc:set_options([{ipfamily, Inet},{unix_socket, Socket}]),
+ {_Pid, Port} = http_test_lib:dummy_server(unix_socket, Inet, [{content_cb, ?MODULE},
+ {unix_socket, Socket}]),
+ Port;
+
server_start(_, HttpdConfig) ->
{ok, Pid} = inets:start(httpd, HttpdConfig),
Serv = inets:services_info(),
+ ok = httpc:set_options([{ipfamily, inet_version()},{unix_socket, undefined}]),
{value, {_, _, Info}} = lists:keysearch(Pid, 2, Serv),
proplists:get_value(port, Info).
@@ -1385,14 +1457,31 @@ server_config(http, Config) ->
{mime_type, "text/plain"},
{script_alias, {"/cgi-bin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}}
];
-
+server_config(http_internal, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ [{port, 0},
+ {server_name,"httpc_test"},
+ {server_root, ServerRoot},
+ {document_root, proplists:get_value(doc_root, Config)},
+ {bind_address, any},
+ {ipfamily, inet_version()},
+ {mime_type, "text/plain"},
+ {erl_script_alias, {"", [httpc_SUITE]}}
+ ];
server_config(https, Config) ->
[{socket_type, {essl, ssl_config(Config)}} | server_config(http, Config)];
server_config(sim_https, Config) ->
ssl_config(Config);
+server_config(http_unix_socket, _Config) ->
+ Socket = ?UNIX_SOCKET,
+ [{unix_socket, Socket}];
+
server_config(_, _) ->
[].
+esi_post(Sid, _Env, _Input) ->
+ mod_esi:deliver(Sid, ["OK"]).
+
start_apps(https) ->
inets_test_lib:start_apps([crypto, public_key, ssl]);
start_apps(sim_https) ->
@@ -2131,6 +2220,19 @@ handle_uri(_,"/delay_close.html",_,_,Socket,_) ->
handle_uri("HEAD",_,_,_,_,_) ->
"HTTP/1.1 200 ok\r\n" ++
"Content-Length:0\r\n\r\n";
+handle_uri("PUT","/v1/kv/foo",_,_,_,_) ->
+ "HTTP/1.1 200 OK\r\n" ++
+ "Date: Tue, 20 Feb 2018 14:39:08 GMT\r\n" ++
+ "Content-Length: 5\r\n\r\n" ++
+ "Content-Type: application/json\r\n\r\n" ++
+ "true\n";
+handle_uri("GET","/v1/kv/foo",_,_,_,_) ->
+ "HTTP/1.1 200 OK\r\n" ++
+ "Date: Tue, 20 Feb 2018 14:39:08 GMT\r\n" ++
+ "Content-Length: 24\r\n" ++
+ "Content-Type: application/json\r\n\r\n" ++
+ "[{\"Value\": \"aGVsbG8=\"}]\n";
+
handle_uri(_,_,_,_,_,DefaultResponse) ->
DefaultResponse.
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 05cf4f6cc3..1fad9afe33 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.4.5
+INETS_VSN = 6.5
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 8477b0e148..1b72769ce3 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -981,8 +981,7 @@ f.txt: {person, "kalle", 25}.
</item>
<tag><c>eisdir</c></tag>
<item>
- <p>The named file is not a regular file. It can be a
- directory, a FIFO, or a device.</p>
+ <p>The named file is a directory.</p>
</item>
<tag><c>enotdir</c></tag>
<item>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index abb045b744..9552332948 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2017</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -277,9 +277,7 @@ fe80::204:acff:fe17:bf38
<p>Returns a <c>hostent</c> record for the host with the specified
hostname.</p>
<p>If resolver option <c>inet6</c> is <c>true</c>,
- an IPv6 address is looked up. If that fails,
- the IPv4 address is looked up and returned on
- IPv6-mapped IPv4 format.</p>
+ an IPv6 address is looked up.</p>
</desc>
</func>
@@ -582,6 +580,19 @@ get_tcpi_sacked(Sock) ->
</func>
<func>
+ <name name="ipv4_mapped_ipv6_address" arity="1" />
+ <fsummary>Convert to and from IPv4-mapped IPv6 address.</fsummary>
+ <desc>
+ <p>
+ Convert an IPv4 address to an IPv4-mapped IPv6 address
+ or the reverse. When converting from an IPv6 address
+ all but the 2 low words are ignored so this function also
+ works on some other types of addresses than IPv4-mapped.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name name="parse_strict_address" arity="1" />
<fsummary>Parse an IPv4 or IPv6 address strict.</fsummary>
<desc>
diff --git a/lib/kernel/doc/src/inet_res.xml b/lib/kernel/doc/src/inet_res.xml
index 3454e3c6f9..351d86a93a 100644
--- a/lib/kernel/doc/src/inet_res.xml
+++ b/lib/kernel/doc/src/inet_res.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2009</year><year>2015</year>
+ <year>2009</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -230,9 +230,7 @@ inet_dns:record_type(_) -> undefined.</pre>
<seealso marker="#getbyname/2"><c>getbyname/2,3</c></seealso>.
</p>
<p>If resolver option <c>inet6</c> is <c>true</c>,
- an IPv6 address is looked up. If that fails,
- the IPv4 address is looked up and returned on
- IPv6-mapped IPv4 format.</p>
+ an IPv6 address is looked up.</p>
</desc>
</func>
diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml
index adec2d9520..fab616e630 100644
--- a/lib/kernel/doc/src/rpc.xml
+++ b/lib/kernel/doc/src/rpc.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2016</year>
+ <year>1996</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -217,7 +217,7 @@
<list type="bulleted">
<item>A list of the nodes that do not exist</item>
<item>A list of the nodes where the server does not exist</item>
- <item>A list of the nodes where the server terminatd before sending
+ <item>A list of the nodes where the server terminated before sending
any reply.</item>
</list>
</desc>
@@ -268,8 +268,9 @@
on the specified nodes and collects the answers. It returns
<c>{<anno>ResL</anno>, <anno>BadNodes</anno>}</c>, where
<c><anno>BadNodes</anno></c> is a list
- of the nodes that terminated or timed out during computation,
- and <c><anno>ResL</anno></c> is a list of the return values.
+ of the nodes that do not exist,
+ and <c><anno>ResL</anno></c> is a list of the return values,
+ or <c>{badrpc, <anno>Reason</anno>}</c> for failing calls.
<c><anno>Timeout</anno></c> is a time (integer) in milliseconds, or
<c>infinity</c>.</p>
<p>The following example is useful when new object code is to
@@ -347,7 +348,7 @@
<func>
<name name="pmap" arity="3"/>
- <fsummary>Parallell evaluation of mapping a function over a
+ <fsummary>Parallel evaluation of mapping a function over a
list.</fsummary>
<desc>
<p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 418b0c50e1..f5a890cb95 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -340,8 +340,7 @@ handle_call(all_loaded, _From, S) ->
{reply,all_loaded(Db),S};
handle_call({get_object_code,Mod}, _From, St) when is_atom(Mod) ->
- Path = St#state.path,
- case mod_to_bin(Path, Mod) of
+ case get_object_code(St, Mod) of
{_,Bin,FName} -> {reply,{Mod,Bin,FName},St};
Error -> {reply,Error,St}
end;
@@ -1182,19 +1181,28 @@ load_file(Mod, From, St0) ->
end,
handle_pending_on_load(Action, Mod, From, St0).
-load_file_1(Mod, From, #state{path=Path}=St) ->
- case mod_to_bin(Path, Mod) of
+load_file_1(Mod, From, St) ->
+ case get_object_code(St, Mod) of
error ->
{reply,{error,nofile},St};
{Mod,Binary,File} ->
try_load_module_1(File, Mod, Binary, From, St)
end.
-mod_to_bin([Dir|Tail], Mod) ->
- File = filename:append(Dir, atom_to_list(Mod) ++ objfile_extension()),
+get_object_code(#state{path=Path}, Mod) when is_atom(Mod) ->
+ ModStr = atom_to_list(Mod),
+ case erl_prim_loader:is_basename(ModStr) of
+ true ->
+ mod_to_bin(Path, Mod, ModStr ++ objfile_extension());
+ false ->
+ error
+ end.
+
+mod_to_bin([Dir|Tail], Mod, ModFile) ->
+ File = filename:append(Dir, ModFile),
case erl_prim_loader:get_file(File) of
error ->
- mod_to_bin(Tail, Mod);
+ mod_to_bin(Tail, Mod, ModFile);
{ok,Bin,_} ->
case filename:pathtype(File) of
absolute ->
@@ -1203,10 +1211,9 @@ mod_to_bin([Dir|Tail], Mod) ->
{Mod,Bin,absname(File)}
end
end;
-mod_to_bin([], Mod) ->
+mod_to_bin([], Mod, ModFile) ->
%% At last, try also erl_prim_loader's own method
- File = to_list(Mod) ++ objfile_extension(),
- case erl_prim_loader:get_file(File) of
+ case erl_prim_loader:get_file(ModFile) of
error ->
error; % No more alternatives !
{ok,Bin,FName} ->
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index fe91b0d33e..4bad523dff 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -34,7 +34,8 @@
ip/1, stats/0, options/0,
pushf/3, popf/1, close/1, gethostname/0, gethostname/1,
parse_ipv4_address/1, parse_ipv6_address/1, parse_ipv4strict_address/1,
- parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1, ntoa/1]).
+ parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1,
+ ntoa/1, ipv4_mapped_ipv6_address/1]).
-export([connect_options/2, listen_options/2, udp_options/2, sctp_options/2]).
-export([udp_module/1, tcp_module/1, tcp_module/2, sctp_module/1]).
@@ -675,6 +676,14 @@ parse_address(Addr) ->
parse_strict_address(Addr) ->
inet_parse:strict_address(Addr).
+-spec ipv4_mapped_ipv6_address(ip_address()) -> ip_address().
+ipv4_mapped_ipv6_address({D1,D2,D3,D4})
+ when (D1 bor D2 bor D3 bor D4) < 256 ->
+ {0,0,0,0,0,16#ffff,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4};
+ipv4_mapped_ipv6_address({D1,D2,D3,D4,D5,D6,D7,D8})
+ when (D1 bor D2 bor D3 bor D4 bor D5 bor D6 bor D7 bor D8) < 65536 ->
+ {D7 bsr 8,D7 band 255,D8 bsr 8,D8 band 255}.
+
%% Return a list of available options
options() ->
[
@@ -1244,9 +1253,7 @@ gethostbyname_string(Name, Type)
inet ->
inet_parse:ipv4_address(Name);
inet6 ->
- %% XXX should we really translate IPv4 addresses here
- %% even if we do not know if this host can do IPv6?
- inet_parse:ipv6_address(Name)
+ inet_parse:ipv6strict_address(Name)
end of
{ok,IP} ->
{ok,make_hostent(Name, [IP], [], Type)};
diff --git a/lib/kernel/src/inet_hosts.erl b/lib/kernel/src/inet_hosts.erl
index 0bdf00ac30..fc653bf0d3 100644
--- a/lib/kernel/src/inet_hosts.erl
+++ b/lib/kernel/src/inet_hosts.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -72,9 +72,6 @@ gethostbyname(Name, Type, Byname, Byaddr) ->
gethostbyaddr({A,B,C,D}=IP) when ?ip(A,B,C,D) ->
gethostbyaddr(IP, inet);
-%% ipv4 only ipv6 address
-gethostbyaddr({0,0,0,0,0,16#ffff=F,G,H}) when ?ip6(0,0,0,0,0,F,G,H) ->
- gethostbyaddr({G bsr 8, G band 255, H bsr 8, H band 255});
gethostbyaddr({A,B,C,D,E,F,G,H}=IP) when ?ip6(A,B,C,D,E,F,G,H) ->
gethostbyaddr(IP, inet6);
gethostbyaddr(Addr) when is_list(Addr) ->
diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl
index 49aa5f8bda..6454802b04 100644
--- a/lib/kernel/src/inet_res.erl
+++ b/lib/kernel/src/inet_res.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -349,9 +349,6 @@ gethostbyaddr_tm({A,B,C,D} = IP, Timer) when ?ip(A,B,C,D) ->
{ok, HEnt} -> {ok, HEnt};
_ -> res_gethostbyaddr(dn_in_addr_arpa(A,B,C,D), IP, Timer)
end;
-%% ipv4 only ipv6 address
-gethostbyaddr_tm({0,0,0,0,0,16#ffff,G,H},Timer) when is_integer(G+H) ->
- gethostbyaddr_tm({G div 256, G rem 256, H div 256, H rem 256},Timer);
gethostbyaddr_tm({A,B,C,D,E,F,G,H} = IP, Timer) when ?ip6(A,B,C,D,E,F,G,H) ->
inet_db:res_update_conf(),
case inet_db:gethostbyaddr(IP) of
@@ -431,28 +428,7 @@ gethostbyname(Name,Family,Timeout) ->
gethostbyname_tm(Name,inet,Timer) ->
getbyname_tm(Name,?S_A,Timer);
gethostbyname_tm(Name,inet6,Timer) ->
- case getbyname_tm(Name,?S_AAAA,Timer) of
- {ok,HEnt} -> {ok,HEnt};
- {error,nxdomain} ->
- case getbyname_tm(Name, ?S_A,Timer) of
- {ok, HEnt} ->
- %% rewrite to a ipv4 only ipv6 address
- {ok,
- HEnt#hostent {
- h_addrtype = inet6,
- h_length = 16,
- h_addr_list =
- lists:map(
- fun({A,B,C,D}) ->
- {0,0,0,0,0,16#ffff,A*256+B,C*256+D}
- end, HEnt#hostent.h_addr_list)
- }};
- Error ->
- Error
- end;
- Error ->
- Error
- end;
+ getbyname_tm(Name,?S_AAAA,Timer);
gethostbyname_tm(_Name, _Family, _Timer) ->
{error, einval}.
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index 1e9db80058..77c883f57f 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -358,16 +358,16 @@ get_data(Port, MonRef, Eot, Sofar, Size, Max) ->
iolist_to_binary(Sofar)
end.
-eot(_Bs, <<>>, _Size, _Max) ->
+eot(Bs, <<>>, Size, Max) when Size + byte_size(Bs) < Max ->
more;
+eot(Bs, <<>>, Size, Max) ->
+ binary:part(Bs, {0, Max - Size});
eot(Bs, Eot, Size, Max) ->
case binary:match(Bs, Eot) of
- nomatch when Size + byte_size(Bs) < Max ->
- more;
{Pos, _} when Size + Pos < Max ->
binary:part(Bs,{0, Pos});
_ ->
- binary:part(Bs,{0, Max - Size})
+ eot(Bs, <<>>, Size, Max)
end.
%% When port_close returns we know that all the
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index 0e0b7dffa3..b04aa9030b 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -498,7 +498,7 @@ start_monitor(Node, Name) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- ResL :: [term()],
+ ResL :: [Res :: term() | {'badrpc', Reason :: term()}],
BadNodes :: [node()].
multicall(M, F, A) ->
@@ -509,14 +509,14 @@ multicall(M, F, A) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- ResL :: [term()],
+ ResL :: [Res :: term() | {'badrpc', Reason :: term()}],
BadNodes :: [node()];
(Module, Function, Args, Timeout) -> {ResL, BadNodes} when
Module :: module(),
Function :: atom(),
Args :: [term()],
Timeout :: timeout(),
- ResL :: [term()],
+ ResL :: [Res :: term() | {'badrpc', Reason :: term()}],
BadNodes :: [node()].
multicall(Nodes, M, F, A) when is_list(Nodes) ->
@@ -531,7 +531,7 @@ multicall(M, F, A, Timeout) ->
Function :: atom(),
Args :: [term()],
Timeout :: timeout(),
- ResL :: [term()],
+ ResL :: [Res :: term() | {'badrpc', Reason :: term()}],
BadNodes :: [node()].
multicall(Nodes, M, F, A, infinity)
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index 073b391288..f6791adf86 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -95,7 +95,11 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
+init_per_testcase(TC, Config) when TC == hostnames;
+ TC == nodenames ->
+ file:make_dir("hostnames_nodedir"),
+ file:write_file("hostnames_nodedir/ignore_core_files",""),
+ Config;
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Config.
@@ -251,7 +255,7 @@ test_node(Name, Illigal) ->
end,
net_kernel:monitor_nodes(true),
BinCommand = unicode:characters_to_binary(Command, utf8),
- Prt = open_port({spawn, BinCommand}, [stream]),
+ Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]),
Node = list_to_atom(Name),
receive
{nodeup, Node} ->
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 3b502be8b8..2e5f8c7d2c 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -40,7 +40,8 @@
lookup_bad_search_option/1,
getif/1,
getif_ifr_name_overflow/1,getservbyname_overflow/1, getifaddrs/1,
- parse_strict_address/1, simple_netns/1, simple_netns_open/1,
+ parse_strict_address/1, ipv4_mapped_ipv6_address/1,
+ simple_netns/1, simple_netns_open/1,
simple_bind_to_device/1, simple_bind_to_device_open/1]).
-export([get_hosts/1, get_ipv6_hosts/1, parse_hosts/1, parse_address/1,
@@ -667,6 +668,26 @@ parse_strict_address(Config) when is_list(Config) ->
{ok, {3089,3106,23603,50240,0,0,119,136}} =
inet:parse_strict_address("c11:0c22:5c33:c440::077:0088").
+ipv4_mapped_ipv6_address(Config) when is_list(Config) ->
+ {D1,D2,D3,D4} = IPv4Address =
+ {rand:uniform(256) - 1,
+ rand:uniform(256) - 1,
+ rand:uniform(256) - 1,
+ rand:uniform(256) - 1},
+ E7 = (D1 bsl 8) bor D2,
+ E8 = (D3 bsl 8) bor D4,
+ io:format("IPv4Address: ~p.~n", [IPv4Address]),
+ {0,0,0,0,0,65535,E7,E8} = inet:ipv4_mapped_ipv6_address(IPv4Address),
+ IPv6Address =
+ {rand:uniform(65536) - 1,
+ rand:uniform(65536) - 1,
+ rand:uniform(65536) - 1,
+ rand:uniform(65536) - 1,
+ rand:uniform(65536) - 1,
+ rand:uniform(65536) - 1, E7, E8},
+ IPv4Address = inet:ipv4_mapped_ipv6_address(IPv6Address),
+ ok.
+
t_gethostnative(Config) when is_list(Config) ->
%% this will result in 26 bytes sent which causes problem in Windows
%% if the port-program has not assured stdin to be read in BINARY mode
@@ -1083,11 +1104,9 @@ ifaddrs([{If,Opts}|IOs]) ->
#ifopts{flags=F} = Ifopts = check_ifopts(Opts, #ifopts{name=If}),
case F of
{flags,Flags} ->
- case lists:member(up, Flags) of
- true ->
- Ifopts#ifopts.addrs;
- false ->
- []
+ case lists:member(running, Flags) of
+ true -> Ifopts#ifopts.addrs;
+ false -> []
end ++ ifaddrs(IOs);
undefined ->
ifaddrs(IOs)
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 4369b1b0f9..591fbb2125 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -334,8 +334,8 @@ max_size_command(_Config) ->
Res32768 = os:cmd("cat /dev/zero", #{ max_size => 32768 }),
32768 = length(Res32768),
- ResHello = os:cmd("echo hello", #{ max_size => 20 }),
- 6 = length(ResHello).
+ ResHello = string:trim(os:cmd("echo hello", #{ max_size => 20 })),
+ 5 = length(ResHello).
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/observer/src/cdv_port_cb.erl b/lib/observer/src/cdv_port_cb.erl
index b5cbe8132d..6bb8f07a74 100644
--- a/lib/observer/src/cdv_port_cb.erl
+++ b/lib/observer/src/cdv_port_cb.erl
@@ -34,7 +34,8 @@
-define(COL_CONN, ?COL_ID+1).
-define(COL_NAME, ?COL_CONN+1).
-define(COL_CTRL, ?COL_NAME+1).
--define(COL_SLOT, ?COL_CTRL+1).
+-define(COL_QUEUE, ?COL_CTRL+1).
+-define(COL_SLOT, ?COL_QUEUE+1).
@@ -44,6 +45,7 @@ col_to_elem(?COL_ID) -> #port.id;
col_to_elem(?COL_CONN) -> #port.connected;
col_to_elem(?COL_NAME) -> #port.name;
col_to_elem(?COL_CTRL) -> #port.controls;
+col_to_elem(?COL_QUEUE) -> #port.queue;
col_to_elem(?COL_SLOT) -> #port.slot.
col_spec() ->
@@ -51,6 +53,7 @@ col_spec() ->
{"Connected", ?wxLIST_FORMAT_LEFT, 120},
{"Name", ?wxLIST_FORMAT_LEFT, 150},
{"Controls", ?wxLIST_FORMAT_LEFT, 200},
+ {"Queue", ?wxLIST_FORMAT_RIGHT, 100},
{"Slot", ?wxLIST_FORMAT_RIGHT, 50}].
get_info(_) ->
@@ -96,9 +99,17 @@ format(D) ->
info_fields() ->
[{"Overview",
[{"Name", name},
+ {"State", state},
+ {"Task Flags", task_flags},
{"Connected", {click,connected}},
{"Slot", slot},
- {"Controls", controls}]},
+ {"Controls", controls},
+ {"Input bytes", input},
+ {"Output bytes", output},
+ {"Queue bytes", queue},
+ {"Port data", port_data}]},
{scroll_boxes,
[{"Links",1,{click,links}},
- {"Monitors",1,{click,monitors}}]}].
+ {"Monitors",1,{click,monitors}},
+ {"Suspended",1,{click,suspended}}
+ ]}].
diff --git a/lib/observer/src/cdv_proc_cb.erl b/lib/observer/src/cdv_proc_cb.erl
index f10650bbb7..0ea23dd7cb 100644
--- a/lib/observer/src/cdv_proc_cb.erl
+++ b/lib/observer/src/cdv_proc_cb.erl
@@ -149,6 +149,10 @@ info_fields() ->
{"Old Heap", old_heap},
{"Heap Unused", heap_unused},
{"Old Heap Unused", old_heap_unused},
+ {"Binary vheap", bin_vheap},
+ {"Old Binary vheap", old_bin_vheap},
+ {"Binary vheap unused", bin_vheap_unused},
+ {"Old Binary vheap unused", old_bin_vheap_unused},
{"Number of Heap Fragements", num_heap_frag},
{"Heap Fragment Data",heap_frag_data},
{"New Heap Start", new_heap_start},
diff --git a/lib/observer/src/cdv_sched_cb.erl b/lib/observer/src/cdv_sched_cb.erl
index 192aaf31a7..d2696a276f 100644
--- a/lib/observer/src/cdv_sched_cb.erl
+++ b/lib/observer/src/cdv_sched_cb.erl
@@ -31,7 +31,8 @@
%% Columns
-define(COL_ID, 0).
--define(COL_PROC, ?COL_ID+1).
+-define(COL_TYPE, ?COL_ID+1).
+-define(COL_PROC, ?COL_TYPE+1).
-define(COL_PORT, ?COL_PROC+1).
-define(COL_RQL, ?COL_PORT+1).
-define(COL_PQL, ?COL_RQL+1).
@@ -39,6 +40,7 @@
%% Callbacks for cdv_virtual_list_wx
col_to_elem(id) -> col_to_elem(?COL_ID);
col_to_elem(?COL_ID) -> #sched.name;
+col_to_elem(?COL_TYPE) -> #sched.type;
col_to_elem(?COL_PROC) -> #sched.process;
col_to_elem(?COL_PORT) -> #sched.port;
col_to_elem(?COL_RQL) -> #sched.run_q;
@@ -46,6 +48,7 @@ col_to_elem(?COL_PQL) -> #sched.port_q.
col_spec() ->
[{"Id", ?wxLIST_FORMAT_RIGHT, 50},
+ {"Type", ?wxLIST_FORMAT_CENTER, 100},
{"Current Process", ?wxLIST_FORMAT_CENTER, 130},
{"Current Port", ?wxLIST_FORMAT_CENTER, 130},
{"Run Queue Length", ?wxLIST_FORMAT_RIGHT, 180},
@@ -73,7 +76,8 @@ detail_pages() ->
[{"Scheduler Information", fun init_gen_page/2}].
init_gen_page(Parent, Info0) ->
- Fields = info_fields(),
+ Type = proplists:get_value(type, Info0),
+ Fields = info_fields(Type),
Details = proplists:get_value(details, Info0),
Info = if is_map(Details) -> Info0 ++ maps:to_list(Details);
true -> Info0
@@ -81,15 +85,16 @@ init_gen_page(Parent, Info0) ->
cdv_info_wx:start_link(Parent,{Fields,Info,[]}).
%%% Internal
-info_fields() ->
+info_fields(Type) ->
[{"Scheduler Overview",
[{"Id", id},
+ {"Type", type},
{"Current Process",process},
{"Current Port", port},
{"Sleep Info Flags", sleep_info},
{"Sleep Aux Work", sleep_aux}
]},
- {"Run Queues",
+ {run_queues_header(Type),
[{"Flags", runq_flags},
{"Priority Max Length", runq_max},
{"Priority High Length", runq_high},
@@ -116,3 +121,8 @@ info_fields() ->
{" ", {currp_stack, 11}}
]}
].
+
+run_queues_header(normal) ->
+ "Run Queues";
+run_queues_header(DirtyX) ->
+ "Run Queues (common for all '" ++ atom_to_list(DirtyX) ++ "' schedulers)".
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index bba97624a7..d0c14db486 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -116,6 +116,10 @@
-define(allocator,allocator).
-define(atoms,atoms).
-define(binary,binary).
+-define(dirty_cpu_scheduler,dirty_cpu_scheduler).
+-define(dirty_cpu_run_queue,dirty_cpu_run_queue).
+-define(dirty_io_scheduler,dirty_io_scheduler).
+-define(dirty_io_run_queue,dirty_io_run_queue).
-define(ende,ende).
-define(erl_crash_dump,erl_crash_dump).
-define(ets,ets).
@@ -1222,6 +1226,18 @@ all_procinfo(Fd,Fun,Proc,WS,LineHead) ->
"OldHeap unused" ->
Bytes = list_to_integer(bytes(Fd))*WS,
get_procinfo(Fd,Fun,Proc#proc{old_heap_unused=Bytes},WS);
+ "BinVHeap" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{bin_vheap=Bytes},WS);
+ "OldBinVHeap" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{old_bin_vheap=Bytes},WS);
+ "BinVHeap unused" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{bin_vheap_unused=Bytes},WS);
+ "OldBinVHeap unused" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{old_bin_vheap_unused=Bytes},WS);
"New heap start" ->
get_procinfo(Fd,Fun,Proc#proc{new_heap_start=bytes(Fd)},WS);
"New heap top" ->
@@ -1632,6 +1648,10 @@ port_to_tuple("#Port<"++Port) ->
get_portinfo(Fd,Port) ->
case line_head(Fd) of
+ "State" ->
+ get_portinfo(Fd,Port#port{state=bytes(Fd)});
+ "Task Flags" ->
+ get_portinfo(Fd,Port#port{task_flags=bytes(Fd)});
"Slot" ->
%% stored as integer so we can sort on it
get_portinfo(Fd,Port#port{slot=list_to_integer(bytes(Fd))});
@@ -1656,6 +1676,10 @@ get_portinfo(Fd,Port) ->
{Pid,Pid++" ("++Ref++")"}
end || Mon <- Monitors0],
get_portinfo(Fd,Port#port{monitors=Monitors});
+ "Suspended" ->
+ Pids = split_pid_list_no_space(bytes(Fd)),
+ Suspended = [{Pid,Pid} || Pid <- Pids],
+ get_portinfo(Fd,Port#port{suspended=Suspended});
"Port controls linked-in driver" ->
Str = lists:flatten(["Linked in driver: " | string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
@@ -1671,6 +1695,15 @@ get_portinfo(Fd,Port) ->
"Port is UNIX fd not opened by emulator" ->
Str = lists:flatten(["UNIX fd not opened by emulator: "| string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
+ "Input" ->
+ get_portinfo(Fd,Port#port{input=list_to_integer(bytes(Fd))});
+ "Output" ->
+ get_portinfo(Fd,Port#port{output=list_to_integer(bytes(Fd))});
+ "Queue" ->
+ get_portinfo(Fd,Port#port{queue=list_to_integer(bytes(Fd))});
+ "Port Data" ->
+ get_portinfo(Fd,Port#port{port_data=string(Fd)});
+
"=" ++ _next_tag ->
Port;
Other ->
@@ -2503,73 +2536,142 @@ get_indextableinfo1(Fd,IndexTable) ->
%%-----------------------------------------------------------------
%% Page with scheduler table information
schedulers(File) ->
- case lookup_index(?scheduler) of
- [] ->
- [];
- Schedulers ->
- Fd = open(File),
- R = lists:map(fun({Name,Start}) ->
- get_schedulerinfo(Fd,Name,Start)
- end,
- Schedulers),
- close(Fd),
- R
- end.
+ Fd = open(File),
-get_schedulerinfo(Fd,Name,Start) ->
+ Schds0 = case lookup_index(?scheduler) of
+ [] ->
+ [];
+ Normals ->
+ [{Normals, #sched{type=normal}}]
+ end,
+ Schds1 = case lookup_index(?dirty_cpu_scheduler) of
+ [] ->
+ Schds0;
+ DirtyCpus ->
+ [{DirtyCpus, get_dirty_runqueue(Fd, ?dirty_cpu_run_queue)}
+ | Schds0]
+ end,
+ Schds2 = case lookup_index(?dirty_io_scheduler) of
+ [] ->
+ Schds1;
+ DirtyIos ->
+ [{DirtyIos, get_dirty_runqueue(Fd, ?dirty_io_run_queue)}
+ | Schds1]
+ end,
+
+ R = schedulers1(Fd, Schds2, []),
+ close(Fd),
+ R.
+
+schedulers1(_Fd, [], Acc) ->
+ Acc;
+schedulers1(Fd, [{Scheds,Sched0} | Tail], Acc0) ->
+ Acc1 = lists:foldl(fun({Name,Start}, AccIn) ->
+ [get_schedulerinfo(Fd,Name,Start,Sched0) | AccIn]
+ end,
+ Acc0,
+ Scheds),
+ schedulers1(Fd, Tail, Acc1).
+
+get_schedulerinfo(Fd,Name,Start,Sched0) ->
pos_bof(Fd,Start),
- get_schedulerinfo1(Fd,#sched{name=Name}).
+ get_schedulerinfo1(Fd,Sched0#sched{name=list_to_integer(Name)}).
-get_schedulerinfo1(Fd,Sched=#sched{details=Ds}) ->
+sched_type(?dirty_cpu_run_queue) -> dirty_cpu;
+sched_type(?dirty_io_run_queue) -> dirty_io.
+
+get_schedulerinfo1(Fd, Sched) ->
+ case get_schedulerinfo2(Fd, Sched) of
+ {more, Sched2} ->
+ get_schedulerinfo1(Fd, Sched2);
+ {done, Sched2} ->
+ Sched2
+ end.
+
+get_schedulerinfo2(Fd, Sched=#sched{details=Ds}) ->
case line_head(Fd) of
"Current Process" ->
- get_schedulerinfo1(Fd,Sched#sched{process=bytes(Fd, "None")});
+ {more, Sched#sched{process=bytes(Fd, "None")}};
"Current Port" ->
- get_schedulerinfo1(Fd,Sched#sched{port=bytes(Fd, "None")});
+ {more, Sched#sched{port=bytes(Fd, "None")}};
+
+ "Scheduler Sleep Info Flags" ->
+ {more, Sched#sched{details=Ds#{sleep_info=>bytes(Fd, "None")}}};
+ "Scheduler Sleep Info Aux Work" ->
+ {more, Sched#sched{details=Ds#{sleep_aux=>bytes(Fd, "None")}}};
+
+ "Current Process State" ->
+ {more, Sched#sched{details=Ds#{currp_state=>bytes(Fd)}}};
+ "Current Process Internal State" ->
+ {more, Sched#sched{details=Ds#{currp_int_state=>bytes(Fd)}}};
+ "Current Process Program counter" ->
+ {more, Sched#sched{details=Ds#{currp_prg_cnt=>string(Fd)}}};
+ "Current Process CP" ->
+ {more, Sched#sched{details=Ds#{currp_cp=>string(Fd)}}};
+ "Current Process Limited Stack Trace" ->
+ %% If there shall be last in scheduler information block
+ {done, Sched#sched{details=get_limited_stack(Fd, 0, Ds)}};
+
+ "=" ++ _next_tag ->
+ {done, Sched};
+
+ Other ->
+ case Sched#sched.type of
+ normal ->
+ get_runqueue_info2(Fd, Other, Sched);
+ _ ->
+ unexpected(Fd,Other,"dirty scheduler information"),
+ {done, Sched}
+ end
+ end.
+
+get_dirty_runqueue(Fd, Tag) ->
+ case lookup_index(Tag) of
+ [{_, Start}] ->
+ pos_bof(Fd,Start),
+ get_runqueue_info1(Fd,#sched{type=sched_type(Tag)});
+ [] ->
+ #sched{}
+ end.
+
+get_runqueue_info1(Fd, Sched) ->
+ case get_runqueue_info2(Fd, line_head(Fd), Sched) of
+ {more, Sched2} ->
+ get_runqueue_info1(Fd, Sched2);
+ {done, Sched2} ->
+ Sched2
+ end.
+
+get_runqueue_info2(Fd, LineHead, Sched=#sched{details=Ds}) ->
+ case LineHead of
"Run Queue Max Length" ->
RQMax = list_to_integer(bytes(Fd)),
RQ = RQMax + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}}};
"Run Queue High Length" ->
RQHigh = list_to_integer(bytes(Fd)),
RQ = RQHigh + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}}};
"Run Queue Normal Length" ->
RQNorm = list_to_integer(bytes(Fd)),
RQ = RQNorm + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}}};
"Run Queue Low Length" ->
RQLow = list_to_integer(bytes(Fd)),
RQ = RQLow + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}}};
"Run Queue Port Length" ->
RQ = list_to_integer(bytes(Fd)),
- get_schedulerinfo1(Fd,Sched#sched{port_q=RQ});
-
- "Scheduler Sleep Info Flags" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_info=>bytes(Fd, "None")}});
- "Scheduler Sleep Info Aux Work" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_aux=>bytes(Fd, "None")}});
+ {more, Sched#sched{port_q=RQ}};
"Run Queue Flags" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{runq_flags=>bytes(Fd, "None")}});
+ {more, Sched#sched{details=Ds#{runq_flags=>bytes(Fd, "None")}}};
- "Current Process State" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_state=>bytes(Fd)}});
- "Current Process Internal State" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_int_state=>bytes(Fd)}});
- "Current Process Program counter" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_prg_cnt=>string(Fd)}});
- "Current Process CP" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_cp=>string(Fd)}});
- "Current Process Limited Stack Trace" ->
- %% If there shall be last in scheduler information block
- Sched#sched{details=get_limited_stack(Fd, 0, Ds)};
"=" ++ _next_tag ->
- Sched;
+ {done, Sched};
Other ->
unexpected(Fd,Other,"scheduler information"),
- Sched
+ {done, Sched}
end.
get_limited_stack(Fd, N, Ds) ->
@@ -3000,6 +3102,10 @@ tag_to_atom("allocated_areas") -> ?allocated_areas;
tag_to_atom("allocator") -> ?allocator;
tag_to_atom("atoms") -> ?atoms;
tag_to_atom("binary") -> ?binary;
+tag_to_atom("dirty_cpu_scheduler") -> ?dirty_cpu_scheduler;
+tag_to_atom("dirty_cpu_run_queue") -> ?dirty_cpu_run_queue;
+tag_to_atom("dirty_io_scheduler") -> ?dirty_io_scheduler;
+tag_to_atom("dirty_io_run_queue") -> ?dirty_io_run_queue;
tag_to_atom("end") -> ?ende;
tag_to_atom("erl_crash_dump") -> ?erl_crash_dump;
tag_to_atom("ets") -> ?ets;
diff --git a/lib/observer/src/crashdump_viewer.hrl b/lib/observer/src/crashdump_viewer.hrl
index 6a93a089fd..252e19379d 100644
--- a/lib/observer/src/crashdump_viewer.hrl
+++ b/lib/observer/src/crashdump_viewer.hrl
@@ -80,6 +80,10 @@
old_heap,
heap_unused,
old_heap_unused,
+ bin_vheap,
+ old_bin_vheap,
+ bin_vheap_unused,
+ old_bin_vheap_unused,
new_heap_start,
new_heap_top,
stack_top,
@@ -95,19 +99,27 @@
-record(port,
{id,
+ state,
+ task_flags=0,
slot,
connected,
links,
name,
monitors,
- controls}).
+ suspended,
+ controls,
+ input,
+ output,
+ queue,
+ port_data}).
-record(sched,
{name,
+ type,
process,
port,
run_q=0,
- port_q=0,
+ port_q,
details=#{}
}).
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 32773a779e..41ca3f3ce9 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -820,10 +820,10 @@ dump_with_size_limit_reached(DataDir,Rel,DumpName,Max) ->
"-env ERL_CRASH_DUMP_BYTES " ++
integer_to_list(Bytes)),
{ok,#file_info{size=Size}} = file:read_file_info(CD),
- if Size < Bytes ->
+ if Size =< Bytes ->
%% This means that the dump was actually smaller than the
%% randomly selected truncation size, so we'll just do it
- %% again with a smaller numer
+ %% again with a smaller number
ok = file:delete(CD),
dump_with_size_limit_reached(DataDir,Rel,DumpName,Size-3);
true ->
diff --git a/lib/runtime_tools/src/system_information.erl b/lib/runtime_tools/src/system_information.erl
index 119d7cc3d4..136ee55b54 100644
--- a/lib/runtime_tools/src/system_information.erl
+++ b/lib/runtime_tools/src/system_information.erl
@@ -488,10 +488,10 @@ to_fd(Fd) ->
ok = file:write(Fd, io_lib:format(Format, Args))
end,
- EmitChunk("{system_information_version, ~p}.~n"
+ EmitChunk("{system_information_version, ~w}.~n"
"{system_information,["
- "{init_arguments,~p},"
- "{code_paths,~p},",
+ "{init_arguments,~w},"
+ "{code_paths,~w},",
[?REPORT_FILE_VSN,
init:get_arguments(),
code:get_path()]),
@@ -499,12 +499,12 @@ to_fd(Fd) ->
emit_code_info(EmitChunk),
EmitChunk( "," %% Note the leading comma!
- "{system_info,~p},"
- "{erts_compile_info,~p},"
- "{beam_dynamic_libraries,~p},"
- "{environment_erts,~p},"
- "{environment,~p},"
- "{sanity_check,~p}"
+ "{system_info,~w},"
+ "{erts_compile_info,~w},"
+ "{beam_dynamic_libraries,~w},"
+ "{environment_erts,~w},"
+ "{environment,~w},"
+ "{sanity_check,~w}"
"]}.~n",
[erlang_system_info(),
erlang:system_info(compile_info),
@@ -533,11 +533,11 @@ emit_application_info(EmitChunk, Path) ->
Description = proplists:get_value(description, Info, []),
Version = proplists:get_value(vsn, Info, []),
- EmitChunk("{application, {~p,["
- "{description,~p},"
- "{vsn,~p},"
- "{path,~p},"
- "{runtime_dependencies,~p},",
+ EmitChunk("{application, {~w,["
+ "{description,~w},"
+ "{vsn,~w},"
+ "{path,~w},"
+ "{runtime_dependencies,~w},",
[App, Description, Version, Path, RtDeps]),
emit_module_info_from_path(EmitChunk, Path),
EmitChunk("]}}", [])
@@ -545,7 +545,7 @@ emit_application_info(EmitChunk, Path) ->
emit_code_path_info(EmitChunk, Path) ->
EmitChunk("{code, ["
- "{path, ~p},", [Path]),
+ "{path, ~w},", [Path]),
emit_module_info_from_path(EmitChunk, Path),
EmitChunk("]}", []).
@@ -573,11 +573,11 @@ emit_module_info(EmitChunk, Beam) ->
_ -> true
end,
- EmitChunk("{~p,["
- "{loaded,~p},"
- "{native,~p},"
- "{compiler,~p},"
- "{md5,~p}"
+ EmitChunk("{~w,["
+ "{loaded,~w},"
+ "{native,~w},"
+ "{compiler,~w},"
+ "{md5,~w}"
"]}",
[Mod, Loaded, Native, CompilerVersion, hexstring(Md5)]).
diff --git a/lib/sasl/doc/src/release_handler.xml b/lib/sasl/doc/src/release_handler.xml
index 8f073807fb..975188f489 100644
--- a/lib/sasl/doc/src/release_handler.xml
+++ b/lib/sasl/doc/src/release_handler.xml
@@ -61,6 +61,7 @@
<list type="bulleted">
<item>A release upgrade file, <c>relup</c></item>
<item>A system configuration file, <c>sys.config</c></item>
+ <item>A system configuration source file, <c>sys.config.src</c></item>
</list>
<p>The <c>relup</c> file contains instructions for how to upgrade
to, or downgrade from, this version of the release.</p>
@@ -819,4 +820,3 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]).
<seealso marker="systools"><c>systools(3)</c></seealso></p>
</section>
</erlref>
-
diff --git a/lib/sasl/doc/src/systools.xml b/lib/sasl/doc/src/systools.xml
index e7c3c499da..4842c732b1 100644
--- a/lib/sasl/doc/src/systools.xml
+++ b/lib/sasl/doc/src/systools.xml
@@ -349,10 +349,11 @@ myapp-1/ebin/myapp.app
the release version as specified in <c>Name.rel</c>.</p>
<p><c>releases/RelVsn</c> contains the boot script
<c>Name.boot</c> renamed to <c>start.boot</c> and, if found,
- the files <c>relup</c> and <c>sys.config</c>. These files
+ the files <c>relup</c> and <c>sys.config</c> or <c>sys.config.src</c>. These files
are searched for in the same directory as <c>Name.rel</c>,
in the current working directory, and in any directories
- specified using option <c>path</c>.</p>
+ specified using option <c>path</c>. In the case of <c>sys.config</c>
+ it is not included if <c>sys.config.src</c> is found.</p>
<p>If the release package is to contain a new Erlang runtime
system, the <c>bin</c> directory of the specified runtime
system <c>{erts,Dir}</c> is copied to <c>erts-ErtsVsn/bin</c>.</p>
@@ -397,4 +398,3 @@ myapp-1/ebin/myapp.app
<seealso marker="script"><c>script(4)</c></seealso></p>
</section>
</erlref>
-
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 9d960b7361..fa3182cc08 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -310,6 +310,7 @@ add_apply_upgrade(Script,Args) ->
%% RelVsn/start.boot
%% relup
%% sys.config
+%% sys.config.src
%% erts-EVsn[/bin]
%%-----------------------------------------------------------------
@@ -1552,6 +1553,7 @@ create_kernel_procs(Appls) ->
%% RelVsn/start.boot
%% relup
%% sys.config
+%% sys.config.src
%% erts-EVsn[/bin]
%%
%% The VariableN.tar.gz files can also be stored as own files not
@@ -1707,14 +1709,18 @@ add_system_files(Tar, RelName, Release, Path1) ->
add_to_tar(Tar, Relup, filename:join(RelVsnDir, "relup"))
end,
- case lookup_file("sys.config", Path) of
- false ->
- ignore;
- Sys ->
- check_sys_config(Sys),
- add_to_tar(Tar, Sys, filename:join(RelVsnDir, "sys.config"))
+ case lookup_file("sys.config.src", Path) of
+ false ->
+ case lookup_file("sys.config", Path) of
+ false ->
+ ignore;
+ Sys ->
+ check_sys_config(Sys),
+ add_to_tar(Tar, Sys, filename:join(RelVsnDir, "sys.config"))
+ end;
+ SysSrc ->
+ add_to_tar(Tar, SysSrc, filename:join(RelVsnDir, "sys.config.src"))
end,
-
ok.
lookup_file(Name, [Dir|Path]) ->
diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl
index 07748d975f..c8b2f31120 100644
--- a/lib/sasl/test/systools_SUITE.erl
+++ b/lib/sasl/test/systools_SUITE.erl
@@ -67,7 +67,7 @@ groups() ->
otp_3065_circular_dependenies, included_and_used_sort_script]},
{tar, [],
[tar_options, normal_tar, no_mod_vsn_tar, system_files_tar,
- invalid_system_files_tar, variable_tar,
+ system_src_file_tar, invalid_system_files_tar, variable_tar,
src_tests_tar, var_tar, exref_tar, link_tar, no_sasl_tar,
otp_9507_path_ebin]},
{relup, [],
@@ -945,12 +945,47 @@ system_files_tar(Config) ->
ok.
+
system_files_tar(cleanup,Config) ->
Dir = ?privdir,
file:delete(filename:join(Dir,"sys.config")),
file:delete(filename:join(Dir,"relup")),
ok.
+%% make_tar: Check that sys.config.src and not sys.config is included
+system_src_file_tar(Config) ->
+ {ok, OldDir} = file:get_cwd(),
+
+ {LatestDir, LatestName} = create_script(latest,Config),
+
+ DataDir = filename:absname(?copydir),
+ LibDir = fname([DataDir, d_normal, lib]),
+ P = [fname([LibDir, 'db-2.1', ebin]),
+ fname([LibDir, 'fe-3.1', ebin])],
+
+ ok = file:set_cwd(LatestDir),
+
+ %% Add dummy sys.config and sys.config.src
+ ok = file:write_file("sys.config.src","[${SOMETHING}].\n"),
+ ok = file:write_file("sys.config","[].\n"),
+
+ {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]),
+ ok = systools:make_tar(LatestName, [{path, P}]),
+ ok = check_tar(fname(["releases","LATEST","sys.config.src"]), LatestName),
+ {error, _} = check_tar(fname(["releases","LATEST","sys.config"]), LatestName),
+ {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]),
+ ok = check_tar(fname(["releases","LATEST","sys.config.src"]), LatestName),
+ {error, _} = check_tar(fname(["releases","LATEST","sys.config"]), LatestName),
+
+ ok = file:set_cwd(OldDir),
+
+ ok.
+
+system_src_file_tar(cleanup,Config) ->
+ Dir = ?privdir,
+ file:delete(filename:join(Dir,"sys.config")),
+ file:delete(filename:join(Dir,"sys.config.src")),
+ ok.
%% make_tar: Check that make_tar fails if relup or sys.config exist
%% but do not have valid content
diff --git a/lib/snmp/doc/src/snmp_impl_example_agent.xml b/lib/snmp/doc/src/snmp_impl_example_agent.xml
index a86006a0a7..e576fa51f3 100644
--- a/lib/snmp/doc/src/snmp_impl_example_agent.xml
+++ b/lib/snmp/doc/src/snmp_impl_example_agent.xml
@@ -47,6 +47,7 @@
EX1-MIB DEFINITIONS ::= BEGIN
IMPORTS
+ experimental FROM RFC1155-SMI
RowStatus FROM STANDARD-MIB
DisplayString FROM RFC1213-MIB
OBJECT-TYPE FROM RFC-1212
@@ -81,7 +82,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
FriendsEntry ::=
SEQUENCE {
- fIndex
+ fIndex
INTEGER,
fName
DisplayString,
@@ -105,6 +106,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"Name of friend"
::= { friendsEntry 2 }
+
fAddress OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
ACCESS read-write
@@ -112,6 +114,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"Address of friend"
::= { friendsEntry 3 }
+
fStatus OBJECT-TYPE
SYNTAX RowStatus
ACCESS read-write
@@ -119,12 +122,13 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"The status of this conceptual row."
::= { friendsEntry 4 }
+
fTrap TRAP-TYPE
ENTERPRISE example1
VARIABLES { myName, fIndex }
DESCRIPTION
- "This trap is sent when something happens to
- the friend specified by fIndex."
+ "This trap is sent when something happens to
+ the friend specified by fIndex."
::= 1
END
</code>
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 3a2f55a487..f5cb3ec254 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,41 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.6.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Adjusted supervisor timeouts</p>
+ <p>
+ Own Id: OTP-14907</p>
+ </item>
+ <item>
+ <p>
+ Remove ERROR messages for slow process exits</p>
+ <p>
+ Own Id: OTP-14930</p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add option <c>save_accepted_host</c> to
+ <c>ssh:connection</c>. This option, if set to false,
+ inhibits saving host keys to e.g the file
+ <c>known_hosts</c>.</p>
+ <p>
+ Own Id: OTP-14935</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.6.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -60,7 +95,6 @@
</list>
</section>
-
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -90,7 +124,6 @@
</section>
<section><title>Ssh 4.6.2</title>
-
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
@@ -370,6 +403,40 @@
</section>
+
+<section><title>Ssh 4.4.2.2</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Default exec is disabled when a user-defined shell is
+ enabled.</p>
+ <p>
+ Own Id: OTP-14881</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+
+<section><title>Ssh 4.4.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Trailing white space was removed at end of the
+ hello-string. This caused interoperability problems with
+ some other ssh-implementations (e.g OpenSSH 7.3p1 on
+ Solaris 11)</p>
+ <p>
+ Own Id: OTP-14763 Aux Id: ERIERL-74 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -740,6 +807,93 @@
</section>
+<section><title>Ssh 4.2.2.5</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Default exec is disabled when a user-defined shell is
+ enabled.</p>
+ <p>
+ Own Id: OTP-14881</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+
+<section><title>Ssh 4.2.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Trailing white space was removed at end of the
+ hello-string. This caused interoperability problems with
+ some other ssh-implementations (e.g OpenSSH 7.3p1 on
+ Solaris 11)</p>
+ <p>
+ Own Id: OTP-14763 Aux Id: ERIERL-74 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.2.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The key exchange algorithm
+ diffie-hellman-group-exchange-sha* has a server-option
+ <c>{dh_gex_limits,{Min,Max}}</c>. There was a hostkey
+ signature validation error on the client side if the
+ option was used and the <c>Min</c> or the <c>Max</c>
+ differed from the corresponding values obtained from the
+ client.</p>
+ <p>
+ This bug is now corrected.</p>
+ <p>
+ Own Id: OTP-14166</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Key exchange algorithms
+ diffie-hellman-group-exchange-sha* optimized, up to a
+ factor of 11 for the slowest ( = biggest and safest) one.</p>
+ <p>
+ Own Id: OTP-14169 Aux Id: seq-13261 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.2.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Upgrade of an established client connection could crash
+ because the ssh client supervisors children had wrong
+ type. This is fixed now.</p>
+ <p>
+ Own Id: OTP-13782 Aux Id: seq13158 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.2.2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 337f4094cc..acf94ff6af 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -227,6 +227,18 @@
</item>
</list>
</item>
+
+ <tag><c><![CDATA[{save_accepted_host, boolean()}]]></c></tag>
+ <item>
+ <p>If <c>true</c>, the client saves an accepted host key to avoid the
+ accept question the next time the same host is connected. If the option
+ <c>key_cb</c> is not present, the key is saved in the file "known_hosts".
+ </p>
+ <p>If <c>false</c>, the key is not saved and the key will still be unknown
+ at the next access of the same host.
+ </p>
+ </item>
+
<tag><c><![CDATA[{user_interaction, boolean()}]]></c></tag>
<item>
<p>If <c>false</c>, disables the client to connect to the server
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index ed7fbf9cf3..129426a6d5 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -464,11 +464,16 @@
<v>FileInfo = record()</v>
</type>
<desc>
- <p>Returns a <c><![CDATA[file_info]]></c> record from the file specified by
+ <p>Returns a <c><![CDATA[file_info]]></c> record from the file system object specified by
<c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>. See
<seealso marker="kernel:file#read_file_info-2">file:read_file_info/2</seealso>
for information about the record.
</p>
+ <p>
+ Depending on the underlying OS:es links might be followed and info on the final file, directory
+ etc is returned. See <seealso marker="#read_link_info-2">ssh_sftp::read_link_info/2</seealso>
+ on how to get information on links instead.
+ </p>
</desc>
</func>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 032d87bdad..25d537c624 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -184,7 +184,6 @@ channel_info(ConnectionRef, ChannelId, Options) ->
daemon(Port) ->
daemon(Port, []).
-
daemon(Socket, UserOptions) when is_port(Socket) ->
try
#{} = Options = ssh_options:handle_options(server, UserOptions),
@@ -267,8 +266,6 @@ daemon(Host0, Port0, UserOptions0) when 0 =< Port0, Port0 =< 65535,
daemon(_, _, _) ->
{error, badarg}.
-
-
%%--------------------------------------------------------------------
-spec daemon_info(daemon_ref()) -> ok_error( [{atom(), term()}] ).
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 3dee1c5521..4711f54fb5 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -35,6 +35,8 @@
-define(DEFAULT_TRANSPORT, {tcp, gen_tcp, tcp_closed} ).
+-define(DEFAULT_SHELL, {shell, start, []} ).
+
-define(MAX_RND_PADDING_LEN, 15).
-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
diff --git a/lib/ssh/src/ssh_acceptor_sup.erl b/lib/ssh/src/ssh_acceptor_sup.erl
index a24664793b..fc564a359b 100644
--- a/lib/ssh/src/ssh_acceptor_sup.erl
+++ b/lib/ssh/src/ssh_acceptor_sup.erl
@@ -86,10 +86,7 @@ child_spec(Address, Port, Profile, Options) ->
Timeout = ?GET_INTERNAL_OPT(timeout, Options, ?DEFAULT_TIMEOUT),
#{id => id(Address, Port, Profile),
start => {ssh_acceptor, start_link, [Port, Address, Options, Timeout]},
- restart => transient,
- shutdown => 5500, %brutal_kill,
- type => worker,
- modules => [ssh_acceptor]
+ restart => transient % because a crashed listener could be replaced by a new one
}.
id(Address, Port, Profile) ->
diff --git a/lib/ssh/src/ssh_channel_sup.erl b/lib/ssh/src/ssh_channel_sup.erl
index 6b01dc334d..8444533fd1 100644
--- a/lib/ssh/src/ssh_channel_sup.erl
+++ b/lib/ssh/src/ssh_channel_sup.erl
@@ -26,7 +26,7 @@
-behaviour(supervisor).
--export([start_link/1, start_child/2]).
+-export([start_link/1, start_child/5]).
%% Supervisor callback
-export([init/1]).
@@ -37,7 +37,14 @@
start_link(Args) ->
supervisor:start_link(?MODULE, [Args]).
-start_child(Sup, ChildSpec) ->
+start_child(Sup, Callback, Id, Args, Exec) ->
+ ChildSpec =
+ #{id => make_ref(),
+ start => {ssh_channel, start_link, [self(), Id, Callback, Args, Exec]},
+ restart => temporary,
+ type => worker,
+ modules => [ssh_channel]
+ },
supervisor:start_child(Sup, ChildSpec).
%%%=========================================================================
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 62854346b0..958c342f5f 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -127,7 +127,8 @@ handle_ssh_msg({ssh_cm, ConnectionHandler,
cm = ConnectionHandler}};
handle_ssh_msg({ssh_cm, ConnectionHandler,
- {exec, ChannelId, WantReply, Cmd}}, #state{exec=undefined} = State) ->
+ {exec, ChannelId, WantReply, Cmd}}, #state{exec=undefined,
+ shell=?DEFAULT_SHELL} = State) ->
{Reply, Status} = exec(Cmd),
write_chars(ConnectionHandler,
ChannelId, io_lib:format("~p\n", [Reply])),
@@ -136,6 +137,15 @@ handle_ssh_msg({ssh_cm, ConnectionHandler,
ssh_connection:exit_status(ConnectionHandler, ChannelId, Status),
ssh_connection:send_eof(ConnectionHandler, ChannelId),
{stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionHandler}};
+
+handle_ssh_msg({ssh_cm, ConnectionHandler,
+ {exec, ChannelId, WantReply, _Cmd}}, #state{exec = undefined} = State) ->
+ write_chars(ConnectionHandler, ChannelId, 1, "Prohibited.\n"),
+ ssh_connection:reply_request(ConnectionHandler, WantReply, success, ChannelId),
+ ssh_connection:exit_status(ConnectionHandler, ChannelId, 255),
+ ssh_connection:send_eof(ConnectionHandler, ChannelId),
+ {stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionHandler}};
+
handle_ssh_msg({ssh_cm, ConnectionHandler,
{exec, ChannelId, WantReply, Cmd}}, State) ->
NewState = start_shell(ConnectionHandler, Cmd, State),
@@ -453,11 +463,14 @@ move_cursor(From, To, #ssh_pty{width=Width, term=Type}) ->
%% %%% make sure that there is data to send
%% %%% before calling ssh_connection:send
write_chars(ConnectionHandler, ChannelId, Chars) ->
+ write_chars(ConnectionHandler, ChannelId, ?SSH_EXTENDED_DATA_DEFAULT, Chars).
+
+write_chars(ConnectionHandler, ChannelId, Type, Chars) ->
case has_chars(Chars) of
false -> ok;
true -> ssh_connection:send(ConnectionHandler,
ChannelId,
- ?SSH_EXTENDED_DATA_DEFAULT,
+ Type,
Chars)
end.
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 7e9ee78fd2..946ae2967b 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -812,22 +812,20 @@ start_channel(Cb, Id, Args, SubSysSup, Opts) ->
start_channel(Cb, Id, Args, SubSysSup, undefined, Opts).
start_channel(Cb, Id, Args, SubSysSup, Exec, Opts) ->
- ChildSpec = child_spec(Cb, Id, Args, Exec),
ChannelSup = ssh_subsystem_sup:channel_supervisor(SubSysSup),
- assert_limit_num_channels_not_exceeded(ChannelSup, Opts),
- ssh_channel_sup:start_child(ChannelSup, ChildSpec).
+ case max_num_channels_not_exceeded(ChannelSup, Opts) of
+ true ->
+ ssh_channel_sup:start_child(ChannelSup, Cb, Id, Args, Exec);
+ false ->
+ throw(max_num_channels_exceeded)
+ end.
-assert_limit_num_channels_not_exceeded(ChannelSup, Opts) ->
+max_num_channels_not_exceeded(ChannelSup, Opts) ->
MaxNumChannels = ?GET_OPT(max_channels, Opts),
NumChannels = length([x || {_,_,worker,[ssh_channel]} <-
supervisor:which_children(ChannelSup)]),
- if
- %% Note that NumChannels is BEFORE starting a new one
- NumChannels < MaxNumChannels ->
- ok;
- true ->
- throw(max_num_channels_exceeded)
- end.
+ %% Note that NumChannels is BEFORE starting a new one
+ NumChannels < MaxNumChannels.
%%--------------------------------------------------------------------
%%% Internal functions
@@ -874,14 +872,6 @@ check_subsystem(SsName, Options) ->
Value
end.
-child_spec(Callback, Id, Args, Exec) ->
- Name = make_ref(),
- StartFunc = {ssh_channel, start_link, [self(), Id, Callback, Args, Exec]},
- Restart = temporary,
- Shutdown = 3600,
- Type = worker,
- {Name, StartFunc, Restart, Shutdown, Type, [ssh_channel]}.
-
start_cli(#connection{cli_spec = no_cli}, _) ->
{error, cli_disabled};
start_cli(#connection{options = Options,
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 0ca960ef96..e11d3adee4 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -1174,17 +1174,25 @@ handle_event({call,_}, _, StateName, _) when not ?CONNECTED(StateName) ->
handle_event({call,From}, {request, ChannelPid, ChannelId, Type, Data, Timeout}, StateName, D0)
when ?CONNECTED(StateName) ->
- D = handle_request(ChannelPid, ChannelId, Type, Data, true, From, D0),
- %% Note reply to channel will happen later when reply is recived from peer on the socket
- start_channel_request_timer(ChannelId, From, Timeout),
- {keep_state, cache_request_idle_timer_check(D)};
+ case handle_request(ChannelPid, ChannelId, Type, Data, true, From, D0) of
+ {error,Error} ->
+ {keep_state, D0, {reply,From,{error,Error}}};
+ D ->
+ %% Note reply to channel will happen later when reply is recived from peer on the socket
+ start_channel_request_timer(ChannelId, From, Timeout),
+ {keep_state, cache_request_idle_timer_check(D)}
+ end;
handle_event({call,From}, {request, ChannelId, Type, Data, Timeout}, StateName, D0)
when ?CONNECTED(StateName) ->
- D = handle_request(ChannelId, Type, Data, true, From, D0),
- %% Note reply to channel will happen later when reply is recived from peer on the socket
- start_channel_request_timer(ChannelId, From, Timeout),
- {keep_state, cache_request_idle_timer_check(D)};
+ case handle_request(ChannelId, Type, Data, true, From, D0) of
+ {error,Error} ->
+ {keep_state, D0, {reply,From,{error,Error}}};
+ D ->
+ %% Note reply to channel will happen later when reply is recived from peer on the socket
+ start_channel_request_timer(ChannelId, From, Timeout),
+ {keep_state, cache_request_idle_timer_check(D)}
+ end;
handle_event({call,From}, {data, ChannelId, Type, Data, Timeout}, StateName, D0)
when ?CONNECTED(StateName) ->
@@ -1460,13 +1468,12 @@ terminate(shutdown, StateName, State0) ->
State0),
finalize_termination(StateName, State);
-%% terminate({shutdown,Msg}, StateName, State0) when is_record(Msg,ssh_msg_disconnect)->
-%% State = send_msg(Msg, State0),
-%% finalize_termination(StateName, Msg, State);
-
terminate({shutdown,_R}, StateName, State) ->
finalize_termination(StateName, State);
+terminate(kill, StateName, State) ->
+ finalize_termination(StateName, State);
+
terminate(Reason, StateName, State0) ->
%% Others, e.g undef, {badmatch,_}
log_error(Reason),
@@ -1774,21 +1781,31 @@ is_usable_user_pubkey(A, Ssh) ->
%%%----------------------------------------------------------------
handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From, D) ->
case ssh_channel:cache_lookup(cache(D), ChannelId) of
- #channel{remote_id = Id} = Channel ->
+ #channel{remote_id = Id,
+ sent_close = false} = Channel ->
update_sys(cache(D), Channel, Type, ChannelPid),
send_msg(ssh_connection:channel_request_msg(Id, Type, WantReply, Data),
add_request(WantReply, ChannelId, From, D));
- undefined ->
- D
+
+ _ when WantReply==true ->
+ {error,closed};
+
+ _ ->
+ D
end.
handle_request(ChannelId, Type, Data, WantReply, From, D) ->
case ssh_channel:cache_lookup(cache(D), ChannelId) of
- #channel{remote_id = Id} ->
+ #channel{remote_id = Id,
+ sent_close = false} ->
send_msg(ssh_connection:channel_request_msg(Id, Type, WantReply, Data),
add_request(WantReply, ChannelId, From, D));
- undefined ->
- D
+
+ _ when WantReply==true ->
+ {error,closed};
+
+ _ ->
+ D
end.
%%%----------------------------------------------------------------
diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl
index 60ee8b7c73..2e8450090a 100644
--- a/lib/ssh/src/ssh_connection_sup.erl
+++ b/lib/ssh/src/ssh_connection_sup.erl
@@ -52,10 +52,7 @@ init(_) ->
},
ChildSpecs = [#{id => undefined, % As simple_one_for_one is used.
start => {ssh_connection_handler, start_link, []},
- restart => temporary,
- shutdown => 4000,
- type => worker,
- modules => [ssh_connection_handler]
+ restart => temporary % because there is no way to restart a crashed connection
}
],
{ok, {SupFlags,ChildSpecs}}.
diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
index 68c99743ee..1e10f72956 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -268,7 +268,7 @@ default(server) ->
},
{shell, def} =>
- #{default => {shell, start, []},
+ #{default => ?DEFAULT_SHELL,
chk => fun({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A);
(V) -> check_function1(V) orelse check_function2(V)
end,
@@ -439,6 +439,12 @@ default(client) ->
class => user_options
},
+ {save_accepted_host, def} =>
+ #{default => true,
+ chk => fun erlang:is_boolean/1,
+ class => user_options
+ },
+
{pref_public_key_algs, def} =>
#{default => ssh_transport:default_algorithms(public_key),
chk => fun check_pref_public_key_algs/1,
diff --git a/lib/ssh/src/ssh_subsystem_sup.erl b/lib/ssh/src/ssh_subsystem_sup.erl
index 8db051095c..77da240a66 100644
--- a/lib/ssh/src/ssh_subsystem_sup.erl
+++ b/lib/ssh/src/ssh_subsystem_sup.erl
@@ -74,18 +74,14 @@ ssh_connection_child_spec(Role, Address, Port, _Profile, Options) ->
#{id => id(Role, ssh_connection_sup, Address, Port),
start => {ssh_connection_sup, start_link, [Options]},
restart => temporary,
- shutdown => 5000,
- type => supervisor,
- modules => [ssh_connection_sup]
+ type => supervisor
}.
ssh_channel_child_spec(Role, Address, Port, _Profile, Options) ->
#{id => id(Role, ssh_channel_sup, Address, Port),
start => {ssh_channel_sup, start_link, [Options]},
restart => temporary,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_channel_sup]
+ type => supervisor
}.
id(Role, Sup, Address, Port) ->
diff --git a/lib/ssh/src/ssh_sup.erl b/lib/ssh/src/ssh_sup.erl
index eaec7a54e4..8183016ba5 100644
--- a/lib/ssh/src/ssh_sup.erl
+++ b/lib/ssh/src/ssh_sup.erl
@@ -36,15 +36,14 @@ init(_) ->
intensity => 10,
period => 3600
},
- ChildSpecs = [#{id => Module,
- start => {Module, start_link, []},
- restart => permanent,
- shutdown => 4000, %brutal_kill,
- type => supervisor,
- modules => [Module]
+ ChildSpecs = [#{id => sshd_sup,
+ start => {sshd_sup, start_link, []},
+ type => supervisor
+ },
+ #{id => sshc_sup,
+ start => {sshc_sup, start_link, []},
+ type => supervisor
}
- || Module <- [sshd_sup,
- sshc_sup]
],
{ok, {SupFlags,ChildSpecs}}.
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index e70abf59c2..17f990c5d8 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -63,9 +63,7 @@ init([Address, Port, Profile, Options]) ->
[#{id => id(ssh_acceptor_sup, Address, Port, Profile),
start => {ssh_acceptor_sup, start_link, [Address, Port, Profile, Options]},
restart => transient,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_acceptor_sup]
+ type => supervisor
}];
_ ->
[]
@@ -124,9 +122,8 @@ start_subsystem(SystemSup, Role, Address, Port, Profile, Options) ->
#{id => make_ref(),
start => {ssh_subsystem_sup, start_link, [Role, Address, Port, Profile, Options]},
restart => temporary,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_subsystem_sup]},
+ type => supervisor
+ },
supervisor:start_child(SystemSup, SubsystemSpec).
stop_subsystem(SystemSup, SubSys) ->
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index ad9efc4755..975053d301 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -889,10 +889,13 @@ known_host_key(#ssh{opts = Opts, key_cb = {KeyCb,KeyCbOpts}, peer = {PeerName,_}
{_,true} ->
ok;
{_,false} ->
+ DoAdd = ?GET_OPT(save_accepted_host, Opts),
case accepted_host(Ssh, PeerName, Public, Opts) of
- true ->
+ true when DoAdd == true ->
{_,R} = add_host_key(KeyCb, PeerName, Public, [{key_cb_private,KeyCbOpts}|UserOpts]),
R;
+ true when DoAdd == false ->
+ ok;
false ->
{error, rejected_by_user};
{error,E} ->
diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl
index 133b2c6450..fd4d8a3c07 100644
--- a/lib/ssh/src/sshc_sup.erl
+++ b/lib/ssh/src/sshc_sup.erl
@@ -60,10 +60,7 @@ init(_) ->
},
ChildSpecs = [#{id => undefined, % As simple_one_for_one is used.
start => {ssh_connection_handler, start_link, []},
- restart => temporary,
- shutdown => 4000,
- type => worker,
- modules => [ssh_connection_handler]
+ restart => temporary % because there is no way to restart a crashed connection
}
],
{ok, {SupFlags,ChildSpecs}}.
diff --git a/lib/ssh/src/sshd_sup.erl b/lib/ssh/src/sshd_sup.erl
index c23e65d955..779a861a54 100644
--- a/lib/ssh/src/sshd_sup.erl
+++ b/lib/ssh/src/sshd_sup.erl
@@ -90,10 +90,8 @@ init(_) ->
child_spec(Address, Port, Profile, Options) ->
#{id => id(Address, Port, Profile),
start => {ssh_system_sup, start_link, [Address, Port, Profile, Options]},
- restart => temporary,
- shutdown => infinity,
- type => supervisor,
- modules => [ssh_system_sup]
+ restart => temporary,
+ type => supervisor
}.
id(Address, Port, Profile) ->
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 202b0afe57..365f25fabb 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -60,7 +60,7 @@
login_bad_pwd_no_retry5/1,
misc_ssh_options/1,
openssh_zlib_basic_test/1,
- packet_size_zero/1,
+ packet_size/1,
pass_phrase/1,
peername_sockname/1,
send/1,
@@ -111,7 +111,7 @@ all() ->
double_close,
daemon_opt_fd,
multi_daemon_opt_fd,
- packet_size_zero,
+ packet_size,
ssh_info_print,
{group, login_bad_pwd_no_retry},
shell_exit_status
@@ -764,11 +764,11 @@ cli(Config) when is_list(Config) ->
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
ssh_connection:shell(ConnectionRef, ChannelId),
- ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
+ ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
receive
{ssh_cm, ConnectionRef,
{data,0,0, <<"\r\nYou are accessing a dummy, type \"q\" to exit\r\n\n">>}} ->
- ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
+ ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
end,
@@ -1104,7 +1104,7 @@ multi_daemon_opt_fd(Config) ->
end || {S,Pid,C} <- Tests].
%%--------------------------------------------------------------------
-packet_size_zero(Config) ->
+packet_size(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
@@ -1119,21 +1119,31 @@ packet_size_zero(Config) ->
{user_interaction, false},
{user, "vego"},
{password, "morot"}]),
-
- {ok,Chan} = ssh_connection:session_channel(Conn, 1000, _MaxPacketSize=0, 60000),
- ok = ssh_connection:shell(Conn, Chan),
+ lists:foreach(
+ fun(MaxPacketSize) ->
+ ct:log("Try max_packet_size=~p",[MaxPacketSize]),
+ {ok,Ch} = ssh_connection:session_channel(Conn, 1000, MaxPacketSize, 60000),
+ ok = ssh_connection:shell(Conn, Ch),
+ rec(Server, Conn, Ch, MaxPacketSize)
+ end, [0, 1, 10, 25]),
ssh:close(Conn),
- ssh:stop_daemon(Server),
+ ssh:stop_daemon(Server).
+rec(Server, Conn, Ch, MaxSz) ->
receive
- {ssh_cm,Conn,{data,Chan,_Type,_Msg1}} = M ->
- ct:log("Got ~p",[M]),
- ct:fail(doesnt_obey_max_packet_size_0)
- after 5000 ->
- ok
- end.
-
+ {ssh_cm,Conn,{data,Ch,_,M}} when size(M) =< MaxSz ->
+ ct:log("~p: ~p",[MaxSz,M]),
+ rec(Server, Conn, Ch, MaxSz);
+ {ssh_cm,Conn,{data,Ch,_,_}} = M ->
+ ct:log("Max pkt size=~p. Got ~p",[MaxSz,M]),
+ ssh:close(Conn),
+ ssh:stop_daemon(Server),
+ ct:fail("Does not obey max_packet_size=~p",[MaxSz])
+ after
+ 2000 -> ok
+ end.
+
%%--------------------------------------------------------------------
shell_no_unicode(Config) ->
new_do_shell(proplists:get_value(io,Config),
@@ -1491,7 +1501,7 @@ new_do_shell(IO, N, Ops=[{Order,Arg}|More]) ->
ct:fail("*** Expected ~p, but got ~p",[string:strip(ExpStr),RecStr])
end
after 30000 ->
- ct:log("Meassage queue of ~p:~n~p",
+ ct:log("Message queue of ~p:~n~p",
[self(), erlang:process_info(self(), messages)]),
case Order of
expect -> ct:fail("timeout, expected ~p",[string:strip(Arg)]);
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
index 82b83dd83d..f7eda1dc08 100644
--- a/lib/ssh/test/ssh_compat_SUITE.erl
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -92,6 +92,7 @@ end_per_suite(Config) ->
%%% os:cmd("docker rm $(docker ps -aq -f status=exited)"),
%% Remove dangling images:
%%% os:cmd("docker rmi $(docker images -f dangling=true -q)"),
+ catch ssh:stop(),
Config.
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index ba4518cfe6..9587c0c251 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -45,6 +45,8 @@ all() ->
{group, openssh},
small_interrupted_send,
interrupted_send,
+ exec_erlang_term,
+ exec_erlang_term_non_default_shell,
start_shell,
start_shell_exec,
start_shell_exec_fun,
@@ -85,6 +87,7 @@ init_per_suite(Config) ->
?CHECK_CRYPTO(Config).
end_per_suite(Config) ->
+ catch ssh:stop(),
Config.
%%--------------------------------------------------------------------
@@ -542,6 +545,79 @@ start_shell_exec(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+exec_erlang_term(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"}
+ ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got expected ~p",[R]);
+ Other ->
+ ct:log("Got unexpected ~p",[Other])
+ after 5000 ->
+ {fail,"Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
+
+%%--------------------------------------------------------------------
+exec_erlang_term_non_default_shell(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {shell, fun(U, H) -> start_our_shell(U, H) end}
+ ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}
+ ]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got unexpected ~p",[R]),
+ {fail,"Could exec erlang term although non-erlang shell"};
+ Other ->
+ ct:log("Got expected ~p",[Other])
+ after 5000 ->
+ {fail, "Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
+
+%%--------------------------------------------------------------------
start_shell_exec_fun() ->
[{doc, "start shell to exec command"}].
@@ -800,6 +876,8 @@ stop_listener(Config) when is_list(Config) ->
ssh:stop_daemon(Pid0),
ssh:stop_daemon(Pid1);
Error ->
+ ssh:close(ConnectionRef0),
+ ssh:stop_daemon(Pid0),
ct:fail({unexpected, Error})
end.
@@ -819,11 +897,22 @@ start_subsystem_on_closed_channel(Config) ->
{user_interaction, false},
{user_dir, UserDir}]),
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
- ok = ssh_connection:close(ConnectionRef, ChannelId),
+ {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:close(ConnectionRef, ChannelId1),
+ {error, closed} = ssh_connection:ptty_alloc(ConnectionRef, ChannelId1, []),
+ {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId1, "echo_n", 5000),
+ {error, closed} = ssh_connection:exec(ConnectionRef, ChannelId1, "testing1.\n", 5000),
+ {error, closed} = ssh_connection:send(ConnectionRef, ChannelId1, "exit().\n", 5000),
- {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity),
+ %% Test that there could be a gap between close and an operation (Bugfix OTP-14939):
+ {ok, ChannelId2} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:close(ConnectionRef, ChannelId2),
+ timer:sleep(2000),
+ {error, closed} = ssh_connection:ptty_alloc(ConnectionRef, ChannelId2, []),
+ {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId2, "echo_n", 5000),
+ {error, closed} = ssh_connection:exec(ConnectionRef, ChannelId2, "testing1.\n", 5000),
+ {error, closed} = ssh_connection:send(ConnectionRef, ChannelId2, "exit().\n", 5000),
ssh:close(ConnectionRef),
ssh:stop_daemon(Pid).
diff --git a/lib/ssh/test/ssh_engine_SUITE.erl b/lib/ssh/test/ssh_engine_SUITE.erl
index daf93891e9..c131a70973 100644
--- a/lib/ssh/test/ssh_engine_SUITE.erl
+++ b/lib/ssh/test/ssh_engine_SUITE.erl
@@ -55,16 +55,22 @@ basic_tests() ->
init_per_suite(Config) ->
ssh:start(),
?CHECK_CRYPTO(
- case load_engine() of
- {ok,E} ->
- [{engine,E}|Config];
- {error, notsup} ->
- {skip, "Engine not supported on this OpenSSL version"};
- {error, bad_engine_id} ->
- {skip, "Dynamic Engine not supported"};
- Other ->
- ct:log("Engine load failed: ~p",[Other]),
- {fail, "Engine load failed"}
+ case crypto:info_lib() of
+ [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}] ->
+ {skip, "Strange Engine stuff"};
+
+ _ ->
+ case load_engine() of
+ {ok,E} ->
+ [{engine,E}|Config];
+ {error, notsup} ->
+ {skip, "Engine not supported on this OpenSSL version"};
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"};
+ Other ->
+ ct:log("Engine load failed: ~p",[Other]),
+ {fail, "Engine load failed"}
+ end
end
).
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index 144ec7f8fd..12a85c40aa 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -70,7 +70,8 @@
hostkey_fingerprint_check_sha256/1,
hostkey_fingerprint_check_sha384/1,
hostkey_fingerprint_check_sha512/1,
- hostkey_fingerprint_check_list/1
+ hostkey_fingerprint_check_list/1,
+ save_accepted_host_option/1
]).
%%% Common test callbacks
@@ -124,6 +125,7 @@ all() ->
id_string_own_string_server,
id_string_own_string_server_trail_space,
id_string_random_server,
+ save_accepted_host_option,
{group, hardening_tests}
].
@@ -206,32 +208,23 @@ end_per_group(_, Config) ->
%%--------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
ssh:start(),
- Config.
-
-end_per_testcase(TestCase, Config) when TestCase == server_password_option;
- TestCase == server_userpassword_option;
- TestCase == server_pwdfun_option;
- TestCase == server_pwdfun_4_option ->
+ %% Create a clean user_dir
UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey),
ssh_test_lib:del_dirs(UserDir),
- end_per_testcase(Config);
-end_per_testcase(_TestCase, Config) ->
- end_per_testcase(Config).
+ file:make_dir(UserDir),
+ [{user_dir,UserDir}|Config].
-end_per_testcase(_Config) ->
+end_per_testcase(_TestCase, Config) ->
ssh:stop(),
ok.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-%%--------------------------------------------------------------------
%%% validate to server that uses the 'password' option
server_password_option(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
@@ -262,12 +255,10 @@ server_password_option(Config) when is_list(Config) ->
%%% validate to server that uses the 'password' option
server_userpassword_option(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{user_passwords, [{"vego", "morot"}]}]),
ConnectionRef =
@@ -297,15 +288,13 @@ server_userpassword_option(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%%% validate to server that uses the 'pwdfun' option
server_pwdfun_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
CHKPWD = fun("foo",Pwd) -> Pwd=="bar";
(_,_) -> false
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{pwdfun,CHKPWD}]),
ConnectionRef =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
@@ -335,9 +324,7 @@ server_pwdfun_option(Config) ->
%%--------------------------------------------------------------------
%%% validate to server that uses the 'pwdfun/4' option
server_pwdfun_4_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
PWDFUN = fun("foo",Pwd,{_,_},undefined) -> Pwd=="bar";
("fie",Pwd,{_,_},undefined) -> {Pwd=="bar",new_state};
@@ -345,7 +332,7 @@ server_pwdfun_4_option(Config) ->
(_,_,_,_) -> false
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{pwdfun,PWDFUN}]),
ConnectionRef1 =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
@@ -395,9 +382,7 @@ server_pwdfun_4_option(Config) ->
%%--------------------------------------------------------------------
server_pwdfun_4_option_repeat(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
%% Test that the state works
Parent = self(),
@@ -406,7 +391,7 @@ server_pwdfun_4_option_repeat(Config) ->
(_,P,_,S) -> Parent!{P,S}, {false,S+1}
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{auth_methods,"keyboard-interactive"},
{pwdfun,PWDFUN}]),
@@ -490,9 +475,7 @@ user_dir_option(Config) ->
%%--------------------------------------------------------------------
%%% validate client that uses the 'ssh_msg_debug_fun' option
ssh_msg_debug_fun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
@@ -530,9 +513,7 @@ ssh_msg_debug_fun_option_client(Config) ->
%%--------------------------------------------------------------------
connectfun_disconnectfun_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -576,9 +557,7 @@ connectfun_disconnectfun_server(Config) ->
%%--------------------------------------------------------------------
connectfun_disconnectfun_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -607,9 +586,7 @@ connectfun_disconnectfun_client(Config) ->
%%--------------------------------------------------------------------
%%% validate client that uses the 'ssh_msg_debug_fun' option
ssh_msg_debug_fun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -651,9 +628,7 @@ ssh_msg_debug_fun_option_server(Config) ->
%%--------------------------------------------------------------------
disconnectfun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -686,9 +661,7 @@ disconnectfun_option_server(Config) ->
%%--------------------------------------------------------------------
disconnectfun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -720,9 +693,7 @@ disconnectfun_option_client(Config) ->
%%--------------------------------------------------------------------
unexpectedfun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -763,9 +734,7 @@ unexpectedfun_option_server(Config) ->
%%--------------------------------------------------------------------
unexpectedfun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -840,14 +809,9 @@ supported_hash(HashAlg) ->
really_do_hostkey_fingerprint_check(Config, HashAlg) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDirServer = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDirServer),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
- UserDirClient =
- ssh_test_lib:create_random_dir(Config), % Ensure no 'known_hosts' disturbs
-
%% All host key fingerprints. Trust that public_key has checked the ssh_hostkey_fingerprint
%% function since that function is used by the ssh client...
FPs0 = [case HashAlg of
@@ -873,7 +837,7 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
%% Start daemon with the public keys that we got fingerprints from
{Pid, Host0, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, UserDirServer},
+ {user_dir, UserDir},
{password, "morot"}]),
Host = ssh_test_lib:ntoa(Host0),
FP_check_fun = fun(PeerName, FP) ->
@@ -896,7 +860,8 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
end},
{user, "foo"},
{password, "morot"},
- {user_dir, UserDirClient},
+ {user_dir, UserDir},
+ {save_accepted_host, false}, % Ensure no 'known_hosts' disturbs
{user_interaction, false}]),
ssh:stop_daemon(Pid).
@@ -987,9 +952,7 @@ ms_passed(T0) ->
%%--------------------------------------------------------------------
ssh_daemon_minimal_remote_max_packet_size_option(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
{Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
@@ -1314,6 +1277,33 @@ try_to_connect(Connect, Host, Port, Pid, Tref, N) ->
end.
%%--------------------------------------------------------------------
+save_accepted_host_option(Config) ->
+ UserDir = proplists:get_value(user_dir, Config),
+ KnownHosts = filename:join(UserDir, "known_hosts"),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"vego", "morot"}]}
+ ]),
+ {error,enoent} = file:read_file(KnownHosts),
+
+ {ok,_C1} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false},
+ {save_accepted_host, false},
+ {user_dir, UserDir}]),
+ {error,enoent} = file:read_file(KnownHosts),
+
+ {ok,_C2} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+ {ok,_} = file:read_file(KnownHosts),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index 3920a1c592..1df55834b1 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -42,7 +42,9 @@ suite() ->
all() ->
[default_tree, sshc_subtree, sshd_subtree, sshd_subtree_profile,
- killed_acceptor_restarts].
+ killed_acceptor_restarts,
+ shell_channel_tree
+ ].
groups() ->
[].
@@ -199,7 +201,7 @@ killed_acceptor_restarts(Config) ->
Port2 = ssh_test_lib:daemon_port(DaemonPid2),
true = (Port /= Port2),
- ct:pal("~s",[lists:flatten(ssh_info:string())]),
+ ct:log("~s",[lists:flatten(ssh_info:string())]),
{ok,[{AccPid,ListenAddr,Port}]} = acceptor_pid(DaemonPid),
{ok,[{AccPid2,ListenAddr,Port2}]} = acceptor_pid(DaemonPid2),
@@ -216,11 +218,14 @@ killed_acceptor_restarts(Config) ->
%% Make acceptor restart:
exit(AccPid, kill),
+ ?wait_match(undefined, process_info(AccPid)),
%% Check it is a new acceptor:
- {ok,[{AccPid1,ListenAddr,Port}]} = acceptor_pid(DaemonPid),
- true = (AccPid /= AccPid1),
- true = (AccPid2 /= AccPid1),
+ ?wait_match({ok,[{AccPid1,ListenAddr,Port}]}, AccPid1=/=AccPid,
+ acceptor_pid(DaemonPid),
+ AccPid1,
+ 500, 30),
+ AccPid1 =/= AccPid2,
%% Connect second client and check it is alive:
{ok,C2} = ssh:connect("localhost", Port, [{silently_accept_hosts, true},
@@ -230,21 +235,113 @@ killed_acceptor_restarts(Config) ->
{user_dir, UserDir}]),
[{client_version,_}] = ssh:connection_info(C2,[client_version]),
- ct:pal("~s",[lists:flatten(ssh_info:string())]),
+ ct:log("~s",[lists:flatten(ssh_info:string())]),
%% Check first client is still alive:
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
ok = ssh:stop_daemon(DaemonPid2),
- timer:sleep(15000),
+ ?wait_match(undefined, process_info(DaemonPid2), 1000, 30),
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
[{client_version,_}] = ssh:connection_info(C2,[client_version]),
ok = ssh:stop_daemon(DaemonPid),
- timer:sleep(15000),
+ ?wait_match(undefined, process_info(DaemonPid), 1000, 30),
{error,closed} = ssh:connection_info(C1,[client_version]),
{error,closed} = ssh:connection_info(C2,[client_version]).
+
+%%-------------------------------------------------------------------------
+shell_channel_tree(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ TimeoutShell =
+ fun() ->
+ io:format("TimeoutShell started!~n",[]),
+ timer:sleep(5000),
+ ct:log("~p TIMEOUT!",[self()])
+ end,
+ {Daemon, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {shell, fun(_User) ->
+ spawn(TimeoutShell)
+ end
+ }
+ ]),
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ [ChannelSup|_] = Sups0 = chk_empty_con_daemon(Daemon),
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:shell(ConnectionRef,ChannelId0),
+
+ ?wait_match([{_, GroupPid,worker,[ssh_channel]}],
+ supervisor:which_children(ChannelSup),
+ [GroupPid]),
+ {links,GroupLinks} = erlang:process_info(GroupPid, links),
+ [ShellPid] = GroupLinks--[ChannelSup],
+ ct:log("GroupPid = ~p, ShellPid = ~p",[GroupPid,ShellPid]),
+
+ receive
+ {ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"TimeoutShell started!\r\n">>}} ->
+ receive
+ %%---- wait for the subsystem to terminate
+ {ssh_cm,ConnectionRef,{closed,ChannelId0}} ->
+ ct:log("Subsystem terminated",[]),
+ case {chk_empty_con_daemon(Daemon),
+ process_info(GroupPid),
+ process_info(ShellPid)} of
+ {Sups0, undefined, undefined} ->
+ %% SUCCESS
+ ssh:stop_daemon(Daemon);
+ {Sups0, _, undefined} ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Group proc lives!");
+ {Sups0, undefined, _} ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Shell proc lives!");
+ _ ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Sup tree changed!")
+ end
+ after 10000 ->
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Daemon),
+ ct:fail("CLI Timeout")
+ end
+ after 10000 ->
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Daemon),
+ ct:fail("CLI Timeout")
+ end.
+
+
+chk_empty_con_daemon(Daemon) ->
+ ?wait_match([{_,SubSysSup, supervisor,[ssh_subsystem_sup]},
+ {{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}],
+ supervisor:which_children(Daemon),
+ [SubSysSup,AccSup]),
+ ?wait_match([{{server,ssh_connection_sup, _,_},
+ ConnectionSup, supervisor,
+ [ssh_connection_sup]},
+ {{server,ssh_channel_sup,_ ,_},
+ ChannelSup,supervisor,
+ [ssh_channel_sup]}],
+ supervisor:which_children(SubSysSup),
+ [ConnectionSup,ChannelSup]),
+ ?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}],
+ supervisor:which_children(AccSup)),
+ ?wait_match([{_, _, worker,[ssh_connection_handler]}],
+ supervisor:which_children(ConnectionSup)),
+ ?wait_match([], supervisor:which_children(ChannelSup)),
+ [ChannelSup, ConnectionSup, SubSysSup, AccSup].
+
%%-------------------------------------------------------------------------
%% Help functions
%%-------------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index f97c3b1352..57ae2dbac2 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -53,10 +53,12 @@ daemon(Host, Options) ->
daemon(Host, Port, Options) ->
- %% ct:log("~p:~p Calling ssh:daemon(~p, ~p, ~p)",[?MODULE,?LINE,Host,Port,Options]),
+ ct:log("~p:~p Calling ssh:daemon(~p, ~p, ~p)",[?MODULE,?LINE,Host,Port,Options]),
case ssh:daemon(Host, Port, Options) of
{ok, Pid} ->
- {ok,L} = ssh:daemon_info(Pid),
+ R = ssh:daemon_info(Pid),
+ ct:log("~p:~p ssh:daemon_info(~p) ->~n ~p",[?MODULE,?LINE,Pid,R]),
+ {ok,L} = R,
ListenPort = proplists:get_value(port, L),
ListenIP = proplists:get_value(ip, L),
{Pid, ListenIP, ListenPort};
diff --git a/lib/ssh/test/ssh_test_lib.hrl b/lib/ssh/test/ssh_test_lib.hrl
index eaf856e6e8..4b6579bd71 100644
--- a/lib/ssh/test/ssh_test_lib.hrl
+++ b/lib/ssh/test/ssh_test_lib.hrl
@@ -16,12 +16,12 @@
%%-------------------------------------------------------------------------
%% Help macro
%%-------------------------------------------------------------------------
--define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+-define(wait_match(Pattern, Guard, FunctionCall, Bind, Timeout, Ntries),
Bind =
(fun() ->
F = fun(N, F1) ->
case FunctionCall of
- Pattern -> Bind;
+ Pattern when Guard -> Bind;
_ when N>0 ->
ct:pal("Must sleep ~p ms at ~p:~p",[Timeout,?MODULE,?LINE]),
timer:sleep(Timeout),
@@ -34,6 +34,9 @@
end)()
).
+-define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+ ?wait_match(Pattern, true, FunctionCall, Bind, Timeout, Ntries)).
+
-define(wait_match(Pattern, FunctionCall, Timeout, Ntries), ?wait_match(Pattern, FunctionCall, ok, Timeout, Ntries)).
-define(wait_match(Pattern, FunctionCall, Bind), ?wait_match(Pattern, FunctionCall, Bind, 500, 10) ).
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 004db6e3a2..480e955ec4 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,4 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.6.4
-
+SSH_VSN = 4.6.5
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 79176f5edf..bdf8711b2f 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -307,6 +307,21 @@
</section>
</section>
+<section><title>SSL 8.1.3.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix alert handling so that unexpected messages are logged
+ and alerted correctly</p>
+ <p>
+ Own Id: OTP-14929</p>
+ </item>
+ </list>
+ </section>
+</section>
+
<section><title>SSL 8.1.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 1d645e5782..a83ce42455 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -96,7 +96,7 @@ security_parameters(Version, CipherSuite, SecParams) ->
expanded_key_material_length = expanded_key_material(Cipher),
key_material_length = key_material(Cipher),
iv_size = iv_size(Cipher),
- mac_algorithm = hash_algorithm(Hash),
+ mac_algorithm = mac_algorithm(Hash),
prf_algorithm = prf_algorithm(PrfHashAlg, Version),
hash_size = hash_size(Hash)}.
@@ -2531,6 +2531,11 @@ prf_algorithm(default_prf, {3, _}) ->
prf_algorithm(Algo, _) ->
hash_algorithm(Algo).
+mac_algorithm(aead) ->
+ aead;
+mac_algorithm(Algo) ->
+ hash_algorithm(Algo).
+
hash_algorithm(null) -> ?NULL;
hash_algorithm(md5) -> ?MD5;
hash_algorithm(sha) -> ?SHA; %% Only sha always refers to "SHA-1"
@@ -2561,6 +2566,10 @@ sign_algorithm(Other) when is_integer(Other) andalso ((Other >= 224) and (Other
hash_size(null) ->
0;
+%% The AEAD MAC hash size is not used in the context
+%% of calculating the master secret. See RFC 5246 Section 6.2.3.3.
+hash_size(aead) ->
+ 0;
hash_size(md5) ->
16;
hash_size(sha) ->
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 0c55af9174..09160e2f9c 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -774,9 +774,8 @@ decode_suites('3_bytes', Dec) ->
%%====================================================================
available_suites(UserSuites, Version) ->
- lists:filtermap(fun(Suite) ->
- lists:member(Suite, ssl_cipher:all_suites(Version))
- end, UserSuites).
+ VersionSuites = ssl_cipher:all_suites(Version) ++ ssl_cipher:anonymous_suites(Version),
+ lists:filtermap(fun(Suite) -> lists:member(Suite, VersionSuites) end, UserSuites).
available_suites(ServerCert, UserSuites, Version, undefined, Curve) ->
ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version))
@@ -1056,7 +1055,9 @@ select_curve(undefined, _, _) ->
%%
%% Description: Handles signature_algorithms hello extension (server)
%%--------------------------------------------------------------------
-select_hashsign(_, undefined, _, _, _Version) ->
+select_hashsign(_, _, KeyExAlgo, _, _Version) when KeyExAlgo == dh_anon;
+ KeyExAlgo == ecdh_anon;
+ KeyExAlgo == srp_anon ->
{null, anon};
%% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have
%% negotiated a lower version.
diff --git a/lib/ssl/test/ssl_engine_SUITE.erl b/lib/ssl/test/ssl_engine_SUITE.erl
index bc221d35fd..71891356e8 100644
--- a/lib/ssl/test/ssl_engine_SUITE.erl
+++ b/lib/ssl/test/ssl_engine_SUITE.erl
@@ -39,23 +39,28 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl_test_lib:clean_start(),
- case crypto:get_test_engine() of
- {ok, EngineName} ->
- try crypto:engine_load(<<"dynamic">>,
- [{<<"SO_PATH">>, EngineName},
- <<"LOAD">>],
- []) of
- {ok, Engine} ->
- [{engine, Engine} |Config];
- {error, Reason} ->
- ct:pal("Reason ~p", [Reason]),
- {skip, "No dynamic engine support"}
- catch error:notsup ->
- {skip, "No engine support in OpenSSL"}
- end;
- {error, notexist} ->
- {skip, "Test engine not found"}
+ case crypto:info_lib() of
+ [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}] ->
+ {skip, "Problem with engine on OpenSSL 1.0.1s-freebsd"};
+ _ ->
+ ssl_test_lib:clean_start(),
+ case crypto:get_test_engine() of
+ {ok, EngineName} ->
+ try crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, EngineName},
+ <<"LOAD">>],
+ []) of
+ {ok, Engine} ->
+ [{engine, Engine} |Config];
+ {error, Reason} ->
+ ct:pal("Reason ~p", [Reason]),
+ {skip, "No dynamic engine support"}
+ catch error:notsup ->
+ {skip, "No engine support in OpenSSL"}
+ end;
+ {error, notexist} ->
+ {skip, "Test engine not found"}
+ end
end
catch _:_ ->
{skip, "Crypto did not start"}
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index f091c8786e..dcdea6beb5 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -70,6 +70,9 @@ all_versions_tests() ->
erlang_server_openssl_client,
erlang_client_openssl_server_dsa_cert,
erlang_server_openssl_client_dsa_cert,
+ erlang_client_openssl_server_anon,
+ erlang_server_openssl_client_anon,
+ erlang_server_openssl_client_anon_with_cert,
erlang_server_openssl_client_reuse_session,
erlang_client_openssl_server_renegotiate,
erlang_client_openssl_server_nowrap_seqnum,
@@ -89,6 +92,9 @@ dtls_all_versions_tests() ->
erlang_server_openssl_client,
erlang_client_openssl_server_dsa_cert,
erlang_server_openssl_client_dsa_cert,
+ erlang_client_openssl_server_anon,
+ erlang_server_openssl_client_anon,
+ erlang_server_openssl_client_anon_with_cert,
erlang_server_openssl_client_reuse_session,
erlang_client_openssl_server_renegotiate,
erlang_client_openssl_server_nowrap_seqnum,
@@ -550,7 +556,121 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
ssl_test_lib:close_port(OpenSslPort),
process_flag(trap_exit, false).
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+erlang_client_openssl_server_anon() ->
+ [{doc,"Test erlang client with openssl server, anonymous"}].
+erlang_client_openssl_server_anon(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ %% OpenSSL expects a certificate and key, even if the cipher spec
+ %% is restructed to aNULL, so we use 'server_rsa_opts' here
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_anon_opts, Config),
+ VersionTuple = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl_test_lib:anonymous_suites(VersionTuple),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile,
+ "-cipher", "aNULL", "-msg"],
+
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, [{ciphers, Ciphers} | ClientOpts]}]),
+
+ true = port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+%%--------------------------------------------------------------------
+erlang_server_openssl_client_anon() ->
+ [{doc,"Test erlang server with openssl client, anonymous"}].
+erlang_server_openssl_client_anon(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_anon_opts, Config),
+ VersionTuple = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl_test_lib:anonymous_suites(VersionTuple),
+
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, [{ciphers, Ciphers} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cipher", "aNULL", "-msg"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+ true = port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+ %%--------------------------------------------------------------------
+ erlang_server_openssl_client_anon_with_cert() ->
+ [{doc,"Test erlang server with openssl client, anonymous (with cert)"}].
+ erlang_server_openssl_client_anon_with_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ VersionTuple = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl_test_lib:anonymous_suites(VersionTuple),
+
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, [{ciphers, Ciphers} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cipher", "aNULL", "-msg"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+ true = port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+%%--------------------------------------------------------------------
erlang_server_openssl_client_reuse_session() ->
[{doc, "Test erlang server with openssl client that reconnects with the"
diff --git a/lib/stdlib/doc/src/assert_hrl.xml b/lib/stdlib/doc/src/assert_hrl.xml
index ea23cca2ee..33f29f38da 100644
--- a/lib/stdlib/doc/src/assert_hrl.xml
+++ b/lib/stdlib/doc/src/assert_hrl.xml
@@ -93,7 +93,7 @@ erlc -DNOASSERT=true *.erl</code>
<taglist>
<tag><c>assert(BoolExpr)</c></tag>
<item></item>
- <tag><c>URKAassert(BoolExpr, Comment)</c></tag>
+ <tag><c>assert(BoolExpr, Comment)</c></tag>
<item>
<p>Tests that <c>BoolExpr</c> completes normally returning
<c>true</c>.</p>
diff --git a/lib/stdlib/doc/src/erl_tar.xml b/lib/stdlib/doc/src/erl_tar.xml
index caf8f4a96d..14c543ee2b 100644
--- a/lib/stdlib/doc/src/erl_tar.xml
+++ b/lib/stdlib/doc/src/erl_tar.xml
@@ -417,7 +417,7 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Cconverts an error reason term to a human-readable error message
+ <p>Converts an error reason term to a human-readable error message
string.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 1b31a1ec9d..305376a425 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -487,6 +487,11 @@ Error: fun containing local Erlang function calls
<p>The pid of the heir of the table, or <c>none</c> if no heir
is set.</p>
</item>
+ <tag><c>{id,</c><seealso marker="#type-tid">
+ <c>tid()</c></seealso><c>}</c></tag>
+ <item>
+ <p>The table identifier.</p>
+ </item>
<tag><c>{keypos, integer() >= 1}</c></tag>
<item>
<p>The key position.</p>
@@ -1074,10 +1079,13 @@ ets:select(Table, MatchSpec),</code>
</item>
<tag><c>named_table</c></tag>
<item>
- <p>If this option is present, name <c><anno>Name</anno></c> is
- associated with the table identifier. The name can then
- be used instead of the table identifier in subsequent
- operations.</p>
+ <p>If this option is present, the table is registered under its
+ <c><anno>Name</anno></c> which can then be used instead of the
+ table identifier in subsequent operations.</p>
+ <p>The function will also return the <c><anno>Name</anno></c>
+ instead of the table identifier. To get the table identifier of a
+ named table, use
+ <seealso marker="#whereis/1"><c>whereis/1</c></seealso>.</p>
</item>
<tag><c>{keypos,<anno>Pos</anno>}</c></tag>
<item>
@@ -2037,6 +2045,21 @@ true</pre>
</list>
</desc>
</func>
+
+ <func>
+ <name name="whereis" arity="1"/>
+ <fsummary>Retrieves the tid() of a named table.</fsummary>
+ <desc>
+ <p>This function returns the
+ <seealso marker="#type-tid"><c>tid()</c></seealso> of the named table
+ identified by <c><anno>TableName</anno></c>, or <c>undefined</c> if
+ no such table exists. The <c>tid()</c> can be used in place of the
+ table name in all operations, which is slightly faster since the name
+ does not have to be resolved on each call.</p>
+ <p>If the table is deleted, the <c>tid()</c> will be invalid even if
+ another named table is created with the same name.</p>
+ </desc>
+ </func>
</funcs>
</erlref>
diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml
index fcaccdb2cb..350847bf7d 100644
--- a/lib/stdlib/doc/src/timer.xml
+++ b/lib/stdlib/doc/src/timer.xml
@@ -270,7 +270,7 @@
<item>
<p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
<anno>Arguments</anno>)</c> and measures the elapsed real time as
- reported by <seealso marker="os:timestamp/0">
+ reported by <seealso marker="kernel:os#timestamp/0">
<c>os:timestamp/0</c></seealso>.</p>
<p>Returns <c>{<anno>Time</anno>, <anno>Value</anno>}</c>, where
<c><anno>Time</anno></c> is the elapsed real time in
diff --git a/lib/stdlib/doc/src/uri_string.xml b/lib/stdlib/doc/src/uri_string.xml
index 21f470e763..88d4600611 100644
--- a/lib/stdlib/doc/src/uri_string.xml
+++ b/lib/stdlib/doc/src/uri_string.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2017</year><year>2017</year>
+ <year>2017</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -24,7 +24,7 @@
<title>uri_string</title>
<prepared>Péter Dimitrov</prepared>
<docno>1</docno>
- <date>2017-10-24</date>
+ <date>2018-02-07</date>
<rev>A</rev>
</header>
<module>uri_string</module>
@@ -32,7 +32,11 @@
<description>
<p>This module contains functions for parsing and handling URIs
(<url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>) and
- form-urlencoded query strings (<url href="https://www.w3.org/TR/html5/forms.html">HTML5</url>).
+ form-urlencoded query strings (<url href="https://www.w3.org/TR/html52/">HTML 5.2</url>).
+ </p>
+ <p>
+ Parsing and serializing non-UTF-8 form-urlencoded query strings are also supported
+ (<url href="https://www.w3.org/TR/html50/">HTML 5.0</url>).
</p>
<p>A URI is an identifier consisting of a sequence of characters matching the syntax
rule named <em>URI</em> in <url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>.
@@ -70,7 +74,8 @@
<seealso marker="#transcode/2"><c>transcode/2</c></seealso>
</item>
<item>Transforming URIs into a normalized form<br></br>
- <seealso marker="#normalize/1"><c>normalize/1</c></seealso>
+ <seealso marker="#normalize/1"><c>normalize/1</c></seealso><br></br>
+ <seealso marker="#normalize/2"><c>normalize/2</c></seealso>
</item>
<item>Composing form-urlencoded query strings from a list of key-value pairs<br></br>
<seealso marker="#compose_query/1"><c>compose_query/1</c></seealso><br></br>
@@ -151,8 +156,10 @@
<p>Composes a form-urlencoded <c><anno>QueryString</anno></c> based on a
<c><anno>QueryList</anno></c>, a list of non-percent-encoded key-value pairs.
Form-urlencoding is defined in section
- 4.10.22.6 of the <url href="https://www.w3.org/TR/html5/forms.html">HTML5</url>
- specification.
+ 4.10.21.6 of the <url href="https://www.w3.org/TR/html52/">HTML 5.2</url>
+ specification and in section 4.10.22.6 of the
+ <url href="https://www.w3.org/TR/html50/">HTML 5.0</url> specification for
+ non-UTF-8 encodings.
</p>
<p>See also the opposite operation <seealso marker="#dissect_query/1">
<c>dissect_query/1</c></seealso>.
@@ -209,12 +216,11 @@
<p>Dissects an urlencoded <c><anno>QueryString</anno></c> and returns a
<c><anno>QueryList</anno></c>, a list of non-percent-encoded key-value pairs.
Form-urlencoding is defined in section
- 4.10.22.6 of the <url href="https://www.w3.org/TR/html5/forms.html">HTML5</url>
- specification.
+ 4.10.21.6 of the <url href="https://www.w3.org/TR/html52/">HTML 5.2</url>
+ specification and in section 4.10.22.6 of the
+ <url href="https://www.w3.org/TR/html50/">HTML 5.0</url> specification for
+ non-UTF-8 encodings.
</p>
- <p>It is not as strict for its input as the decoding algorithm defined by
- <url href="https://www.w3.org/TR/html5/forms.html">HTML5</url>
- and accepts all unicode characters.</p>
<p>See also the opposite operation <seealso marker="#compose_query/1">
<c>compose_query/1</c></seealso>.
</p>
@@ -233,7 +239,7 @@
<name name="normalize" arity="1"/>
<fsummary>Syntax-based normalization.</fsummary>
<desc>
- <p>Transforms <c><anno>URIString</anno></c> into a normalized form
+ <p>Transforms an <c><anno>URI</anno></c> into a normalized form
using Syntax-Based Normalization as defined by
<url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>.</p>
<p>This function implements case normalization, percent-encoding
@@ -247,6 +253,33 @@
<![CDATA[<<"mid/6">>]]>
3> uri_string:normalize("http://localhost:80").
"https://localhost/"
+4> <input>uri_string:normalize(#{scheme => "http",port => 80,path => "/a/b/c/./../../g",</input>
+4> host => "localhost-örebro"}).
+"http://localhost-%C3%B6rebro/a/g"
+ </pre>
+ </desc>
+ </func>
+
+ <func>
+ <name name="normalize" arity="2"/>
+ <fsummary>Syntax-based normalization.</fsummary>
+ <desc>
+ <p>Same as <c>normalize/1</c> but with an additional
+ <c><anno>Options</anno></c> parameter, that controls if the normalized URI
+ shall be returned as an uri_map().
+ There is one supported option: <c>return_map</c>.
+ </p>
+ <p><em>Example:</em></p>
+ <pre>
+1> <input>uri_string:normalize("/a/b/c/./../../g", [return_map]).</input>
+#{path => "/a/g"}
+2> <![CDATA[uri_string:normalize(<<"mid/content=5/../6">>, [return_map]).]]>
+<![CDATA[#{path => <<"mid/6">>}]]>
+3> uri_string:normalize("http://localhost:80", [return_map]).
+#{scheme => "http",path => "/",host => "localhost"}
+4> <input>uri_string:normalize(#{scheme => "http",port => 80,path => "/a/b/c/./../../g",</input>
+4> host => "localhost-örebro"}, [return_map]).
+#{scheme => "http",path => "/a/g",host => "localhost-örebro"}
</pre>
</desc>
</func>
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index 06c15fceda..24349c74e8 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -148,7 +148,8 @@ chunks(File, Chunks, Options) ->
try read_chunk_data(File, Chunks, Options)
catch Error -> Error end.
--spec all_chunks(beam()) -> {'ok', 'beam_lib', [{chunkid(), dataB()}]}.
+-spec all_chunks(beam()) ->
+ {'ok', 'beam_lib', [{chunkid(), dataB()}]} | {'error', 'beam_lib', info_rsn()}.
all_chunks(File) ->
read_all_chunks(File).
diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl
index 6a64133b45..7d0e42489e 100644
--- a/lib/stdlib/src/binary.erl
+++ b/lib/stdlib/src/binary.erl
@@ -47,23 +47,39 @@ at(_, _) ->
-spec bin_to_list(Subject) -> [byte()] when
Subject :: binary().
-bin_to_list(_) ->
- erlang:nif_error(undef).
+bin_to_list(Subject) ->
+ binary_to_list(Subject).
-spec bin_to_list(Subject, PosLen) -> [byte()] when
Subject :: binary(),
PosLen :: part().
-bin_to_list(_, _) ->
- erlang:nif_error(undef).
+bin_to_list(Subject, {Pos, Len}) ->
+ bin_to_list(Subject, Pos, Len);
+bin_to_list(_Subject, _BadArg) ->
+ erlang:error(badarg).
-spec bin_to_list(Subject, Pos, Len) -> [byte()] when
Subject :: binary(),
Pos :: non_neg_integer(),
Len :: integer().
-bin_to_list(_, _, _) ->
- erlang:nif_error(undef).
+bin_to_list(Subject, Pos, Len) when not is_binary(Subject);
+ not is_integer(Pos);
+ not is_integer(Len) ->
+ %% binary_to_list/3 allows bitstrings as long as the slice fits, and we
+ %% want to badarg when Pos/Len aren't integers instead of raising badarith
+ %% when adjusting args for binary_to_list/3.
+ erlang:error(badarg);
+bin_to_list(Subject, Pos, 0) when Pos >= 0, Pos =< byte_size(Subject) ->
+ %% binary_to_list/3 doesn't handle this case.
+ [];
+bin_to_list(_Subject, _Pos, 0) ->
+ erlang:error(badarg);
+bin_to_list(Subject, Pos, Len) when Len < 0 ->
+ bin_to_list(Subject, Pos + Len, -Len);
+bin_to_list(Subject, Pos, Len) when Len > 0 ->
+ binary_to_list(Subject, Pos + 1, Pos + Len).
-spec compile_pattern(Pattern) -> cp() when
Pattern :: binary() | [binary()].
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 00e6a10d8a..77cc88eb08 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1197,21 +1197,21 @@ skip_else(_Else, From, St, Sis) ->
%% macro_expansion(Tokens, Anno)
%% Extract the macro parameters and the expansion from a macro definition.
-macro_pars([{')',_Lp}, {',',Ld}|Ex], Args) ->
- {ok, {lists:reverse(Args), macro_expansion(Ex, Ld)}};
-macro_pars([{var,_,Name}, {')',_Lp}, {',',Ld}|Ex], Args) ->
+macro_pars([{')',_Lp}, {',',_Ld}=Comma|Ex], Args) ->
+ {ok, {lists:reverse(Args), macro_expansion(Ex, Comma)}};
+macro_pars([{var,_,Name}, {')',_Lp}, {',',_Ld}=Comma|Ex], Args) ->
false = lists:member(Name, Args), %Prolog is nice
- {ok, {lists:reverse([Name|Args]), macro_expansion(Ex, Ld)}};
+ {ok, {lists:reverse([Name|Args]), macro_expansion(Ex, Comma)}};
macro_pars([{var,_L,Name}, {',',_}|Ts], Args) ->
false = lists:member(Name, Args),
macro_pars(Ts, [Name|Args]).
-macro_expansion([{')',_Lp},{dot,_Ld}], _Anno0) -> [];
-macro_expansion([{dot,_}=Dot], _Anno0) ->
+macro_expansion([{')',_Lp},{dot,_Ld}], _T0) -> [];
+macro_expansion([{dot,_}=Dot], _T0) ->
throw({error,loc(Dot),missing_parenthesis});
-macro_expansion([T|Ts], _Anno0) ->
+macro_expansion([T|Ts], _T0) ->
[T|macro_expansion(Ts, T)];
-macro_expansion([], Anno0) -> throw({error,loc(Anno0),premature_end}).
+macro_expansion([], T0) -> throw({error,loc(T0),premature_end}).
%% expand_macros(Tokens, St)
%% expand_macro(Tokens, MacroToken, RestTokens)
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 039ab45868..6a559f0be5 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -73,7 +73,8 @@
select_count/2, select_delete/2, select_replace/2, select_reverse/1,
select_reverse/2, select_reverse/3, setopts/2, slot/2,
take/2,
- update_counter/3, update_counter/4, update_element/3]).
+ update_counter/3, update_counter/4, update_element/3,
+ whereis/1]).
%% internal exports
-export([internal_request_all/0]).
@@ -145,6 +146,7 @@ give_away(_, _, _) ->
InfoList :: [InfoTuple],
InfoTuple :: {compressed, boolean()}
| {heir, pid() | none}
+ | {id, tid()}
| {keypos, pos_integer()}
| {memory, non_neg_integer()}
| {name, atom()}
@@ -162,7 +164,7 @@ info(_) ->
-spec info(Tab, Item) -> Value | undefined when
Tab :: tab(),
- Item :: compressed | fixed | heir | keypos | memory
+ Item :: compressed | fixed | heir | id | keypos | memory
| name | named_table | node | owner | protection
| safe_fixed | safe_fixed_monotonic_time | size | stats | type
| write_concurrency | read_concurrency,
@@ -512,6 +514,11 @@ update_counter(_, _, _, _) ->
update_element(_, _, _) ->
erlang:nif_error(undef).
+-spec whereis(TableName) -> tid() | undefined when
+ TableName :: atom().
+whereis(_) ->
+ erlang:nif_error(undef).
+
%%% End of BIFs
-opaque comp_match_spec() :: reference().
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index e01bb7d85e..4e89819e41 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -420,10 +420,12 @@ to_number(_, Number, Rest, _, Tail) ->
%% Return the remaining string with prefix removed or else nomatch
-spec prefix(String::unicode:chardata(), Prefix::unicode:chardata()) ->
'nomatch' | unicode:chardata().
-prefix(Str, []) -> Str;
prefix(Str, Prefix0) ->
- Prefix = unicode:characters_to_list(Prefix0),
- case prefix_1(Str, Prefix) of
+ Result = case unicode:characters_to_list(Prefix0) of
+ [] -> Str;
+ Prefix -> prefix_1(Str, Prefix)
+ end,
+ case Result of
[] when is_binary(Str) -> <<>>;
Res -> Res
end.
diff --git a/lib/stdlib/src/uri_string.erl b/lib/stdlib/src/uri_string.erl
index a84679c595..28d36ea229 100644
--- a/lib/stdlib/src/uri_string.erl
+++ b/lib/stdlib/src/uri_string.erl
@@ -227,7 +227,7 @@
%% External API
%%-------------------------------------------------------------------------
-export([compose_query/1, compose_query/2,
- dissect_query/1, normalize/1, parse/1,
+ dissect_query/1, normalize/1, normalize/2, parse/1,
recompose/1, transcode/2]).
-export_type([error/0, uri_map/0, uri_string/0]).
@@ -292,18 +292,36 @@
%%-------------------------------------------------------------------------
%% Normalize URIs
%%-------------------------------------------------------------------------
--spec normalize(URIString) -> NormalizedURI when
- URIString :: uri_string(),
- NormalizedURI :: uri_string().
-normalize(URIString) ->
- %% Percent-encoding normalization and case normalization for
- %% percent-encoded triplets are achieved by running parse and
- %% recompose on the input URI string.
- recompose(
- normalize_path_segment(
- normalize_scheme_based(
- normalize_case(
- parse(URIString))))).
+-spec normalize(URI) -> NormalizedURI when
+ URI :: uri_string() | uri_map(),
+ NormalizedURI :: uri_string()
+ | error().
+normalize(URIMap) ->
+ normalize(URIMap, []).
+
+
+-spec normalize(URI, Options) -> NormalizedURI when
+ URI :: uri_string() | uri_map(),
+ Options :: [return_map],
+ NormalizedURI :: uri_string() | uri_map().
+normalize(URIMap, []) when is_map(URIMap) ->
+ recompose(normalize_map(URIMap));
+normalize(URIMap, [return_map]) when is_map(URIMap) ->
+ normalize_map(URIMap);
+normalize(URIString, []) ->
+ case parse(URIString) of
+ Value when is_map(Value) ->
+ recompose(normalize_map(Value));
+ Error ->
+ Error
+ end;
+normalize(URIString, [return_map]) ->
+ case parse(URIString) of
+ Value when is_map(Value) ->
+ normalize_map(Value);
+ Error ->
+ Error
+ end.
%%-------------------------------------------------------------------------
@@ -385,7 +403,8 @@ transcode(URIString, Options) when is_list(URIString) ->
%%-------------------------------------------------------------------------
%% Functions for working with the query part of a URI as a list
%% of key/value pairs.
-%% HTML5 - 4.10.22.6 URL-encoded form data
+%% HTML 5.2 - 4.10.21.6 URL-encoded form data - WHATWG URL (10 Jan 2018) - UTF-8
+%% HTML 5.0 - 4.10.22.6 URL-encoded form data - non UTF-8
%%-------------------------------------------------------------------------
%%-------------------------------------------------------------------------
@@ -393,7 +412,7 @@ transcode(URIString, Options) when is_list(URIString) ->
%% (application/x-www-form-urlencoded encoding algorithm)
%%-------------------------------------------------------------------------
-spec compose_query(QueryList) -> QueryString when
- QueryList :: [{uri_string(), uri_string()}],
+ QueryList :: [{unicode:chardata(), unicode:chardata()}],
QueryString :: uri_string()
| error().
compose_query(List) ->
@@ -401,7 +420,7 @@ compose_query(List) ->
-spec compose_query(QueryList, Options) -> QueryString when
- QueryList :: [{uri_string(), uri_string()}],
+ QueryList :: [{unicode:chardata(), unicode:chardata()}],
Options :: [{encoding, atom()}],
QueryString :: uri_string()
| error().
@@ -432,7 +451,7 @@ compose_query([], _Options, IsList, Acc) ->
%%-------------------------------------------------------------------------
-spec dissect_query(QueryString) -> QueryList when
QueryString :: uri_string(),
- QueryList :: [{uri_string(), uri_string()}]
+ QueryList :: [{unicode:chardata(), unicode:chardata()}]
| error().
dissect_query(<<>>) ->
[];
@@ -1755,7 +1774,8 @@ get_separator(_L) ->
<<"&">>.
-%% HTML5 - 4.10.22.6 URL-encoded form data - encoding
+%% HTML 5.2 - 4.10.21.6 URL-encoded form data - WHATWG URL (10 Jan 2018) - UTF-8
+%% HTML 5.0 - 4.10.22.6 URL-encoded form data - encoding (non UTF-8)
form_urlencode(Cs, [{encoding, latin1}]) when is_list(Cs) ->
B = convert_to_binary(Cs, utf8, utf8),
html5_byte_encode(base10_encode(B));
@@ -1850,7 +1870,8 @@ dissect_query_value(<<>>, IsList, Acc, Key, Value) ->
lists:reverse([{K,V}|Acc]).
-%% Form-urldecode input based on RFC 1866 [8.2.1]
+%% HTML 5.2 - 4.10.21.6 URL-encoded form data - WHATWG URL (10 Jan 2018) - UTF-8
+%% HTML 5.0 - 4.10.22.6 URL-encoded form data - decoding (non UTF-8)
form_urldecode(true, B) ->
Result = base10_decode(form_urldecode(B, <<>>)),
convert_to_list(Result, utf8);
@@ -1903,6 +1924,12 @@ base10_decode_unicode(<<H,_/binary>>, _, _) ->
%% Helper functions for normalize
%%-------------------------------------------------------------------------
+normalize_map(URIMap) ->
+ normalize_path_segment(
+ normalize_scheme_based(
+ normalize_case(URIMap))).
+
+
%% 6.2.2.1. Case Normalization
normalize_case(#{scheme := Scheme, host := Host} = Map) ->
Map#{scheme => to_lower(Scheme),
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 0cfb1b5516..8b651f4b43 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -78,6 +78,7 @@
-export([ets_all/1]).
-export([massive_ets_all/1]).
-export([take/1]).
+-export([whereis_table/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% Convenience for manual testing
@@ -137,7 +138,8 @@ all() ->
otp_9423,
ets_all,
massive_ets_all,
- take].
+ take,
+ whereis_table].
groups() ->
[{new, [],
@@ -4099,6 +4101,7 @@ info_do(Opts) ->
{value, {keypos, 2}} = lists:keysearch(keypos, 1, Res),
{value, {protection, protected}} =
lists:keysearch(protection, 1, Res),
+ {value, {id, Tab}} = lists:keysearch(id, 1, Res),
true = ets:delete(Tab),
undefined = ets:info(non_existing_table_xxyy),
undefined = ets:info(non_existing_table_xxyy,type),
@@ -5892,6 +5895,36 @@ take(Config) when is_list(Config) ->
ets:delete(T3),
ok.
+whereis_table(Config) when is_list(Config) ->
+ %% Do we return 'undefined' when the named table doesn't exist?
+ undefined = ets:whereis(whereis_test),
+
+ %% Does the tid() refer to the same table as the name?
+ whereis_test = ets:new(whereis_test, [named_table]),
+ Tid = ets:whereis(whereis_test),
+
+ ets:insert(whereis_test, [{hello}, {there}]),
+
+ [[{hello}],[{there}]] = ets:match(whereis_test, '$1'),
+ [[{hello}],[{there}]] = ets:match(Tid, '$1'),
+
+ true = ets:delete_all_objects(Tid),
+
+ [] = ets:match(whereis_test, '$1'),
+ [] = ets:match(Tid, '$1'),
+
+ %% Does the name disappear when deleted through the tid()?
+ true = ets:delete(Tid),
+ undefined = ets:info(whereis_test),
+ {'EXIT',{badarg, _}} = (catch ets:match(whereis_test, '$1')),
+
+ %% Is the old tid() broken when the table is re-created with the same
+ %% name?
+ whereis_test = ets:new(whereis_test, [named_table]),
+ [] = ets:match(whereis_test, '$1'),
+ {'EXIT',{badarg, _}} = (catch ets:match(Tid, '$1')),
+
+ ok.
%%
%% Utility functions:
diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl
index 15ae4fa2d7..d753d929f5 100644
--- a/lib/stdlib/test/rand_SUITE.erl
+++ b/lib/stdlib/test/rand_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -52,7 +52,9 @@ all() ->
[seed, interval_int, interval_float,
api_eq,
reference,
- {group, basic_stats}, uniform_real_conv,
+ {group, basic_stats},
+ {group, distr_stats},
+ uniform_real_conv,
plugin, measure,
{group, reference_jump}
].
@@ -60,8 +62,9 @@ all() ->
groups() ->
[{basic_stats, [parallel],
[basic_stats_uniform_1, basic_stats_uniform_2,
- basic_stats_standard_normal,
- stats_standard_normal_box_muller,
+ basic_stats_standard_normal]},
+ {distr_stats, [parallel],
+ [stats_standard_normal_box_muller,
stats_standard_normal_box_muller_2,
stats_standard_normal]},
{reference_jump, [parallel],
@@ -70,6 +73,9 @@ groups() ->
group(basic_stats) ->
%% valgrind needs a lot of time
[{timetrap,{minutes,10}}];
+group(distr_stats) ->
+ %% valgrind needs a lot of time
+ [{timetrap,{minutes,10}}];
group(reference_jump) ->
%% valgrind needs a lot of time
[{timetrap,{minutes,10}}].
@@ -437,7 +443,7 @@ stats_standard_normal_box_muller(Config) when is_list(Config) ->
{Z, [S]}
end,
State = [rand:seed(exrop)],
- stats_standard_normal(NormalS, State)
+ stats_standard_normal(NormalS, State, 3)
catch error:_ ->
{skip, "math:erfc/1 not supported"}
end.
@@ -462,7 +468,7 @@ stats_standard_normal_box_muller_2(Config) when is_list(Config) ->
{Z, [S]}
end,
State = [rand:seed(exrop)],
- stats_standard_normal(NormalS, State)
+ stats_standard_normal(NormalS, State, 3)
catch error:_ ->
{skip, "math:erfc/1 not supported"}
end.
@@ -472,21 +478,21 @@ stats_standard_normal(Config) when is_list(Config) ->
try math:erfc(1.0) of
_ ->
stats_standard_normal(
- fun rand:normal_s/1, rand:seed_s(exrop))
+ fun rand:normal_s/1, rand:seed_s(exrop), 3)
catch error:_ ->
{skip, "math:erfc/1 not supported"}
end.
%%
-stats_standard_normal(Fun, S) ->
+stats_standard_normal(Fun, S, Retries) ->
%%%
%%% ct config:
-%%% {rand_SUITE, [{stats_standard_normal,[{seconds, 8}, {std_devs, 4.2}]}]}.
+%%% {rand_SUITE, [{stats_standard_normal,[{seconds, 8}, {std_devs, 4.0}]}]}.
%%%
Seconds = ct:get_config({?MODULE, ?FUNCTION_NAME, seconds}, 8),
StdDevs =
ct:get_config(
{?MODULE, ?FUNCTION_NAME, std_devs},
- 4.2), % probability erfc(4.2/sqrt(2)) (1/37465) to fail a bucket
+ 4.0), % probability erfc(4.0/sqrt(2)) (1/15787) to fail a bucket
%%%
ct:timetrap({seconds, Seconds + 120}),
%% Buckets is chosen to get a range where the the probability to land
@@ -505,11 +511,11 @@ stats_standard_normal(Fun, S) ->
P0 = math:erf(1 / W),
Rounds = TargetHits * ceil(1.0 / P0),
Histogram = array:new({default, 0}),
- StopTime = erlang:monotonic_time(second) + Seconds,
ct:pal(
"Running standard normal test against ~w std devs for ~w seconds...",
[StdDevs, Seconds]),
- {PositiveHistogram, NegativeHistogram, Outlier, TotalRounds} =
+ StopTime = erlang:monotonic_time(second) + Seconds,
+ {PositiveHistogram, NegativeHistogram, Outlier, TotalRounds, NewS} =
stats_standard_normal(
InvDelta, Buckets, Histogram, Histogram, 0.0,
Fun, S, Rounds, StopTime, Rounds, 0),
@@ -522,16 +528,33 @@ stats_standard_normal(Fun, S) ->
"Total rounds: ~w, tolerance: 1/~.2f..1/~.2f, "
"outlier: ~.2f, probability 1/~.2f.",
[TotalRounds, Precision, TopPrecision, Outlier, InvOP]),
- {TotalRounds, [], []} =
- {TotalRounds,
+ case
+ {bucket_error, TotalRounds,
check_histogram(
W, TotalRounds, StdDevs, PositiveHistogram, Buckets),
check_histogram(
- W, TotalRounds, StdDevs, NegativeHistogram, Buckets)},
- %% If the probability for getting this Outlier is lower than 1/50,
- %% then this is fishy!
- true = (1/50 =< OutlierProbability),
- {comment, {tp, TopPrecision, op, InvOP}}.
+ W, TotalRounds, StdDevs, NegativeHistogram, Buckets)}
+ of
+ {_, _, [], []} when InvOP < 100 ->
+ {comment, {tp, TopPrecision, op, InvOP}};
+ {_, _, [], []} ->
+ %% If the probability for getting this Outlier is lower than
+ %% 1/100, then this is fishy!
+ stats_standard_normal(
+ Fun, NewS, Retries, {outlier_fishy, InvOP});
+ BucketErrors ->
+ stats_standard_normal(
+ Fun, NewS, Retries, BucketErrors)
+ end.
+%%
+stats_standard_normal(Fun, S, Retries, Failure) ->
+ case Retries - 1 of
+ 0 ->
+ ct:fail(Failure);
+ NewRetries ->
+ ct:pal("Retry due to TC glitch: ~p", [Failure]),
+ stats_standard_normal(Fun, S, NewRetries)
+ end.
%%
stats_standard_normal(
InvDelta, Buckets, PositiveHistogram, NegativeHistogram, Outlier,
@@ -544,7 +567,7 @@ stats_standard_normal(
Fun, S, Rounds, StopTime, Rounds, TotalRounds + Rounds);
_ ->
{PositiveHistogram, NegativeHistogram,
- Outlier, TotalRounds + Rounds}
+ Outlier, TotalRounds + Rounds, S}
end;
stats_standard_normal(
InvDelta, Buckets, PositiveHistogram, NegativeHistogram, Outlier,
@@ -571,9 +594,6 @@ increment_bucket(Bucket, Array) ->
array:set(Bucket, array:get(Bucket, Array) + 1, Array).
check_histogram(W, Rounds, StdDevs, Histogram, Buckets) ->
- %%PrevBucket = 512,
- %%Bucket = PrevBucket - 1,
- %%P = 0.5 * math:erfc(PrevBucket / W),
TargetP = 0.5 * math:erfc(Buckets / W),
P = 0.0,
N = 0,
@@ -592,7 +612,7 @@ check_histogram(
P = 0.5 * math:erfc(Bucket / W),
BucketP = P - PrevP,
if
- TargetP =< BucketP ->
+ BucketP < TargetP ->
check_histogram(
W, Rounds, StdDevs, Histogram, TargetP,
Bucket - 1, PrevBucket, PrevP, N);
@@ -604,7 +624,7 @@ check_histogram(
UpperLimit = ceil(Exp + Threshold),
if
N < LowerLimit; UpperLimit < N ->
- [#{bucket => {Bucket, PrevBucket}, n => N, exp => Exp,
+ [#{bucket => {Bucket, PrevBucket}, n => N,
lower => LowerLimit, upper => UpperLimit} |
check_histogram(
W, Rounds, StdDevs, Histogram, TargetP,
diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl
index 71f86e32e5..7b82647416 100644
--- a/lib/stdlib/test/re_SUITE.erl
+++ b/lib/stdlib/test/re_SUITE.erl
@@ -894,10 +894,13 @@ match_limit(Config) when is_list(Config) ->
%% Test that we get sub-binaries if subject is a binary and we capture
%% binaries.
sub_binaries(Config) when is_list(Config) ->
- Bin = list_to_binary(lists:seq(1,255)),
- {match,[B,C]}=re:run(Bin,"(a)",[{capture,all,binary}]),
- 255 = binary:referenced_byte_size(B),
- 255 = binary:referenced_byte_size(C),
- {match,[D]}=re:run(Bin,"(a)",[{capture,[1],binary}]),
- 255 = binary:referenced_byte_size(D),
+ %% The GC can auto-convert tiny sub-binaries to heap binaries, so we
+ %% extract large sequences to make the test more stable.
+ Bin = << <<I>> || I <- lists:seq(1, 4096) >>,
+ {match,[B,C]}=re:run(Bin,"a(.+)$",[{capture,all,binary}]),
+ true = byte_size(B) =/= byte_size(C),
+ 4096 = binary:referenced_byte_size(B),
+ 4096 = binary:referenced_byte_size(C),
+ {match,[D]}=re:run(Bin,"a(.+)$",[{capture,[1],binary}]),
+ 4096 = binary:referenced_byte_size(D),
ok.
diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl
index c4a469c251..fdff2d24b8 100644
--- a/lib/stdlib/test/string_SUITE.erl
+++ b/lib/stdlib/test/string_SUITE.erl
@@ -486,6 +486,10 @@ to_float(_) ->
prefix(_) ->
?TEST("", ["a"], nomatch),
?TEST("a", [""], "a"),
+ ?TEST("a", [[[]]], "a"),
+ ?TEST("a", [<<>>], "a"),
+ ?TEST("a", [[<<>>]], "a"),
+ ?TEST("a", [[[<<>>]]], "a"),
?TEST("b", ["a"], nomatch),
?TEST("a", ["a"], ""),
?TEST("å", ["a"], nomatch),
diff --git a/lib/stdlib/test/uri_string_SUITE.erl b/lib/stdlib/test/uri_string_SUITE.erl
index fef356355c..92f8bb3292 100644
--- a/lib/stdlib/test/uri_string_SUITE.erl
+++ b/lib/stdlib/test/uri_string_SUITE.erl
@@ -22,7 +22,7 @@
-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,groups/0,
- normalize/1,
+ normalize/1, normalize_map/1, normalize_return_map/1, normalize_negative/1,
parse_binary_fragment/1, parse_binary_host/1, parse_binary_host_ipv4/1,
parse_binary_host_ipv6/1,
parse_binary_path/1, parse_binary_pct_encoded_fragment/1, parse_binary_pct_encoded_query/1,
@@ -68,6 +68,9 @@ suite() ->
all() ->
[
normalize,
+ normalize_map,
+ normalize_return_map,
+ normalize_negative,
parse_binary_scheme,
parse_binary_userinfo,
parse_binary_pct_encoded_userinfo,
@@ -912,6 +915,56 @@ normalize(_Config) ->
<<"tftp://localhost">> =
uri_string:normalize(<<"tftp://localhost:69">>).
+normalize_map(_Config) ->
+ "/a/g" = uri_string:normalize(#{path => "/a/b/c/./../../g"}),
+ <<"mid/6">> = uri_string:normalize(#{path => <<"mid/content=5/../6">>}),
+ "http://localhost-%C3%B6rebro/a/g" =
+ uri_string:normalize(#{scheme => "http",port => 80,path => "/a/b/c/./../../g",
+ host => "localhost-örebro"}),
+ <<"http://localhost-%C3%B6rebro/a/g">> =
+ uri_string:normalize(#{scheme => <<"http">>,port => 80,
+ path => <<"/a/b/c/./../../g">>,
+ host => <<"localhost-örebro"/utf8>>}),
+ <<"https://localhost/">> =
+ uri_string:normalize(#{scheme => <<"https">>,port => 443,path => <<>>,
+ host => <<"localhost">>}),
+ <<"https://localhost:445/">> =
+ uri_string:normalize(#{scheme => <<"https">>,port => 445,path => <<>>,
+ host => <<"localhost">>}),
+ <<"ftp://localhost">> =
+ uri_string:normalize(#{scheme => <<"ftp">>,port => 21,path => <<>>,
+ host => <<"localhost">>}),
+ <<"ssh://localhost">> =
+ uri_string:normalize(#{scheme => <<"ssh">>,port => 22,path => <<>>,
+ host => <<"localhost">>}),
+ <<"sftp://localhost">> =
+ uri_string:normalize(#{scheme => <<"sftp">>,port => 22,path => <<>>,
+ host => <<"localhost">>}),
+ <<"tftp://localhost">> =
+ uri_string:normalize(#{scheme => <<"tftp">>,port => 69,path => <<>>,
+ host => <<"localhost">>}).
+
+normalize_return_map(_Config) ->
+ #{scheme := "http",path := "/a/g",host := "localhost-örebro"} =
+ uri_string:normalize("http://localhos%74-%c3%b6rebro:80/a/b/c/./../../g",
+ [return_map]),
+ #{scheme := <<"http">>,path := <<"/a/g">>, host := <<"localhost-örebro"/utf8>>} =
+ uri_string:normalize(<<"http://localhos%74-%c3%b6rebro:80/a/b/c/./../../g">>,
+ [return_map]),
+ #{scheme := <<"https">>,path := <<"/">>, host := <<"localhost">>} =
+ uri_string:normalize(#{scheme => <<"https">>,port => 443,path => <<>>,
+ host => <<"localhost">>}, [return_map]).
+
+normalize_negative(_Config) ->
+ {error,invalid_uri,":"} =
+ uri_string:normalize("http://local>host"),
+ {error,invalid_uri,":"} =
+ uri_string:normalize(<<"http://local>host">>),
+ {error,invalid_uri,":"} =
+ uri_string:normalize("http://[192.168.0.1]", [return_map]),
+ {error,invalid_uri,":"} =
+ uri_string:normalize(<<"http://[192.168.0.1]">>, [return_map]).
+
interop_query_utf8(_Config) ->
Q = uri_string:compose_query([{"foo bar","1"}, {"合", "2"}]),
Uri = uri_string:recompose(#{path => "/", query => Q}),
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index 35c93ba4ed..ea4d6cb723 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -54,8 +54,6 @@ EL_FILES = $(EMACS_FILES:%=%.el)
ELC_FILES = $(EMACS_FILES:%=%.elc)
-TEST_FILES = test.erl.indented test.erl.orig
-
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
@@ -75,7 +73,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/emacs"
- $(INSTALL_DATA) $(EL_FILES) $(README_FILES) $(TEST_FILES) \
+ $(INSTALL_DATA) $(EL_FILES) $(README_FILES) \
"$(RELSYSDIR)/emacs"
ifeq ($(DOCTYPE),pdf)
@@ -89,19 +87,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN_FILES) "$(RELEASE_PATH)/man/man3"
endif
endif
-
-EMACS ?= emacs
-
-test_indentation:
- @rm -f test.erl
- @rm -f test_indent.el
- @echo '(load "erlang-start")' >> test_indent.el
- @echo '(find-file "test.erl.orig")' >> test_indent.el
- @echo "(require 'cl) ; required with Emacs < 23 for ignore-errors" >> test_indent.el
- @echo '(erlang-mode)' >> test_indent.el
- @echo '(toggle-debug-on-error)' >> test_indent.el
- @echo '(erlang-indent-current-buffer)' >> test_indent.el
- @echo '(write-file "test.erl")' >> test_indent.el
- $(EMACS) --batch -Q -L . -l test_indent.el
- diff -u test.erl.indented test.erl
- @echo "No differences between expected and actual indentation"
diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index bdb3d9ad4a..534f50ab33 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -279,7 +279,8 @@ Please see the function `tempo-define-template'.")
'((erlang-skel-include erlang-skel-large-header)
"-behaviour(application)." n n
"%% Application callbacks" n
- "-export([start/2, stop/1])." n n
+ "-export([start/2, start_phase/3, stop/1, prep_stop/1," n>
+ "config_change/3])." n n
(erlang-skel-double-separator-start 3)
"%%% Application callbacks" n
(erlang-skel-double-separator-end 3) n
@@ -291,13 +292,14 @@ Please see the function `tempo-define-template'.")
"%% application. If the application is structured according to the OTP" n
"%% design principles as a supervision tree, this means starting the" n
"%% top supervisor of the tree." n
- "%%" n
- "%% @spec start(StartType, StartArgs) -> {ok, Pid} |" n
- "%% {ok, Pid, State} |" n
- "%% {error, Reason}" n
- "%% StartType = normal | {takeover, Node} | {failover, Node}" n
- "%% StartArgs = term()" n
(erlang-skel-separator-end 2)
+ "-spec start(StartType :: normal |" n>
+ "{takeover, Node :: node()} |" n>
+ "{failover, Node :: node()}," n>
+ "StartArgs :: term()) ->" n>
+ "{ok, Pid :: pid()} |" n>
+ "{ok, Pid :: pid(), State :: term()} |" n>
+ "{error, Reason :: term()}." n
"start(_StartType, _StartArgs) ->" n>
"case 'TopSupervisor':start_link() of" n>
"{ok, Pid} ->" n>
@@ -309,15 +311,52 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
+ "%% top supervisor of the tree." n
+ "%% Starts an application with included applications, when" n
+ "%% synchronization is needed between processes in the different" n
+ "%% applications during startup."
+ (erlang-skel-separator-end 2)
+ "-spec start_phase(Phase :: atom()," n>
+ "StartType :: normal |" n>
+ "{takeover, Node :: node()} |" n>
+ "{failover, Node :: node()}," n>
+ "PhaseArgs :: term()) -> ok | {error, Reason :: term()}." n
+ "start_phase(_Phase, _StartType, _PhaseArgs) ->" n>
+ "ok."n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
"%% This function is called whenever an application has stopped. It" n
"%% is intended to be the opposite of Module:start/2 and should do" n
"%% any necessary cleaning up. The return value is ignored." n
- "%%" n
- "%% @spec stop(State) -> void()" n
(erlang-skel-separator-end 2)
+ "-spec stop(State :: term()) -> any()." n
"stop(_State) ->" n>
"ok." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called when an application is about to be stopped," n
+ "%% before shutting down the processes of the application." n
+ (erlang-skel-separator-end 2)
+ "-spec prep_stop(State :: term()) -> NewState :: term()." n
+ "prep_stop(State) ->" n>
+ "State." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called by an application after a code replacement," n
+ "%% if the configuration parameters have changed." n
+ (erlang-skel-separator-end 2)
+ "-spec config_change(Changed :: [{Par :: atom(), Val :: term()}]," n>
+ "New :: [{Par :: atom(), Val :: term()}]," n>
+ "Removed :: [Par :: atom()]) -> ok." n
+ "config_change(_Changed, _New, _Removed) ->" n>
+ "ok." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
@@ -343,9 +382,12 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the supervisor" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, {already_started, Pid :: pid()}} |" n>
+ "{error, {shutdown, term()}} |" n>
+ "{error, term()} |" n>
+ "ignore." n
"start_link() ->" n>
"supervisor:start_link({local, ?SERVER}, ?MODULE, [])." n
n
@@ -359,11 +401,11 @@ Please see the function `tempo-define-template'.")
"%% this function is called by the new process to find out about" n
"%% restart strategy, maximum restart intensity, and child" n
"%% specifications." n
- "%%" n
- "%% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |" n
- "%% ignore |" n
- "%% {error, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) ->" n>
+ "{ok, {SupFlags :: supervisor:sup_flags()," n>
+ "[ChildSpec :: supervisor:child_spec()]}} |" n>
+ "ignore." n
"init([]) ->" n
"" n>
"SupFlags = #{strategy => one_for_one," n>
@@ -406,9 +448,11 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the supervisor bridge" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, {already_started, Pid :: pid()}} |" n>
+ "{error, term()} |" n>
+ "ignore." n
"start_link() ->" n>
"supervisor_bridge:start_link({local, ?SERVER}, ?MODULE, [])." n
n
@@ -422,11 +466,10 @@ Please see the function `tempo-define-template'.")
"%% which calls Module:init/1 to start the subsystem. To ensure a" n
"%% synchronized start-up procedure, this function does not return" n
"%% until Module:init/1 has returned." n
- "%%" n
- "%% @spec init(Args) -> {ok, Pid, State} |" n
- "%% ignore |" n
- "%% {error, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) -> {ok, Pid :: pid(), State :: term()} |" n>
+ "{error, Error :: term()} |" n>
+ "ignore." n
"init([]) ->" n>
"case 'AModule':start_link() of" n>
"{ok, Pid} ->" n>
@@ -442,10 +485,9 @@ Please see the function `tempo-define-template'.")
"%% to terminate. It should be the opposite of Module:init/1 and stop" n
"%% the subsystem and do any necessary cleaning up.The return value is" n
"%% ignored." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
- "terminate(Reason, State) ->" n>
+ "-spec terminate(Reason :: shutdown | term(), State :: term()) -> any()." n
+ "terminate(_Reason, _State) ->" n>
"'AModule':stop()," n>
"ok." n
n
@@ -464,9 +506,8 @@ Please see the function `tempo-define-template'.")
"-export([start_link/0])." n n
"%% gen_server callbacks" n
- "-export([init/1, handle_call/3, handle_cast/2, "
- "handle_info/2," n>
- "terminate/2, code_change/3])." n n
+ "-export([init/1, handle_call/3, handle_cast/2, handle_info/2," n>
+ "terminate/2, code_change/3, format_status/2])." n n
"-define(SERVER, ?MODULE)." n n
@@ -478,9 +519,11 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the server" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, Error :: {already_started, pid()}} |" n>
+ "{error, Error :: term()} |" n>
+ "ignore." n
"start_link() ->" n>
"gen_server:start_link({local, ?SERVER}, ?MODULE, [], [])." n
n
@@ -492,12 +535,12 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Initializes the server" n
- "%%" n
- "%% @spec init(Args) -> {ok, State} |" n
- "%% {ok, State, Timeout} |" n
- "%% ignore |" n
- "%% {stop, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) -> {ok, State :: term()} |" n>
+ "{ok, State :: term(), Timeout :: timeout()} |" n>
+ "{ok, State :: term(), hibernate} |" n>
+ "{stop, Reason :: term()} |" n>
+ "ignore." n
"init([]) ->" n>
"process_flag(trap_exit, true)," n>
"{ok, #state{}}." n
@@ -506,15 +549,16 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Handling call messages" n
- "%%" n
- "%% @spec handle_call(Request, From, State) ->" n
- "%% {reply, Reply, State} |" n
- "%% {reply, Reply, State, Timeout} |" n
- "%% {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, Reply, State} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
+ "-spec handle_call(Request :: term(), From :: {pid(), term()}, State :: term()) ->" n>
+ "{reply, Reply :: term(), NewState :: term()} |" n>
+ "{reply, Reply :: term(), NewState :: term(), Timeout :: timeout()} |" n>
+ "{reply, Reply :: term(), NewState :: term(), hibernate} |" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: term(), Reply :: term(), NewState :: term()} |" n>
+ "{stop, Reason :: term(), NewState :: term()}." n
"handle_call(_Request, _From, State) ->" n>
"Reply = ok," n>
"{reply, Reply, State}." n
@@ -523,23 +567,25 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Handling cast messages" n
- "%%" n
- "%% @spec handle_cast(Msg, State) -> {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
- "handle_cast(_Msg, State) ->" n>
+ "-spec handle_cast(Request :: term(), State :: term()) ->" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: term(), NewState :: term()}." n
+ "handle_cast(_Request, State) ->" n>
"{noreply, State}." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
"%% Handling all non call/cast messages" n
- "%%" n
- "%% @spec handle_info(Info, State) -> {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
+ "-spec handle_info(Info :: timeout() | term(), State :: term()) ->" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: normal | term(), NewState :: term()}." n
"handle_info(_Info, State) ->" n>
"{noreply, State}." n
n
@@ -550,9 +596,9 @@ Please see the function `tempo-define-template'.")
"%% terminate. It should be the opposite of Module:init/1 and do any" n
"%% necessary cleaning up. When it returns, the gen_server terminates" n
"%% with Reason. The return value is ignored." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
+ "-spec terminate(Reason :: normal | shutdown | {shutdown, term()} | term()," n>
+ "State :: term()) -> any()." n
"terminate(_Reason, _State) ->" n>
"ok." n
n
@@ -560,12 +606,26 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Convert process state when code is changed" n
- "%%" n
- "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n
(erlang-skel-separator-end 2)
+ "-spec code_change(OldVsn :: term() | {down, term()}," n>
+ "State :: term()," n>
+ "Extra :: term()) -> {ok, NewState :: term()} |" n>
+ "{error, Reason :: term()}." n
"code_change(_OldVsn, State, _Extra) ->" n>
"{ok, State}." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called for changing the form and appearance" n
+ "%% of gen_server status when it is returned from sys:get_status/1,2" n
+ "%% or when it appears in termination error logs." n
+ (erlang-skel-separator-end 2)
+ "-spec format_status(Opt :: normal | terminate," n>
+ "Status :: list()) -> Status :: term()." n
+ "format_status(_Opt, Status) ->" n>
+ "Status." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
@@ -581,8 +641,8 @@ Please see the function `tempo-define-template'.")
"-export([start_link/0, add_handler/0])." n n
"%% gen_event callbacks" n
- "-export([init/1, handle_event/2, handle_call/2, " n>
- "handle_info/2, terminate/2, code_change/3])." n n
+ "-export([init/1, handle_event/2, handle_call/2, handle_info/2," n>
+ "terminate/2, code_change/3, format_status/2])." n n
"-define(SERVER, ?MODULE)." n n
@@ -594,18 +654,17 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Creates an event manager" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, Error :: {already_started, pid()} | term()}." n
"start_link() ->" n>
"gen_event:start_link({local, ?SERVER})." n
n
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Adds an event handler" n
- "%%" n
- "%% @spec add_handler() -> ok | {'EXIT', Reason} | term()" n
(erlang-skel-separator-end 2)
+ "-spec add_handler() -> ok | {'EXIT', Reason :: term()} | term()." n
"add_handler() ->" n>
"gen_event:add_handler(?SERVER, ?MODULE, [])." n
n
@@ -617,9 +676,11 @@ Please see the function `tempo-define-template'.")
"%% @doc" n
"%% Whenever a new event handler is added to an event manager," n
"%% this function is called to initialize the event handler." n
- "%%" n
- "%% @spec init(Args) -> {ok, State}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term() | {Args :: term(), Term :: term()}) ->" n>
+ "{ok, State :: term()} |" n>
+ "{ok, State :: term(), hibernate} |" n>
+ "{error, Reason :: term()}." n
"init([]) ->" n>
"{ok, #state{}}." n
n
@@ -629,12 +690,13 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event manager receives an event sent using" n
"%% gen_event:notify/2 or gen_event:sync_notify/2, this function is" n
"%% called for each installed event handler to handle the event." n
- "%%" n
- "%% @spec handle_event(Event, State) ->" n
- "%% {ok, State} |" n
- "%% {swap_handler, Args1, State1, Mod2, Args2} |"n
- "%% remove_handler" n
(erlang-skel-separator-end 2)
+ "-spec handle_event(Event :: term(), State :: term()) ->" n>
+ "{ok, NewState :: term()} |" n>
+ "{ok, NewState :: term(), hibernate} |" n>
+ "remove_handler |" n>
+ "{swap_handler, Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()} , Args2 :: term()}." n>
"handle_event(_Event, State) ->" n>
"{ok, State}." n
n
@@ -644,12 +706,13 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event manager receives a request sent using" n
"%% gen_event:call/3,4, this function is called for the specified" n
"%% event handler to handle the request." n
- "%%" n
- "%% @spec handle_call(Request, State) ->" n
- "%% {ok, Reply, State} |" n
- "%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |" n
- "%% {remove_handler, Reply}" n
(erlang-skel-separator-end 2)
+ "-spec handle_call(Request :: term(), State :: term()) ->" n>
+ "{ok, Reply :: term(), NewState :: term()} |" n>
+ "{ok, Reply :: term(), NewState :: term(), hibernate} |" n>
+ "{remove_handler, Reply :: term()} |" n>
+ "{swap_handler, Reply :: term(), Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()}, Args2 :: term()}." n
"handle_call(_Request, State) ->" n>
"Reply = ok," n>
"{ok, Reply, State}." n
@@ -660,12 +723,13 @@ Please see the function `tempo-define-template'.")
"%% This function is called for each installed event handler when" n
"%% an event manager receives any other message than an event or a" n
"%% synchronous request (or a system message)." n
- "%%" n
- "%% @spec handle_info(Info, State) ->" n
- "%% {ok, State} |" n
- "%% {swap_handler, Args1, State1, Mod2, Args2} |" n
- "%% remove_handler" n
(erlang-skel-separator-end 2)
+ "-spec handle_info(Info :: term(), State :: term()) ->" n>
+ "{ok, NewState :: term()} |" n>
+ "{ok, NewState :: term(), hibernate} |" n>
+ "remove_handler |" n>
+ "{swap_handler, Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()}, Args2 :: term()}." n
"handle_info(_Info, State) ->" n>
"{ok, State}." n
n
@@ -675,22 +739,40 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event handler is deleted from an event manager, this" n
"%% function is called. It should be the opposite of Module:init/1 and" n
"%% do any necessary cleaning up." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
- "terminate(_Reason, _State) ->" n>
+ "-spec terminate(Arg :: {stop, Reason :: term()} |" n>
+ "stop |" n>
+ "remove_handler |" n>
+ "{error, {'EXIT', Reason :: term()}} |" n>
+ "{error, Term :: term()} |" n>
+ "term()," n>
+ "State :: term()) -> any()." n
+ "terminate(_Arg, _State) ->" n>
"ok." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
"%% Convert process state when code is changed" n
- "%%" n
- "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n
(erlang-skel-separator-end 2)
+ "-spec code_change(OldVsn :: term() | {down, term()}," n>
+ "State :: term()," n>
+ "Extra :: term()) -> {ok, NewState :: term()}." n
"code_change(_OldVsn, State, _Extra) ->" n>
"{ok, State}." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called for changing the form and appearance" n
+ "%% of gen_event status when it is returned from sys:get_status/1,2" n
+ "%% or when it appears in termination error logs." n
+ (erlang-skel-separator-end 2)
+ "-spec format_status(Opt :: normal | terminate," n>
+ "Status :: list()) -> Status :: term()." n
+ "format_status(_Opt, Status) ->" n>
+ "Status." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 6b93d63182..b3411c3ce7 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -4,7 +4,7 @@
;; Author: Anders Lindgren
;; Keywords: erlang, languages, processes
;; Date: 2011-12-11
-;; Version: 2.8.0
+;; Version: 2.8.1
;; Package-Requires: ((emacs "24.1"))
;; %CopyrightBegin%
@@ -84,7 +84,7 @@
"The Erlang programming language."
:group 'languages)
-(defconst erlang-version "2.8.0"
+(defconst erlang-version "2.8.1"
"The version number of Erlang mode.")
(defcustom erlang-root-dir nil
@@ -2742,7 +2742,7 @@ Return nil if inside string, t if in a comment."
(1+ (nth 2 stack-top)))
((= (char-syntax (following-char)) ?\))
(goto-char (nth 1 stack-top))
- (cond ((looking-at "[({]\\s *\\($\\|%\\)")
+ (cond ((erlang-record-or-function-args-p)
;; Line ends with parenthesis.
(let ((previous (erlang-indent-find-preceding-expr))
(stack-pos (nth 2 stack-top)))
@@ -2752,19 +2752,10 @@ Return nil if inside string, t if in a comment."
(nth 2 stack-top))))
((= (following-char) ?,)
;; a comma at the start of the line: line up with opening parenthesis.
- (nth 2 stack-top))
+ (min (nth 2 stack-top)
+ (erlang-indent-element stack-top indent-point token)))
(t
- (goto-char (nth 1 stack-top))
- (let ((base (cond ((looking-at "[({]\\s *\\($\\|%\\)")
- ;; Line ends with parenthesis.
- (erlang-indent-parenthesis (nth 2 stack-top)))
- (t
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (1+ (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column)))))
- (erlang-indent-standard indent-point token base 't)))))
+ (erlang-indent-element stack-top indent-point token))))
;;
((eq (car stack-top) '<<)
;; Element of binary (possible comprehension) expression,
@@ -2773,13 +2764,11 @@ Return nil if inside string, t if in a comment."
(+ 2 (nth 2 stack-top)))
((looking-at "\\(>>\\)[^_a-zA-Z0-9]")
(nth 2 stack-top))
+ ((= (following-char) ?,)
+ (min (+ (nth 2 stack-top) 1)
+ (- (erlang-indent-to-first-element stack-top 2) 1)))
(t
- (goto-char (nth 1 stack-top))
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (+ 2 (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column))))
+ (erlang-indent-to-first-element stack-top 2))))
((memq (car stack-top) '(icr fun spec))
;; The default indentation is the column of the option
@@ -2835,12 +2824,13 @@ Return nil if inside string, t if in a comment."
(let ((base (erlang-indent-find-base stack indent-point off skip)))
;; Special cases
(goto-char indent-point)
- (cond ((looking-at "\\(end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
+ (cond ((looking-at "\\(;\\|end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
(if (eq (car stack-top) '->)
(erlang-pop stack))
- (if stack
- (erlang-caddr (car stack))
- 0))
+ (cond ((and stack (looking-at ";"))
+ (+ (erlang-caddr (car stack)) (- erlang-indent-level 2)))
+ (stack (erlang-caddr (car stack)))
+ (t off)))
((looking-at "catch\\b\\($\\|[^_a-zA-Z0-9]\\)")
;; Are we in a try
(let ((start (if (eq (car stack-top) '->)
@@ -2914,6 +2904,22 @@ Return nil if inside string, t if in a comment."
(current-column))) start-alternativ))))))
)))
+(defun erlang-indent-to-first-element (stack-top extra)
+ ;; Indent to the same column as the first
+ ;; argument. extra should be 1 for lists tuples or 2 for binaries
+ (goto-char (+ (nth 1 stack-top) extra))
+ (skip-chars-forward " \t")
+ (current-column))
+
+(defun erlang-indent-element (stack-top indent-point token)
+ (goto-char (nth 1 stack-top))
+ (let ((base (cond ((erlang-record-or-function-args-p)
+ ;; Line ends with parenthesis.
+ (erlang-indent-parenthesis (nth 2 stack-top)))
+ (t
+ (erlang-indent-to-first-element stack-top 1)))))
+ (erlang-indent-standard indent-point token base 't)))
+
(defun erlang-indent-standard (indent-point token base inside-parenthesis)
"Standard indent when in blocks or tuple or arguments.
Look at last thing to see in what state we are, move relative to the base."
@@ -2939,6 +2945,9 @@ Return nil if inside string, t if in a comment."
;; Avoid treating comments a continued line.
((= (following-char) ?%)
base)
+ ((and (= (following-char) ?,) inside-parenthesis)
+ ;; a comma at the start of the line line up with parenthesis
+ (- base 1))
;; Continued line (e.g. line beginning
;; with an operator.)
(t
@@ -3028,11 +3037,21 @@ This assumes that the preceding expression is either simple
(t col)))
col))))
+(defun erlang-record-or-function-args-p ()
+ (and (looking-at "[({]\\s *\\($\\|%\\)")
+ (or (eq (following-char) ?\( )
+ (save-excursion
+ (ignore-errors (forward-sexp (- 1)))
+ (eq (preceding-char) ?#)))))
+
(defun erlang-indent-parenthesis (stack-position)
(let ((previous (erlang-indent-find-preceding-expr)))
- (if (> previous stack-position)
- (+ stack-position erlang-argument-indent)
- (+ previous erlang-argument-indent))))
+ (cond ((eq previous stack-position) ;; tuple or map not a record
+ (1+ stack-position))
+ ((> previous stack-position)
+ (+ stack-position erlang-argument-indent))
+ (t
+ (+ previous erlang-argument-indent)))))
(defun erlang-skip-blank (&optional lim)
"Skip over whitespace and comments until limit reached."
@@ -5166,7 +5185,6 @@ future, a new shell on an already running host will be started."
;; e.g. it does not assume that we are running an inferior
;; Erlang, there exists a lot of other possibilities.
-
(defvar erlang-shell-buffer-name "*erlang*"
"The name of the Erlang link shell buffer.")
@@ -5177,46 +5195,28 @@ Also see the description of `ielm-prompt-read-only'."
:type 'boolean
:package-version '(erlang . "2.8.0"))
-(defvar erlang-shell-mode-map nil
- "Keymap used by Erlang shells.")
-
-
-(defvar erlang-shell-mode-hook nil
- "User functions to run when an Erlang shell is started.
-
-This hook is used to change the behaviour of Erlang mode. It is
-normally used by the user to personalise the programming environment.
-When used in a site init file, it could be used to customise Erlang
-mode for all users on the system.
-
-The function added to this hook is run every time a new Erlang
-shell is started.
+(defvar erlang-shell-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "\M-\t" 'erlang-complete-tag)
-See also `erlang-load-hook', a hook which is run once, when Erlang
-mode is loaded, and `erlang-mode-hook' which is run every time a new
-Erlang source file is loaded into Emacs.")
+ ;; Normally the other way around.
+ (define-key map "\C-a" 'comint-bol)
+ (define-key map "\C-c\C-a" 'beginning-of-line)
+ (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
+ (define-key map "\M-\C-m" 'compile-goto-error)
+ map)
+ "Keymap used by Erlang shells.")
(defvar erlang-input-ring-file-name "~/.erlang_history"
"When non-nil, file name used to store Erlang shell history information.")
-
-(defun erlang-shell-mode ()
+(define-derived-mode erlang-shell-mode comint-mode "Erlang Shell"
"Major mode for interacting with an Erlang shell.
-We assume that we already are in Comint mode.
-
The following special commands are available:
\\{erlang-shell-mode-map}"
- (interactive)
- (setq major-mode 'erlang-shell-mode)
- (setq mode-name "Erlang Shell")
(erlang-mode-variables)
- (if erlang-shell-mode-map
- nil
- (setq erlang-shell-mode-map (copy-keymap comint-mode-map))
- (erlang-shell-mode-commands erlang-shell-mode-map))
- (use-local-map erlang-shell-mode-map)
;; Needed when compiling directly from the Erlang shell.
(setq compilation-last-buffer (current-buffer))
(setq comint-prompt-regexp "^[^>=]*> *")
@@ -5230,7 +5230,6 @@ The following special commands are available:
'inferior-erlang-strip-delete nil t)
(add-hook 'comint-output-filter-functions
'inferior-erlang-strip-ctrl-m nil t)
-
(setq comint-input-ring-file-name erlang-input-ring-file-name)
(comint-read-input-ring t)
(make-local-variable 'kill-buffer-hook)
@@ -5249,8 +5248,7 @@ The following special commands are available:
(define-key map [menu-bar compilation]
(cons "Errors" compilation-menu-map)))
map))))
- (erlang-tags-init)
- (run-hooks 'erlang-shell-mode-hook))
+ (erlang-tags-init))
(defun erlang-mouse-2-command (event)
@@ -5272,13 +5270,6 @@ Selects Comint or Compilation mode command as appropriate."
(call-interactively (lookup-key compilation-mode-map "\C-m"))
(call-interactively (lookup-key comint-mode-map "\C-m"))))
-(defun erlang-shell-mode-commands (map)
- (define-key map "\M-\t" 'erlang-complete-tag)
- (define-key map "\C-a" 'comint-bol) ; Normally the other way around.
- (define-key map "\C-c\C-a" 'beginning-of-line)
- (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
- (define-key map "\M-\C-m" 'compile-goto-error))
-
;;;
;;; Inferior Erlang -- Run an Erlang shell as a subprocess.
;;;
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
deleted file mode 100644
index 14a4eca7c3..0000000000
--- a/lib/tools/emacs/test.erl.indented
+++ /dev/null
@@ -1,784 +0,0 @@
-%% -*- Mode: erlang; indent-tabs-mode: nil -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-
-%%%-------------------------------------------------------------------
-%%% File : test.erl
-%%% Author : Dan Gudmundsson <[email protected]>
-%%% Description : Test emacs mode indention and font-locking
-%%% this file is intentionally not indented.
-%%% Copy the file and indent it and you should end up with test.erl.indented
-%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
-%%%-------------------------------------------------------------------
-
-%% Start off with syntax highlighting you have to verify this by looking here
-%% and see that the code looks alright
-
--module(test).
--compile(export_all).
-
-%% Used to cause an "Unbalanced parentheses" error.
-foo(M) ->
- M#{a :=<<"a">>
- ,b:=1}.
-foo() ->
- #{a =><<"a">>
- ,b=>1}.
-
-%% Module attributes should be highlighted
-
--export([t/1]).
--record(record1, {a,
- b,
- c
- }).
--record(record2, {
- a,
- b
- }).
-
--record(record3, {a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record4, {
- a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record5, { a = 1 :: integer()
- , b = foobar :: atom()
- }).
-
--define(MACRO_1, macro).
--define(MACRO_2(_), macro).
-
--spec t(integer()) -> any().
-
--type ann() :: Var :: integer().
--type ann2() :: Var ::
- 'return'
- | 'return_white_spaces'
- | 'return_comments'
- | 'text' | ann().
--type paren() ::
- (ann2()).
--type t1() :: atom().
--type t2() :: [t1()].
--type t3(Atom) :: integer(Atom).
--type t4() :: t3(foobar).
--type t5() :: {t1(), t3(foo)}.
--type t6() :: 1 | 2 | 3 |
- 'foo' | 'bar'.
--type t7() :: [].
--type t71() :: [_].
--type t8() :: {any(),none(),pid(),port(),
- reference(),float()}.
--type t9() :: [1|2|3|foo|bar] |
- list(a | b | c) | t71().
--type t10() :: {1|2|3|foo|t9()} | {}.
--type t11() :: 1..2.
--type t13() :: maybe_improper_list(integer(), t11()).
--type t14() :: [erl_scan:foo() |
- %% Should be highlighted
- term() |
- bool() |
- byte() |
- char() |
- non_neg_integer() | nonempty_list() |
- pos_integer() |
- neg_integer() |
- number() |
- list() |
- nonempty_improper_list() | nonempty_maybe_improper_list() |
- maybe_improper_list() | string() | iolist() | byte() |
- module() |
- mfa() |
- node() |
- timeout() |
- no_return() |
- %% Should not be highlighted
- nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
-
-
--type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
- <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
- <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
- <<_:34>>|<<_:34>>|<<_:34>>].
--type t16() :: fun().
--type t17() :: fun((...) -> paren()).
--type t18() :: fun(() -> t17() | t16()).
--type t19() :: fun((t18()) -> t16()) |
- fun((nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
- nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()).
--type t20() :: [t19(), ...].
--type t21() :: tuple().
--type t21(A) :: A.
--type t22() :: t21(integer()).
--type t23() :: #rec1{}.
--type t24() :: #rec2{a :: t23(), b :: [atom()]}.
--type t25() :: #rec3{f123 :: [t24() |
- 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())]}.
--type t26() :: #rec4{ a :: integer()
- , b :: any()
- }.
--type t27() :: { integer()
- , atom()
- }.
--type t99() ::
- {t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
- t15(),t20(),t21(), t22(),t25()}.
--spec t1(FooBar :: t99()) -> t99();
- (t2()) -> t2();
- (t4()) -> t4() when is_subtype(t4(), t24);
- (t23()) -> t23() when is_subtype(t23(), atom()),
- is_subtype(t23(), t14());
- (t24()) -> t24() when is_subtype(t24(), atom()),
- is_subtype(t24(), t14()),
- is_subtype(t24(), t4()).
-
--spec over(I :: integer()) -> R1 :: foo:typen();
- (A :: atom()) -> R2 :: foo:atomen();
- (T :: tuple()) -> R3 :: bar:typen().
-
--spec mod:t2() -> any().
-
--spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec handle_cast(Cast ::
- {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec all(fun((T) -> boolean()), List :: [T]) ->
- boolean() when is_subtype(T, term()). % (*)
-
--spec get_closest_pid(term()) ->
- Return :: pid()
- | {'error', {'no_process', term()}
- | {'no_such_group', term()}}.
-
--spec add( X :: integer()
- , Y :: integer()
- ) -> integer().
-
--opaque attributes_data() ::
- [{'column', column()} | {'line', info_line()} |
- {'text', string()}] | {line(),column()}.
--record(r,{
- f1 :: attributes_data(),
- f222 = foo:bar(34, #rec3{}, 234234234423,
- aassdsfsdfsdf, 2234242323) ::
- [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
- f333 :: [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
- f3 = x:y(),
- f4 = x:z() :: t99(),
- f17 :: 'undefined',
- f18 :: 1 | 2 | 'undefined',
- f19 = 3 :: integer()|undefined,
- f5 = 3 :: undefined|integer()}).
-
--record(state, {
- sequence_number = 1 :: integer()
- }).
-
-
-highlighting(X) % Function definitions should be highlighted
- when is_integer(X) -> % and so should `when' and `is_integer' be
- %% Highlighting
- %% Various characters (we keep an `atom' after to see that highlighting ends)
- $a,atom, % Characters should be marked
- "string",atom, % and strings
- 'asdasd',atom, % quote should be atoms??
- 'VaV',atom,
- 'aVa',atom,
- '\'atom',atom,
- 'atom\'',atom,
- 'at\'om',atom,
- '#1',atom,
-
- $", atom, % atom should be ok
- $', atom,
-
- "string$", atom, "string$", atom, % currently buggy I know...
- "string\$", atom, % workaround for bug above
-
- "char $in string", atom,
-
- 'atom$', atom, 'atom$', atom,
- 'atom\$', atom,
-
- 'char $in atom', atom,
-
- $[, ${, $\\, atom,
- ?MACRO_1,
- ?MACRO_2(foo),
-
- %% Numerical constants
- 16#DD, % AD Should not be highlighted
- 32#dd, % AD Should not be highlighted
- 32#ddAB, % AD Should not be highlighted
- 32#101, % AD Should not be highlighted
- 32#ABTR, % AD Should not be highlighted
-
- %% Variables
- Variables = lists:foo(),
- _Variables = lists:foo(), % AD
- AppSpec = Xyz/2,
- Module42 = Xyz(foo, bar),
- Module:foo(),
- _Module:foo(), % AD
- FooÅÅ = lists:reverse([tl,hd,tl,hd]), % AD Should highlight FooÅÅ
- _FooÅÅ = 42, % AD Should highlight _FooÅÅ
-
- %% Bifs
- erlang:registered(),
- registered(),
- hd(tl(tl(hd([a,b,c])))),
- erlang:anything(lists),
- %% Guards
- is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
- is_function(Fun), is_pid(self()),
- not_a_guard:is_list([]),
- %% Other Types
-
- atom, % not (currently) hightlighted
- 234234,
- 234.43,
-
- [list, are, not, higlighted],
- {nor, is, tuple},
- ok.
-
-%%%
-%%% Indentation
-%%%
-
-%%% Left
-
-%% Indented
-
- % Right
-
-
-indent_basics(X, Y, Z)
- when X > 42,
- Z < 13;
- Y =:= 4711 ->
- %% comments
- % right comments
- case lists:filter(fun(_, AlongName,
- B,
- C) ->
- true
- end,
- [a,v,b])
- of
- [] ->
- Y = 5 * 43,
- ok;
- [_|_] ->
- Y = 5 * 43,
- ok
- end,
- Y,
- %% List, tuples and binaries
- [a,
- b, c
- ],
- [ a,
- b, c
- ],
-
- [
- a,
- b
- ],
- {a,
- b,c
- },
- { a,
- b,c
- },
-
- {
- a,
- b
- },
-
- <<1:8,
- 2:8
- >>,
- <<
- 1:8,
- 2:8
- >>,
- << 1:8,
- 2:8
- >>,
-
- (a,
- b,
- c
- ),
-
- ( a,
- b,
- c
- ),
-
-
- (
- a,
- b,
- c
- ),
-
- call(2#42423 bor
- #4234,
- 2#5432,
- other_arg),
- ok;
-indent_basics(Xlongname,
- #struct{a=Foo,
- b=Bar},
- [X|
- Y]) ->
- testing_next_clause,
- ok;
-indent_basics( % AD added clause
- X, % not sure how this should look
- Y,
- Z)
- when
- X < 42, Z > 13;
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) when % AD added clause
- X < 42, Z > 13; % testing when indentation
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) % AD added clause
- when % testing when indentation
- X < 42, Z > 13; % unsure about this one
- Y =:= 4711 ->
- foo.
-
-
-
-indent_nested() ->
- [
- {foo, 2, "string"},
- {bar, 3, "another string"}
- ].
-
-
-indent_icr(Z) -> % icr = if case receive
- %% If
- if Z >= 0 ->
- X = 43 div 4,
- foo(X);
- Z =< 10 ->
- X = 43 div 4,
- foo(X);
- Z == 5 orelse
- Z == 7 ->
- X = 43 div 4,
- foo(X);
- true ->
- if_works
- end,
- %% Case
- case {Z, foo, bar} of
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- {Z,_,_} when
- Z =:= 42 -> % AD line should be indented as a when
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when Z < 10 -> % AD when should be indented
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when % AD when should be indented
- Z < 10 % and the guards should follow when
- andalso % unsure about how though
- true ->
- X = 43 div 4,
- foo(X)
- end,
- %% begin
- begin
- sune,
- X = 74234 + foo(8456) +
- 345 div 43,
- ok
- end,
-
-
- %% receive
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- end,
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z % AD added clause
- when Z =:= 1 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z when % AD added clause
- Z =:= 2 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- after infinity ->
- foo(X),
- asd(X),
- 5*43
- end,
- receive
- after 10 ->
- foo(X),
- asd(X),
- 5*43
- end,
- ok.
-
-indent_fun() ->
- %% Changed fun to one indention level
- Var = spawn(fun(X)
- when X == 2;
- X > 10 ->
- hello,
- case Hello() of
- true when is_atom(X) ->
- foo;
- false ->
- bar
- end;
- (Foo) when is_atom(Foo),
- is_integer(X) ->
- X = 6* 45,
- Y = true andalso
- kalle
- end),
- %% check EEP37 named funs
- Fn1 = fun Fact(N) when N > 0 ->
- F = Fact(N-1),
- N * F;
- Fact(0) ->
- 1
- end,
- %% check anonymous funs too
- Fn2 = fun(0) ->
- 1;
- (N) ->
- N
- end,
- ok.
-
-indent_try_catch() ->
- try
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R % AD added clause
- when R =:= 42 -> % when should be indented
- foo(R);
- error:R % AD added clause
- when % when should be indented
- R =:= 42 -> % but unsure about this (maybe 2 more)
- foo(R);
- error:R when % AD added clause
- R =:= foo -> % line should be 2 indented (works)
- foo(R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile)
- end;
-indent_try_catch() ->
- try
- foo(bar)
- of
- X when true andalso
- kalle ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2);
- X % AD added clause
- when false andalso % when should be 2 indented
- bengt ->
- gurka();
- X when % AD added clause
- false andalso % line should be 2 indented
- not bengt ->
- gurka();
- X ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile),
- bar(with_long_arg,
- with_second_arg)
- end;
-indent_try_catch() ->
- try foo()
- after
- foo(),
- bar(with_long_arg,
- with_second_arg)
- end.
-
-indent_catch() ->
- D = B +
- float(43.1),
-
- B = catch oskar(X),
-
- A = catch (baz +
- bax),
- catch foo(),
-
- C = catch B +
- float(43.1),
-
- case catch foo(X) of
- A ->
- B
- end,
-
- case
- catch foo(X)
- of
- A ->
- B
- end,
-
- case
- foo(X)
- of
- A ->
- catch B,
- X
- end,
-
- try sune of
- _ -> foo
- catch _:_ -> baf
- end,
-
- try
- sune
- of
- _ ->
- X = 5,
- (catch foo(X)),
- X + 10
- catch _:_ -> baf
- end,
-
- try
- (catch sune)
- of
- _ ->
- catch foo() %% BUGBUG can't handle catch inside try without parentheses
- catch _:_ ->
- baf
- end,
-
- try
- (catch exit())
- catch
- _ ->
- catch baf()
- end,
- ok.
-
-indent_binary() ->
- X = lists:foldr(fun(M) ->
- <<Ma/binary, " ">>
- end, [], A),
- A = <<X/binary, 0:8>>,
- B.
-
-
-indent_comprehensions() ->
- %% I don't have a good idea how we want to handle this
- %% but they are here to show how they are indented today.
- Result1 = [X ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- ],
- Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- ],
-
- Binary1 = << <<X:8>> ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- >>,
-
- Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- >>,
- ok.
-
-%% This causes an error in earlier erlang-mode versions.
-foo() ->
- [#foo{
- foo = foo}].
-
-%% Record indentation
-some_function_with_a_very_long_name() ->
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b},
- case dummy_function_with_a_very_very_long_name(x) of
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- ok;
- Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b};
- #xyz{
- a=1,
- b=2} ->
- ok
- end.
-
-another_function_with_a_very_very_long_name() ->
- #rec{
- field1=1,
- field2=1}.
-
-some_function_name_xyz(xyzzy, #some_record{
- field1=Field1,
- field2=Field2}) ->
- SomeVariable = f(#'Some-long-record-name'{
- field_a = 1,
- 'inter-xyz-parameters' =
- #'Some-other-very-long-record-name'{
- field2 = Field1,
- field2 = Field2}}),
- {ok, SomeVariable}.
-
-commas_first() ->
- {abc, [ {some_var, 1}
- , {some_other_var, 2}
- , {erlang_ftw, 9}
- , {erlang_cookie, 'cookie'}
- , {cmds,
- [ {one, "sudo ls"}
- , {one, "sudo ls"}
- , {two, "sudo ls"}
- , {three, "sudo ls"}
- , {four, "sudo ls"}
- , {three, "sudo ls"}
- ] }
- , {ssh_username, "yow"}
- , {cluster,
- [ {aaaa, [ {"10.198.55.12" , "" }
- , {"10.198.55.13" , "" }
- ] }
- , {bbbb, [ {"10.198.55.151", "" }
- , {"10.198.55.123", "" }
- , {"10.198.55.34" , "" }
- , {"10.198.55.85" , "" }
- , {"10.198.55.67" , "" }
- ] }
- , {cccc, [ {"10.198.55.68" , "" }
- , {"10.198.55.69" , "" }
- ] }
- ] }
- ]
- }.
-
-
-%% this used to result in a scan-sexp error
-[{
- }].
-
-%% this used to result in 2x the correct indentation within the function
-%% body, due to the function name being mistaken for a keyword
-catcher(N) ->
- try generate_exception(N) of
- Val -> {N, normal, Val}
- catch
- throw:X -> {N, caught, thrown, X};
- exit:X -> {N, caught, exited, X};
- error:X -> {N, caught, error, X}
- end.
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
deleted file mode 100644
index c0cf1749b6..0000000000
--- a/lib/tools/emacs/test.erl.orig
+++ /dev/null
@@ -1,784 +0,0 @@
-%% -*- Mode: erlang; indent-tabs-mode: nil -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-
-%%%-------------------------------------------------------------------
-%%% File : test.erl
-%%% Author : Dan Gudmundsson <[email protected]>
-%%% Description : Test emacs mode indention and font-locking
-%%% this file is intentionally not indented.
-%%% Copy the file and indent it and you should end up with test.erl.indented
-%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
-%%%-------------------------------------------------------------------
-
-%% Start off with syntax highlighting you have to verify this by looking here
-%% and see that the code looks alright
-
--module(test).
--compile(export_all).
-
-%% Used to cause an "Unbalanced parentheses" error.
-foo(M) ->
-M#{a :=<<"a">>
-,b:=1}.
-foo() ->
-#{a =><<"a">>
-,b=>1}.
-
-%% Module attributes should be highlighted
-
--export([t/1]).
--record(record1, {a,
- b,
- c
-}).
--record(record2, {
- a,
- b
- }).
-
--record(record3, {a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
-234,
- d}).
-
--record(record4, {
- a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record5, { a = 1 :: integer()
-, b = foobar :: atom()
-}).
-
--define(MACRO_1, macro).
--define(MACRO_2(_), macro).
-
--spec t(integer()) -> any().
-
--type ann() :: Var :: integer().
--type ann2() :: Var ::
- 'return'
- | 'return_white_spaces'
- | 'return_comments'
- | 'text' | ann().
--type paren() ::
- (ann2()).
--type t1() :: atom().
--type t2() :: [t1()].
--type t3(Atom) :: integer(Atom).
--type t4() :: t3(foobar).
--type t5() :: {t1(), t3(foo)}.
--type t6() :: 1 | 2 | 3 |
- 'foo' | 'bar'.
--type t7() :: [].
--type t71() :: [_].
--type t8() :: {any(),none(),pid(),port(),
- reference(),float()}.
--type t9() :: [1|2|3|foo|bar] |
- list(a | b | c) | t71().
--type t10() :: {1|2|3|foo|t9()} | {}.
--type t11() :: 1..2.
--type t13() :: maybe_improper_list(integer(), t11()).
--type t14() :: [erl_scan:foo() |
- %% Should be highlighted
- term() |
- bool() |
- byte() |
- char() |
- non_neg_integer() | nonempty_list() |
- pos_integer() |
- neg_integer() |
- number() |
- list() |
- nonempty_improper_list() | nonempty_maybe_improper_list() |
- maybe_improper_list() | string() | iolist() | byte() |
- module() |
- mfa() |
- node() |
- timeout() |
- no_return() |
- %% Should not be highlighted
- nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
-
-
--type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
- <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
-<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
-<<_:34>>|<<_:34>>|<<_:34>>].
--type t16() :: fun().
--type t17() :: fun((...) -> paren()).
--type t18() :: fun(() -> t17() | t16()).
--type t19() :: fun((t18()) -> t16()) |
- fun((nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
-nonempty_maybe_improper_list('integer', any())|
-1|2|3|a|b|<<_:3,_:_*14>>|integer()).
--type t20() :: [t19(), ...].
--type t21() :: tuple().
--type t21(A) :: A.
--type t22() :: t21(integer()).
--type t23() :: #rec1{}.
--type t24() :: #rec2{a :: t23(), b :: [atom()]}.
--type t25() :: #rec3{f123 :: [t24() |
-1|2|3|4|a|b|c|d|
-nonempty_maybe_improper_list(integer, any())]}.
--type t26() :: #rec4{ a :: integer()
-, b :: any()
-}.
--type t27() :: { integer()
-, atom()
-}.
--type t99() ::
-{t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
-t15(),t20(),t21(), t22(),t25()}.
--spec t1(FooBar :: t99()) -> t99();
-(t2()) -> t2();
- (t4()) -> t4() when is_subtype(t4(), t24);
-(t23()) -> t23() when is_subtype(t23(), atom()),
- is_subtype(t23(), t14());
-(t24()) -> t24() when is_subtype(t24(), atom()),
- is_subtype(t24(), t14()),
- is_subtype(t24(), t4()).
-
--spec over(I :: integer()) -> R1 :: foo:typen();
- (A :: atom()) -> R2 :: foo:atomen();
- (T :: tuple()) -> R3 :: bar:typen().
-
--spec mod:t2() -> any().
-
--spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec handle_cast(Cast ::
- {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec all(fun((T) -> boolean()), List :: [T]) ->
- boolean() when is_subtype(T, term()). % (*)
-
--spec get_closest_pid(term()) ->
- Return :: pid()
- | {'error', {'no_process', term()}
- | {'no_such_group', term()}}.
-
--spec add( X :: integer()
-, Y :: integer()
-) -> integer().
-
--opaque attributes_data() ::
-[{'column', column()} | {'line', info_line()} |
- {'text', string()}] | {line(),column()}.
--record(r,{
- f1 :: attributes_data(),
-f222 = foo:bar(34, #rec3{}, 234234234423,
- aassdsfsdfsdf, 2234242323) ::
-[t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
-f333 :: [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
-f3 = x:y(),
-f4 = x:z() :: t99(),
-f17 :: 'undefined',
-f18 :: 1 | 2 | 'undefined',
-f19 = 3 :: integer()|undefined,
-f5 = 3 :: undefined|integer()}).
-
--record(state, {
- sequence_number = 1 :: integer()
- }).
-
-
-highlighting(X) % Function definitions should be highlighted
- when is_integer(X) -> % and so should `when' and `is_integer' be
- %% Highlighting
- %% Various characters (we keep an `atom' after to see that highlighting ends)
- $a,atom, % Characters should be marked
- "string",atom, % and strings
- 'asdasd',atom, % quote should be atoms??
- 'VaV',atom,
- 'aVa',atom,
- '\'atom',atom,
- 'atom\'',atom,
- 'at\'om',atom,
- '#1',atom,
-
- $", atom, % atom should be ok
- $', atom,
-
- "string$", atom, "string$", atom, % currently buggy I know...
- "string\$", atom, % workaround for bug above
-
- "char $in string", atom,
-
- 'atom$', atom, 'atom$', atom,
- 'atom\$', atom,
-
- 'char $in atom', atom,
-
- $[, ${, $\\, atom,
- ?MACRO_1,
- ?MACRO_2(foo),
-
- %% Numerical constants
- 16#DD, % AD Should not be highlighted
- 32#dd, % AD Should not be highlighted
- 32#ddAB, % AD Should not be highlighted
- 32#101, % AD Should not be highlighted
- 32#ABTR, % AD Should not be highlighted
-
- %% Variables
- Variables = lists:foo(),
- _Variables = lists:foo(), % AD
- AppSpec = Xyz/2,
- Module42 = Xyz(foo, bar),
- Module:foo(),
- _Module:foo(), % AD
- FooÅÅ = lists:reverse([tl,hd,tl,hd]), % AD Should highlight FooÅÅ
- _FooÅÅ = 42, % AD Should highlight _FooÅÅ
-
- %% Bifs
- erlang:registered(),
- registered(),
- hd(tl(tl(hd([a,b,c])))),
- erlang:anything(lists),
- %% Guards
- is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
- is_function(Fun), is_pid(self()),
- not_a_guard:is_list([]),
- %% Other Types
-
- atom, % not (currently) hightlighted
- 234234,
- 234.43,
-
- [list, are, not, higlighted],
- {nor, is, tuple},
- ok.
-
-%%%
-%%% Indentation
-%%%
-
-%%% Left
-
-%% Indented
-
-% Right
-
-
-indent_basics(X, Y, Z)
- when X > 42,
-Z < 13;
-Y =:= 4711 ->
- %% comments
- % right comments
- case lists:filter(fun(_, AlongName,
- B,
- C) ->
- true
- end,
- [a,v,b])
- of
- [] ->
- Y = 5 * 43,
- ok;
- [_|_] ->
- Y = 5 * 43,
- ok
- end,
- Y,
- %% List, tuples and binaries
- [a,
- b, c
- ],
- [ a,
- b, c
- ],
-
- [
- a,
- b
-],
- {a,
- b,c
- },
- { a,
- b,c
- },
-
- {
- a,
- b
- },
-
-<<1:8,
- 2:8
- >>,
- <<
- 1:8,
- 2:8
- >>,
- << 1:8,
- 2:8
- >>,
-
- (a,
- b,
- c
- ),
-
- ( a,
- b,
- c
- ),
-
-
- (
- a,
- b,
- c
- ),
-
- call(2#42423 bor
- #4234,
- 2#5432,
- other_arg),
- ok;
-indent_basics(Xlongname,
- #struct{a=Foo,
- b=Bar},
- [X|
- Y]) ->
- testing_next_clause,
- ok;
-indent_basics( % AD added clause
- X, % not sure how this should look
- Y,
- Z)
- when
- X < 42, Z > 13;
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) when % AD added clause
- X < 42, Z > 13; % testing when indentation
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) % AD added clause
- when % testing when indentation
- X < 42, Z > 13; % unsure about this one
- Y =:= 4711 ->
- foo.
-
-
-
-indent_nested() ->
- [
- {foo, 2, "string"},
- {bar, 3, "another string"}
- ].
-
-
-indent_icr(Z) -> % icr = if case receive
- %% If
- if Z >= 0 ->
- X = 43 div 4,
- foo(X);
- Z =< 10 ->
- X = 43 div 4,
- foo(X);
- Z == 5 orelse
- Z == 7 ->
- X = 43 div 4,
- foo(X);
- true ->
- if_works
- end,
- %% Case
- case {Z, foo, bar} of
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- {Z,_,_} when
- Z =:= 42 -> % AD line should be indented as a when
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when Z < 10 -> % AD when should be indented
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when % AD when should be indented
- Z < 10 % and the guards should follow when
- andalso % unsure about how though
- true ->
- X = 43 div 4,
- foo(X)
- end,
- %% begin
- begin
- sune,
- X = 74234 + foo(8456) +
- 345 div 43,
- ok
- end,
-
-
- %% receive
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- end,
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z % AD added clause
- when Z =:= 1 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z when % AD added clause
- Z =:= 2 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- after infinity ->
- foo(X),
- asd(X),
- 5*43
- end,
- receive
- after 10 ->
- foo(X),
- asd(X),
- 5*43
- end,
- ok.
-
-indent_fun() ->
- %% Changed fun to one indention level
-Var = spawn(fun(X)
- when X == 2;
- X > 10 ->
- hello,
- case Hello() of
- true when is_atom(X) ->
- foo;
- false ->
- bar
- end;
- (Foo) when is_atom(Foo),
- is_integer(X) ->
- X = 6* 45,
- Y = true andalso
- kalle
- end),
-%% check EEP37 named funs
-Fn1 = fun Fact(N) when N > 0 ->
- F = Fact(N-1),
- N * F;
-Fact(0) ->
- 1
- end,
-%% check anonymous funs too
- Fn2 = fun(0) ->
-1;
- (N) ->
- N
- end,
- ok.
-
-indent_try_catch() ->
- try
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R % AD added clause
- when R =:= 42 -> % when should be indented
- foo(R);
- error:R % AD added clause
- when % when should be indented
- R =:= 42 -> % but unsure about this (maybe 2 more)
- foo(R);
- error:R when % AD added clause
- R =:= foo -> % line should be 2 indented (works)
- foo(R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile)
- end;
-indent_try_catch() ->
- try
- foo(bar)
- of
- X when true andalso
- kalle ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2);
- X % AD added clause
- when false andalso % when should be 2 indented
- bengt ->
- gurka();
- X when % AD added clause
- false andalso % line should be 2 indented
- not bengt ->
- gurka();
- X ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile),
- bar(with_long_arg,
- with_second_arg)
- end;
- indent_try_catch() ->
- try foo()
- after
- foo(),
- bar(with_long_arg,
- with_second_arg)
- end.
-
-indent_catch() ->
- D = B +
- float(43.1),
-
- B = catch oskar(X),
-
- A = catch (baz +
- bax),
- catch foo(),
-
- C = catch B +
- float(43.1),
-
- case catch foo(X) of
- A ->
- B
- end,
-
- case
- catch foo(X)
- of
- A ->
- B
- end,
-
- case
- foo(X)
- of
- A ->
- catch B,
- X
- end,
-
- try sune of
- _ -> foo
- catch _:_ -> baf
- end,
-
- try
-sune
- of
- _ ->
- X = 5,
- (catch foo(X)),
- X + 10
- catch _:_ -> baf
- end,
-
- try
- (catch sune)
- of
- _ ->
- catch foo() %% BUGBUG can't handle catch inside try without parentheses
- catch _:_ ->
- baf
- end,
-
- try
-(catch exit())
- catch
-_ ->
- catch baf()
- end,
- ok.
-
-indent_binary() ->
- X = lists:foldr(fun(M) ->
- <<Ma/binary, " ">>
- end, [], A),
- A = <<X/binary, 0:8>>,
- B.
-
-
-indent_comprehensions() ->
-%% I don't have a good idea how we want to handle this
-%% but they are here to show how they are indented today.
-Result1 = [X ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- ],
-Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- ],
-
-Binary1 = << <<X:8>> ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- >>,
-
-Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- >>,
-ok.
-
-%% This causes an error in earlier erlang-mode versions.
-foo() ->
-[#foo{
-foo = foo}].
-
-%% Record indentation
-some_function_with_a_very_long_name() ->
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b},
- case dummy_function_with_a_very_very_long_name(x) of
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- ok;
- Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b};
- #xyz{
- a=1,
- b=2} ->
- ok
- end.
-
-another_function_with_a_very_very_long_name() ->
- #rec{
- field1=1,
- field2=1}.
-
-some_function_name_xyz(xyzzy, #some_record{
- field1=Field1,
- field2=Field2}) ->
- SomeVariable = f(#'Some-long-record-name'{
- field_a = 1,
- 'inter-xyz-parameters' =
- #'Some-other-very-long-record-name'{
- field2 = Field1,
- field2 = Field2}}),
- {ok, SomeVariable}.
-
-commas_first() ->
- {abc, [ {some_var, 1}
- , {some_other_var, 2}
- , {erlang_ftw, 9}
- , {erlang_cookie, 'cookie'}
- , {cmds,
- [ {one, "sudo ls"}
- , {one, "sudo ls"}
- , {two, "sudo ls"}
- , {three, "sudo ls"}
- , {four, "sudo ls"}
- , {three, "sudo ls"}
- ] }
- , {ssh_username, "yow"}
- , {cluster,
- [ {aaaa, [ {"10.198.55.12" , "" }
- , {"10.198.55.13" , "" }
- ] }
- , {bbbb, [ {"10.198.55.151", "" }
- , {"10.198.55.123", "" }
- , {"10.198.55.34" , "" }
- , {"10.198.55.85" , "" }
- , {"10.198.55.67" , "" }
- ] }
- , {cccc, [ {"10.198.55.68" , "" }
- , {"10.198.55.69" , "" }
- ] }
- ] }
-]
-}.
-
-
-%% this used to result in a scan-sexp error
-[{
-}].
-
-%% this used to result in 2x the correct indentation within the function
-%% body, due to the function name being mistaken for a keyword
-catcher(N) ->
-try generate_exception(N) of
-Val -> {N, normal, Val}
-catch
-throw:X -> {N, caught, thrown, X};
-exit:X -> {N, caught, exited, X};
-error:X -> {N, caught, error, X}
-end.
diff --git a/lib/tools/test/emacs_SUITE.erl b/lib/tools/test/emacs_SUITE.erl
index 77a8813db5..f4e78da667 100644
--- a/lib/tools/test/emacs_SUITE.erl
+++ b/lib/tools/test/emacs_SUITE.erl
@@ -23,10 +23,10 @@
-export([all/0, init_per_testcase/2, end_per_testcase/2]).
--export([bif_highlight/1]).
+-export([bif_highlight/1, indent/1]).
-all() ->
- [bif_highlight].
+all() ->
+ [bif_highlight, indent].
init_per_testcase(_Case, Config) ->
ErlangEl = filename:join([code:lib_dir(tools),"emacs","erlang.el"]),
@@ -74,4 +74,69 @@ check_bif_highlight(Bin, Tag, Compare) ->
[] = Compare -- EmacsIntBifs,
[] = EmacsIntBifs -- Compare.
-
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+indent(Config) ->
+ case emacs_version_ok() of
+ false -> {skip, "Old or no emacs found"};
+ true ->
+ Def = filename:dirname(code:which(?MODULE)) ++ "/" ++ ?MODULE_STRING ++ "_data",
+ Dir = proplists:get_value(data_dir, Config, Def),
+ OrigFs = filelib:wildcard(Dir ++ "/*"),
+ io:format("Dir: ~s~nFs: ~p~n", [Dir, OrigFs]),
+ Fs = [{File, unindent(File)} || File <- OrigFs,
+ filename:extension(File) =:= ""],
+ Indent = fun emacs/1,
+ [Indent(File) || {_, File} <- Fs],
+ Res = [diff(Orig, File) || {Orig, File} <- Fs],
+ [file:delete(File) || {ok, File} <- Res], %% Cleanup
+ [] = [Fail || {fail, Fail} <- Res],
+ ok
+ end.
+
+unindent(Input) ->
+ Output = Input ++ ".erl",
+ {ok, Bin} = file:read_file(Input),
+ Lines0 = string:split(Bin, "\n", all),
+ Lines = [string:trim(Line, leading, [$\s,$\t]) || Line <- Lines0],
+ %% io:format("File: ~s lines: ~w~n", [Input, length(Lines0)]),
+ %% [io:format("~s~n", [L]) || L <- Lines],
+ ok = file:write_file(Output, lists:join("\n", Lines)),
+ Output.
+
+diff(Orig, File) ->
+ case os:cmd(["diff ", Orig, " ", File]) of
+ "" -> {ok, File};
+ Diff ->
+ io:format("Fail: ~s vs ~s~n~s~n~n",[Orig, File, Diff]),
+ {fail, File}
+ end.
+
+emacs_version_ok() ->
+ case os:cmd("emacs --version | head -1") of
+ "GNU Emacs " ++ Ver ->
+ case string:to_float(Ver) of
+ {Vsn, _} when Vsn >= 24.1 ->
+ true;
+ _ ->
+ io:format("Emacs version fail~n~s~n~n",[Ver]),
+ false
+ end;
+ Res ->
+ io:format("Emacs version fail~n~s~n~n",[Res]),
+ false
+ end.
+
+emacs(File) ->
+ EmacsErlDir = filename:join([code:lib_dir(tools), "emacs"]),
+ Cmd = ["emacs ",
+ "--batch --quick ",
+ "--directory ", EmacsErlDir, " ",
+ "--eval \"(require 'erlang-start)\" ",
+ File, " ",
+ "--eval '(indent-region (point-min) (point-max) nil)' ",
+ "--eval '(save-buffer 0)'"
+ ],
+ _Res = os:cmd(Cmd),
+ % io:format("cmd ~s:~n=> ~s~n", [Cmd, _Res]),
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/comments b/lib/tools/test/emacs_SUITE_data/comments
new file mode 100644
index 0000000000..ff974ca295
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/comments
@@ -0,0 +1,25 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% 3 comment chars: always left indented
+%%% 2 comment chars: Context indented
+%%% 1 comment char: Rigth indented
+
+%%% left
+%% context dependent
+ % rigth
+
+func() ->
+%%% left
+ %% context dependent
+ % right indented
+ case get(foo) of
+ undefined ->
+ %% Testing indention
+ ok;
+ %% Catch all
+ Other ->
+ Other
+ end,
+ ok.
+
diff --git a/lib/tools/test/emacs_SUITE_data/comprehensions b/lib/tools/test/emacs_SUITE_data/comprehensions
new file mode 100644
index 0000000000..45279850a5
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/comprehensions
@@ -0,0 +1,47 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of comprehensions
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+list() ->
+ %% I don't have a good idea how we want to handle this
+ %% but they are here to show how they are indented today.
+ Result1 = [X ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
+ Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ ],
+ Res = [ func(X,
+ arg2)
+ ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
+ Result1.
+
+binary(B) ->
+ Binary1 = << <<X:8>> ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ >>,
+
+ Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ >>,
+
+ Bin3 = <<
+ <<
+ X:8,
+ 34:8
+ >>
+ || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ >>,
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/funcs b/lib/tools/test/emacs_SUITE_data/funcs
new file mode 100644
index 0000000000..877f982005
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/funcs
@@ -0,0 +1,174 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Function (and funs) indentation
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+-export([
+ func1/0,
+ func2/0,
+ a_function_with_a_very_very_long_name/0,
+ when1/2
+ ]).
+
+-compile([nowarn_unused_functions,
+ {inline, [
+ func2/2,
+ func3/2
+ ]
+ }
+ ]).
+
+func1() ->
+ basic.
+
+func2(A1,
+ A2) ->
+ ok.
+
+func3(
+ A1,
+ A2
+ ) ->
+ ok.
+
+%% Okeefe style
+func4(A1
+ ,A2
+ ,A3
+ ) ->
+ ok.
+
+func5(
+ A41
+ ,A42) ->
+ ok.
+
+a_function_with_a_very_very_long_name() ->
+ A00 = #record{
+ field1=1,
+ field2=1
+ },
+ A00.
+
+when1(W1, W2)
+ when is_number(W1),
+ is_number(W2) ->
+ ok.
+
+when2(W1,W2,W3) when
+ W1 > W2,
+ W2 > W3 ->
+ ok.
+
+when3(W1,W2,W3) when
+ W1 > W2,
+ W2 > W3
+ ->
+ ok.
+
+when4(W1,W2,W3)
+ when
+ W1 > W2,
+ W2 > W3 ->
+ ok.
+
+match1({[H|T],
+ Other},
+ M1A2) ->
+ ok.
+
+match2(
+ {
+ [H|T],
+ Other
+ },
+ M2A2
+ ) ->
+ ok.
+
+match3({
+ M3A1,
+ [
+ H |
+ T
+ ],
+ Other
+ },
+ M3A2
+ ) ->
+ ok.
+
+match4(<<
+ M4A:8,
+ M4B:16/unsigned-integer,
+ _/binary
+ >>,
+ M4C) ->
+ ok.
+
+match5(M5A,
+ #record{
+ b=M5B,
+ c=M5C
+ }
+ ) ->
+ ok.
+
+match6(M6A,
+ #{key6a := a6,
+ key6b := b6
+ }) ->
+ ok.
+
+funs(1)
+ when
+ X ->
+ %% Changed fun to one indention level
+ %% 'when' and several clause forces a depth of '4'
+ Var = spawn(fun(X, _)
+ when X == 2;
+ X > 10 ->
+ hello,
+ case Hello() of
+ true when is_atom(X) ->
+ foo;
+ false ->
+ bar
+ end;
+ (Foo) when is_atom(Foo),
+ is_integer(X) ->
+ X = 6 * 45,
+ Y = true andalso
+ kalle
+ end),
+ Var;
+funs(2) ->
+ %% check EEP37 named funs
+ Fn1 = fun
+ Factory(N) when
+ N > 0 ->
+ F = Fact(N-1),
+ N * F;
+ Factory(0) ->
+ 1
+ end,
+ Fn1;
+funs(3) ->
+ %% check anonymous funs too
+ Fn2 = fun(0) ->
+ 1;
+ (N) ->
+ N
+ end,
+ ok;
+funs(4) ->
+ X = lists:foldr(fun(M) ->
+ <<M/binary, " ">>
+ end, [], Z),
+ A = <<X/binary, 0:8>>,
+ A.
diff --git a/lib/tools/test/emacs_SUITE_data/highlight b/lib/tools/test/emacs_SUITE_data/highlight
new file mode 100644
index 0000000000..0719f6516a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/highlight
@@ -0,0 +1,78 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Open this file in your editor and manually check the colors of
+%%% different types and calls and builtin words
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+
+highlighting(X) % Function definitions should be highlighted
+ when is_integer(X) -> % and so should `when' and `is_integer' be
+ %% Highlighting
+ %% Various characters (we keep an `atom' after to see that highlighting ends)
+ $a,atom, % Characters should be marked
+ "string",atom, % and strings
+ 'asdasd',atom, % quote should be atoms??
+ 'VaV',atom,
+ 'aVa',atom,
+ '\'atom',atom,
+ 'atom\'',atom,
+ 'at\'om',atom,
+ '#1',atom,
+
+ $", atom, % atom should be ok
+ $', atom,
+
+ "string$", atom, "string$", atom, % currently buggy I know...
+ "string\$", atom, % workaround for bug above
+
+ "char $in string", atom,
+
+ 'atom$', atom, 'atom$', atom,
+ 'atom\$', atom,
+
+ 'char $in atom', atom,
+
+ $[, ${, $\\, atom,
+ ?MACRO_1,
+ ?MACRO_2(foo),
+
+ %% Numerical constants
+ 16#DD, % Should not be highlighted
+ 32#dd, % Should not be highlighted
+ 32#ddAB, % Should not be highlighted
+ 32#101, % Should not be highlighted
+ 32#ABTR, % Should not be highlighted
+
+ %% Variables
+ Variables = lists:foo(),
+ _Variables = lists:foo(),
+ AppSpec = Xyz/2,
+ Module42 = Xyz(foo, bar),
+ Module:foo(),
+ _Module:foo(), %
+ FooÅÅ = lists:reverse([tl,hd,tl,hd]), % Should highlight FooÅÅ
+ _FooÅÅ = 42, % Should highlight _FooÅÅ
+
+ %% Bifs
+ erlang:registered(),
+ registered(),
+ hd(tl(tl(hd([a,b,c])))),
+ erlang:anything(lists),
+ %% Guards
+ is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
+ is_function(Fun), is_pid(self()),
+ not_a_guard:is_list([]),
+ %% Other Types
+
+ atom, % not (currently) hightlighted
+ 234234,
+ 234.43,
+
+ [list, are, not, higlighted],
+ {nor, is, tuple},
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/icr b/lib/tools/test/emacs_SUITE_data/icr
new file mode 100644
index 0000000000..8445c1a74d
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/icr
@@ -0,0 +1,157 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of if case receive statements
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+indent_if(1, Z) ->
+ %% If
+ if Z >= 0 ->
+ X = 43 div Z,
+ X;
+ Z =< 10 ->
+ X = 43 div Z,
+ X;
+ Z == 5 orelse
+ Z == 7 ->
+ X = 43 div Z,
+ X;
+ is_number(Z),
+ Z < 32 ->
+ Z;
+ is_number(Z);
+ Z < 32 ->
+ Z * 32;
+ true ->
+ if_works
+ end;
+indent_if(2, Z) ->
+ %% If
+ if
+ Z >= 0 ->
+ X = 43 div Z,
+ X
+ ; Z =< 10 ->
+ 43 div Z
+ ; Z == 5 orelse
+ Z == 7 ->
+ X = 43 div Z,
+ X
+ ; is_number(Z),
+ Z < 32 ->
+ Z
+ ; true ->
+ if_works
+ end.
+
+indent_case(1, Z) ->
+ %% Case
+ case {Z, foo, bar} of
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_} when
+ Z =:= 42 -> % line should be indented as a when
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_}
+ when Z < 10 orelse
+ Z =:= foo -> % Binary op alignment here !!!
+ X = 43 div 4,
+ Bool = Z < 5 orelse % Binary op args align differently after when
+ Z =:= foo, % and elsewhere ???
+ foo(X);
+ {Z,_,_}
+ when % when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
+ end;
+indent_case(2, Z) ->
+ %% Case
+ case {Z, foo, bar} of
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_} when
+ Z =:= 42 -> % line should be indented as a when
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_}
+ when Z < 10 -> % when should be indented
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_}
+ when % when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
+ end.
+
+indent_begin(Z) ->
+ %% Begin
+ begin
+ sune,
+ Z = 74234 +
+ foo(8456) +
+ 345 div 43,
+ Foo = begin
+ ok,
+ foo(234),
+ begin
+ io:format("Down here\n")
+ end
+ end,
+ {Foo,
+ bar}
+ end.
+
+indent_receive(1) ->
+ %% receive
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X)
+ ; Z ->
+ X = 43 div 4,
+ foo(X)
+ end,
+ ok;
+indent_receive(2) ->
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ Z % added clause
+ when Z =:= 1 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z when % added clause
+ Z =:= 2 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z ->
+ X = 43 div 4,
+ foo(X)
+ after infinity ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ ok;
+indent_receive() ->
+ receive
+ after 10 ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/macros b/lib/tools/test/emacs_SUITE_data/macros
new file mode 100644
index 0000000000..6c874e9187
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/macros
@@ -0,0 +1,31 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Macros should be indented as code
+
+-define(M0, ok).
+
+-define(M1,
+ case X of
+ undefined -> error;
+ _ -> ok
+ end).
+
+-define(M2(M2A1,
+ M2A2),
+ func(M2A1,
+ M2A2)
+ ).
+
+-define(
+ M3,
+ undefined
+ ).
+
+-ifdef(DEBUG).
+-define(LOG,
+ logger:log(?MODULE,?LINE)
+ ).
+-else().
+-define(LOG, ok).
+-endif().
diff --git a/lib/tools/test/emacs_SUITE_data/records b/lib/tools/test/emacs_SUITE_data/records
new file mode 100644
index 0000000000..241582718c
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/records
@@ -0,0 +1,35 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%% Test that records are indented correctly
+
+-record(record0,
+ {
+ r0a,
+ r0b,
+ r0c
+ }).
+
+-record(record1, {r1a,
+ r1b,
+ r1c
+ }).
+
+-record(record2, {
+ r2a,
+ r2b
+ }).
+
+-record(record3, {r3a = 8#42423 bor
+ 8#4234,
+ r3b = 8#5432
+ bor 2#1010101,
+ r3c = 123 +
+ 234,
+ r3d}).
+
+-record(record5,
+ { r5a = 1 :: integer()
+ , r5b = foobar :: atom()
+ }).
+
diff --git a/lib/tools/test/emacs_SUITE_data/terms b/lib/tools/test/emacs_SUITE_data/terms
new file mode 100644
index 0000000000..352364a73c
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/terms
@@ -0,0 +1,174 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of terms contain builtin types
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+
+list(1) ->
+ [a,
+ b,
+ c
+ ];
+list(2) ->
+ [ a,
+ b, c
+ ];
+list(3) ->
+ [
+ a,
+ b, c
+ ];
+list(4) ->
+ [ a
+ , b
+ , c
+ ].
+
+tuple(1) ->
+ {a,
+ b,c
+ };
+tuple(2) ->
+ { a,
+ b,c
+ };
+tuple(3) ->
+ {
+ a,
+ b,c
+ };
+tuple(4) ->
+ { a
+ , b
+ ,c
+ }.
+
+binary(1) ->
+ <<1:8,
+ 2:8
+ >>;
+binary(2) ->
+ <<
+ 1:8,
+ 2:8
+ >>;
+binary(3) ->
+ << 1:8,
+ 2:8
+ >>;
+binary(4) ->
+ <<
+ 1:8
+ ,2:8
+ >>;
+binary(5) ->
+ << 1:8
+ , 2:8
+ >>.
+
+record(1) ->
+ #record{a=1,
+ b=2
+ };
+record(2) ->
+ #record{ a=1,
+ b=2
+ };
+record(3) ->
+ #record{
+ a=1,
+ b=2
+ };
+record(4) ->
+ #record{
+ a=1
+ ,b=2
+ };
+record(Record) ->
+ Record#record{
+ a=1
+ ,b=2
+ }.
+
+map(1) ->
+ #{a=>1,
+ b=>2
+ };
+map(2) ->
+ #{ a=>1,
+ b=>2
+ };
+map(3) ->
+ #{
+ a=>1,
+ b=>2
+ };
+map(4) ->
+ #{
+ a => <<"a">>
+ ,b => 2
+ };
+map(MapVar) ->
+ MapVar = #{a :=<<"a">>
+ ,b:=1}.
+
+deep(Rec) ->
+ Rec#rec{ atom = 'atom',
+ map = #{ k1 => {v,
+ 1},
+ k2 => [
+ 1,
+ 2,
+ 3
+ ],
+ {key,
+ 3}
+ =>
+ <<
+ 123:8,
+ 255:8
+ >>
+ }
+ }.
+
+%% Record indentation
+some_function_with_a_very_long_name() ->
+ #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b},
+ case dummy_function_with_a_very_very_long_name(x) of
+ #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b} ->
+ ok;
+ Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b} ->
+ Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b};
+ #xyz{
+ a=1,
+ b=2} ->
+ ok
+ end.
+
+some_function_name_xyz(xyzzy, #some_record{
+ field1=Field1,
+ field2=Field2}) ->
+ SomeVariable = f(#'Some-long-record-name'{
+ field_a = 1,
+ 'inter-xyz-parameters' =
+ #'Some-other-very-long-record-name'{
+ field2 = Field1,
+ field2 = Field2}}),
+ {ok, SomeVariable}.
+
+foo() ->
+ [#foo{
+ foo = foo}].
diff --git a/lib/tools/test/emacs_SUITE_data/try_catch b/lib/tools/test/emacs_SUITE_data/try_catch
new file mode 100644
index 0000000000..0005b2003a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/try_catch
@@ -0,0 +1,166 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Try and catch indentation is hard
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+try_catch() ->
+ try
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R
+ when R =:= 42 -> % when should be indented
+ foo(R);
+ error:R
+ when % when should be indented
+ R =:= 42 -> % but unsure about this (maybe 2 more)
+ foo(R);
+ error:R when
+ R =:= foo -> % line should be 2 indented (works)
+ foo(R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile)
+ end;
+try_catch() ->
+ try
+ foo(bar)
+ of
+ X when true andalso
+ kalle ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2);
+ X
+ when false andalso % when should be 2 indented
+ bengt ->
+ gurka();
+ X when
+ false andalso % line should be 2 indented
+ not bengt ->
+ gurka();
+ X ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile),
+ bar(with_long_arg,
+ with_second_arg)
+ end;
+try_catch() ->
+ try foo()
+ after
+ foo(),
+ bar(with_long_arg,
+ with_second_arg)
+ end.
+
+indent_catch() ->
+ D = B +
+ float(43.1),
+
+ B = catch oskar(X),
+
+ A = catch (baz +
+ bax),
+ catch foo(),
+
+ C = catch B +
+ float(43.1),
+
+ case catch foo(X) of
+ A ->
+ B
+ end,
+
+ case
+ catch foo(X)
+ of
+ A ->
+ B
+ end,
+
+ case
+ foo(X)
+ of
+ A ->
+ catch B,
+ X
+ end,
+
+ try sune of
+ _ -> foo
+ catch _:_ -> baf
+ end,
+
+ Variable = try
+ sune
+ of
+ _ ->
+ X = 5,
+ (catch foo(X)),
+ X + 10
+ catch _:_ -> baf
+ after cleanup()
+ end,
+
+ try
+ (catch sune)
+ of
+ _ ->
+ foo1(),
+ catch foo() %% BUGBUG can't handle catch inside try without parentheses
+ catch _:_ ->
+ baf
+ end,
+
+ try
+ (catch exit())
+ catch
+ _ ->
+ catch baf()
+ end,
+ ok.
+
+%% this used to result in 2x the correct indentation within the function
+%% body, due to the function name being mistaken for a keyword
+catcher(N) ->
+ try generate_exception(N) of
+ Val -> {N, normal, Val}
+ catch
+ throw:X -> {N, caught, thrown, X};
+ exit:X -> {N, caught, exited, X};
+ error:X -> {N, caught, error, X}
+ end.
diff --git a/lib/tools/test/emacs_SUITE_data/type_specs b/lib/tools/test/emacs_SUITE_data/type_specs
new file mode 100644
index 0000000000..e71841cc7a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/type_specs
@@ -0,0 +1,110 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%% Tests how types and specs are indented (also that the editor can parse them)
+%% May need improvements
+
+
+-type ann() :: Var :: integer().
+-type ann2() ::
+ 'return'
+ | 'return_white_spaces'
+ | 'return_comments'
+ | 'text' | ann().
+-type paren() ::
+ (ann2()).
+
+-type t6() ::
+ 1 | 2 | 3 |
+ 'foo'
+ | 'bar'.
+
+-type t8() :: {any(),none(),pid(),port(),
+ reference(),float()}.
+
+-type t14() :: [erl_scan:foo() |
+ %% Should be highlighted
+ term() |
+ boolean() |
+ byte() |
+ char() |
+ non_neg_integer() | nonempty_list() |
+ pos_integer() |
+ neg_integer() |
+ number() |
+ list() |
+ nonempty_improper_list() | nonempty_maybe_improper_list() |
+ maybe_improper_list() | string() | iolist() | byte() |
+ module() |
+ mfa() |
+ node() |
+ timeout() |
+ no_return() |
+ %% Should not be highlighted
+ nonempty_() | nonlist() |
+ erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+
+-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
+ <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
+ <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
+ <<_:34>>|<<_:34>>|<<_:34>>].
+
+-type t18() ::
+ fun(() -> t17() | t16()).
+-type t19() ::
+ fun((t18()) -> t16()) |
+ fun((nonempty_maybe_improper_list('integer', any())|
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer())
+ ->
+ nonempty_maybe_improper_list('integer', any())| %% left to col 16?
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer()). %% left to col 16?
+-type t20() :: [t19(), ...].
+-type t25() :: #rec3{f123 :: [t24() |
+ 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())]}.
+-type t26() :: #rec4{ a :: integer()
+ , b :: any()
+ }.
+
+%% Spec
+
+-spec t1(FooBar :: t99()) -> t99();
+ (t2()) -> t2();
+ (t4()) -> t4() when is_subtype(t4(), t24);
+ (t23()) -> t23() when is_subtype(t23(), atom()),
+ is_subtype(t23(), t14());
+ (t24()) -> t24() when is_subtype(t24(), atom()),
+ is_subtype(t24(), t14()),
+ is_subtype(t24(), t4()).
+
+-spec over(I :: integer()) -> R1 :: foo:typen();
+ (A :: atom()) -> R2 :: foo:atomen();
+ (T :: tuple()) -> R3 :: bar:typen().
+
+-spec mod:t2() -> any().
+
+-spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
+ | {'del_member', name(), pid()},
+ #state{}) -> {'noreply', #state{}}.
+
+-spec handle_cast(Cast ::
+ {'exchange', node(), [[name(),...]]}
+ | {'del_member', name(), pid()},
+ #state{}) ->
+ {'noreply', #state{}}. %% left to col 10?
+
+-spec all(fun((T) -> boolean()), List :: [T]) ->
+ boolean() when is_subtype(T, term()). % (*)
+
+-spec get_closest_pid(term()) ->
+ Return :: pid() %% left to col 10?
+ | {'error', {'no_process', term()}} %% left to col 10?
+ | {'no_such_group', term()}. %% left to col 10?
+
+-spec add( X :: integer()
+ , Y :: integer()
+ ) -> integer().
+
+-opaque attributes_data() ::
+ [{'column', column()} | {'line', info_line()} |
+ {'text', string()}] | {line(),column()}.
diff --git a/otp_versions.table b/otp_versions.table
index 5626fdff88..b976962c1e 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-20.2.4 : ssh-4.6.5 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.2 megaco-3.18.3 mnesia-4.15.3 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.4 sasl-3.1.1 snmp-5.2.9 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 :
OTP-20.2.3 : erts-9.2.1 kernel-5.4.2 runtime_tools-1.12.4 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 megaco-3.18.3 mnesia-4.15.3 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 sasl-3.1.1 snmp-5.2.9 ssh-4.6.4 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 :
OTP-20.2.2 : mnesia-4.15.3 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.2 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.1 megaco-3.18.3 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.3 sasl-3.1.1 snmp-5.2.9 ssh-4.6.4 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 :
OTP-20.2.1 : ssh-4.6.4 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.2 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.1 megaco-3.18.3 mnesia-4.15.2 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.3 sasl-3.1.1 snmp-5.2.9 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 :
@@ -17,6 +18,7 @@ OTP-20.0.3 : asn1-5.0.2 compiler-7.1.1 erts-9.0.3 ssh-4.5.1 # common_test-1.15.1
OTP-20.0.2 : asn1-5.0.1 erts-9.0.2 kernel-5.3.1 # common_test-1.15.1 compiler-7.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 eldap-1.2.2 erl_docgen-0.7 erl_interface-3.10 et-1.6 eunit-2.3.3 hipe-3.16 ic-4.4.2 inets-6.4 jinterface-1.8 megaco-3.18.2 mnesia-4.15 observer-2.4 odbc-2.12 orber-3.8.3 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 runtime_tools-1.12.1 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 stdlib-3.4.1 syntax_tools-2.1.2 tools-2.10.1 wx-1.8.1 xmerl-1.3.15 :
OTP-20.0.1 : common_test-1.15.1 erts-9.0.1 runtime_tools-1.12.1 stdlib-3.4.1 tools-2.10.1 # asn1-5.0 compiler-7.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 eldap-1.2.2 erl_docgen-0.7 erl_interface-3.10 et-1.6 eunit-2.3.3 hipe-3.16 ic-4.4.2 inets-6.4 jinterface-1.8 kernel-5.3 megaco-3.18.2 mnesia-4.15 observer-2.4 odbc-2.12 orber-3.8.3 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 syntax_tools-2.1.2 wx-1.8.1 xmerl-1.3.15 :
OTP-20.0 : asn1-5.0 common_test-1.15 compiler-7.1 cosProperty-1.2.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 erl_docgen-0.7 erl_interface-3.10 erts-9.0 eunit-2.3.3 hipe-3.16 inets-6.4 jinterface-1.8 kernel-5.3 megaco-3.18.2 mnesia-4.15 observer-2.4 orber-3.8.3 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 runtime_tools-1.12 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 stdlib-3.4 syntax_tools-2.1.2 tools-2.10 wx-1.8.1 xmerl-1.3.15 # cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 eldap-1.2.2 et-1.6 ic-4.4.2 odbc-2.12 os_mon-2.4.2 otp_mibs-1.1.1 :
+OTP-19.3.6.6 : ssh-4.4.2.2 ssl-8.1.3.1.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.4 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.6.5 : erts-8.3.5.4 mnesia-4.14.3.1 ssh-4.4.2.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssl-8.1.3.1 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.6.4 : ssl-8.1.3.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.6.3 : compiler-7.0.4.1 erts-8.3.5.3 # asn1-4.0.4 common_test-1.14 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
@@ -50,6 +52,7 @@ OTP-19.0.3 : inets-6.3.2 kernel-5.0.1 ssl-8.0.1 # asn1-4.0.3 common_test-1.12.2
OTP-19.0.2 : compiler-7.0.1 erts-8.0.2 stdlib-3.0.1 # asn1-4.0.3 common_test-1.12.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
OTP-19.0.1 : dialyzer-3.0.1 erts-8.0.1 inets-6.3.1 observer-2.2.1 ssh-4.3.1 tools-2.8.5 # asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
OTP-19.0 : asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 tools-2.8.4 typer-0.9.11 wx-1.7 xmerl-1.3.11 # :
+OTP-18.3.4.8 : ssh-4.2.2.5 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3.1 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1.4 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4.1 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.4.7 : ssl-7.3.3.2 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3.1 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1.4 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4.1 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2.4 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.4.6 : compiler-6.0.3.1 eldap-1.2.1.1 erts-7.3.1.4 ssh-4.2.2.4 # asn1-4.0.2 common_test-1.12.1.1 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4.1 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3.1 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.4.5 : crypto-3.6.3.1 erts-7.3.1.3 inets-6.2.4.1 ssh-4.2.2.3 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3.1 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
diff --git a/system/doc/design_principles/release_structure.xml b/system/doc/design_principles/release_structure.xml
index a0ab21c43f..8e62ba845f 100644
--- a/system/doc/design_principles/release_structure.xml
+++ b/system/doc/design_principles/release_structure.xml
@@ -208,8 +208,8 @@ releases/ch_rel-1.rel</pre>
is therefore now instead duplicated in the tar file so no manual
copying is necessary.</p>
<p>If a <c>relup</c> file and/or a system configuration file called
- <c>sys.config</c> is found, these files are also included in
- the release package. See
+ <c>sys.config</c>, or a <c>sys.config.src</c>, is found, these files
+ are also included in the release package. See
<seealso marker="release_handling#req">Release Handling</seealso>.</p>
<p>Options can be set to make the release package include source
code and the ERTS binary as well.</p>
@@ -240,7 +240,7 @@ $ROOT/lib/App1-AVsn1/ebin
<item><c>erts-EVsn/bin</c> - Erlang runtime system executables</item>
<item><c>releases/Vsn</c> - <c>.rel</c> file and boot script
<c>start.boot</c>; if present in the release package, <c>relup</c>
- and/or <c>sys.config</c></item>
+ and/or <c>sys.config</c> or <c>sys.config.src</c></item>
<item><c>bin</c> - Top-level Erlang runtime system executables</item>
</list>
<p>Applications are not required to be located under directory
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 896eda5f1c..245e7d3674 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -151,7 +151,7 @@
<row>
<cell>Processes</cell>
<cell>The maximum number of simultaneously alive Erlang processes
- is by default 32,768. This limit can be configured at startup.
+ is by default 262,144. This limit can be configured at startup.
For more information, see the
<seealso marker="erts:erl#max_processes"><c>+P</c></seealso>
command-line flag in the
diff --git a/system/doc/system_principles/create_target.xmlsrc b/system/doc/system_principles/create_target.xmlsrc
index f9b27ffc35..dc6cbbe980 100644
--- a/system/doc/system_principles/create_target.xmlsrc
+++ b/system/doc/system_principles/create_target.xmlsrc
@@ -263,6 +263,14 @@ os> <input>/usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FI
current directory create not only the file <c>mysystem.rel</c>,
but also file <c>sys.config</c>, the latter file is tacitly
put in the appropriate directory.</p>
+ <p>However, it can also be convenient to replace variables in
+ within a <c>sys.config</c> on the target after unpacking but
+ before running the release. If you have a <c>sys.config.src</c>
+ it will be included and is not required to be a valid Erlang term
+ file like <c>sys.config</c>. Before running the release you must
+ have a valid <c>sys.config</c> in the same directory, so using
+ <c>sys.config.src</c> requires having some tool to populate what is
+ needed and write <c>sys.config</c> to disk before booting the release.</p>
</section>
<section>