aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--HOWTO/INSTALL-WIN32.md8
-rw-r--r--bootstrap/bin/start.bootbin5121 -> 5151 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin5121 -> 5151 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_a.beambin2804 -> 2632 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11780 -> 11604 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin8916 -> 8744 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bs.beambin5788 -> 5616 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bsm.beambin12120 -> 11948 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_clean.beambin8624 -> 8452 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dead.beambin13124 -> 12952 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dict.beambin5256 -> 5084 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_disasm.beambin24972 -> 24800 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_except.beambin3456 -> 3284 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_flatten.beambin3028 -> 2856 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin8800 -> 8628 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_listing.beambin2932 -> 2760 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_opcodes.beambin7276 -> 7104 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_peep.beambin2680 -> 2508 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_receive.beambin6344 -> 6172 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_record.beambin2072 -> 1900 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_reorder.beambin2124 -> 1952 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_split.beambin2320 -> 2148 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_trim.beambin7728 -> 7556 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_type.beambin17804 -> 17632 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin13316 -> 13144 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin29220 -> 29048 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_z.beambin2984 -> 2812 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin30300 -> 30128 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_clauses.beambin3104 -> 2932 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_inline.beambin37908 -> 37728 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_sets.beambin3032 -> 2860 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_trees.beambin21048 -> 20876 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin39192 -> 39020 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_lib.beambin4460 -> 4288 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_lint.beambin13028 -> 12856 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_parse.beambin62036 -> 61872 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_pp.beambin11920 -> 11748 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_scan.beambin6884 -> 6712 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/erl_bifs.beambin2372 -> 2152 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/rec_env.beambin4760 -> 4588 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_dsetel.beambin7120 -> 6948 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin49584 -> 49412 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold_lists.beambin4744 -> 4572 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_inline.beambin4168 -> 3996 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_pre_attributes.beambin2916 -> 2744 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_codegen.beambin53860 -> 53688 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin56556 -> 56384 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin54844 -> 54672 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel_pp.beambin12700 -> 12532 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_life.beambin17188 -> 17016 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application.beambin3908 -> 3792 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin30956 -> 30840 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_master.beambin6520 -> 6404 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_starter.beambin1312 -> 1196 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/auth.beambin6464 -> 6348 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code.beambin13228 -> 13112 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin24168 -> 24052 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log.beambin32776 -> 32768 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin24176 -> 24212 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_server.beambin6548 -> 6432 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_sup.beambin664 -> 548 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_ac.beambin25088 -> 24972 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin10968 -> 10852 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_boot_server.beambin5872 -> 5756 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_ddll.beambin3004 -> 2888 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_distribution.beambin1724 -> 1608 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_epmd.beambin7180 -> 7064 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_reply.beambin1016 -> 900 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_signal_handler.beambin1068 -> 952 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_handler.beambin1744 -> 1628 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_logger.beambin6044 -> 6252 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erts_debug.beambin5752 -> 5636 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file.beambin14208 -> 14092 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_io_server.beambin15160 -> 15044 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_server.beambin5464 -> 5348 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_sctp.beambin3304 -> 3188 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_tcp.beambin2204 -> 2088 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_udp.beambin1428 -> 1312 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global.beambin31372 -> 31256 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin17240 -> 17124 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_search.beambin3192 -> 3076 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group.beambin13984 -> 13936 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group_history.beambin0 -> 5732 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/heart.beambin5468 -> 5352 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin12632 -> 12516 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin23384 -> 23268 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_sctp.beambin1580 -> 1464 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp.beambin3120 -> 3004 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp_dist.beambin980 -> 864 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_udp.beambin1872 -> 1756 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_config.beambin7640 -> 7524 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_db.beambin26632 -> 26516 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_dns.beambin19428 -> 19312 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_gethost_native.beambin10252 -> 10136 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_hosts.beambin2232 -> 2116 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_parse.beambin12856 -> 12740 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_res.beambin14404 -> 14288 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_sctp.beambin2296 -> 2180 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp.beambin2812 -> 2696 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp_dist.beambin7496 -> 7380 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_udp.beambin2040 -> 1924 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app1
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.beambin4000 -> 3880 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel_config.beambin2868 -> 2752 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/local_tcp.beambin2376 -> 2260 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/local_udp.beambin1524 -> 1408 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net.beambin720 -> 604 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_adm.beambin3056 -> 2940 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_kernel.beambin24452 -> 24336 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin4368 -> 4252 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/pg2.beambin7968 -> 7852 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/ram_file.beambin6456 -> 6340 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/rpc.beambin8104 -> 7988 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/seq_trace.beambin1716 -> 1600 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/standard_error.beambin3940 -> 3824 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user.beambin11612 -> 11496 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_drv.beambin11260 -> 11144 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_sup.beambin1848 -> 1732 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/wrap_log_reader.beambin3244 -> 3128 bytes
-rw-r--r--bootstrap/lib/kernel/include/inet.hrl2
-rw-r--r--bootstrap/lib/stdlib/ebin/array.beambin11912 -> 11776 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/base64.beambin4756 -> 4620 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/beam_lib.beambin19652 -> 19516 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin2952 -> 2816 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/c.beambin17592 -> 17456 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/calendar.beambin5264 -> 5128 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin49160 -> 49024 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_server.beambin6992 -> 6856 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_sup.beambin672 -> 536 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_utils.beambin28020 -> 27884 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v9.beambin48096 -> 47960 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dict.beambin9652 -> 9516 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph.beambin8016 -> 7880 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph_utils.beambin6960 -> 6824 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin.beambin10176 -> 10040 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin_expand.beambin4024 -> 3888 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin28160 -> 28024 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_abstract_code.beambin1156 -> 1020 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_anno.beambin3764 -> 3628 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_bits.beambin2604 -> 2468 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_compile.beambin7236 -> 7100 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_eval.beambin29952 -> 29816 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_expand_records.beambin21932 -> 21796 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_internal.beambin7896 -> 7760 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin91164 -> 91992 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin88620 -> 88484 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_posix_msg.beambin5136 -> 5000 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin27084 -> 26948 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_scan.beambin28352 -> 28216 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_tar.beambin32932 -> 32796 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_file_h.beambin4460 -> 4208 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_tty_h.beambin4792 -> 4544 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/escript.beambin16976 -> 16848 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ets.beambin22472 -> 22336 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/eval_bits.beambin8240 -> 8104 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/file_sorter.beambin29420 -> 29284 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filelib.beambin10176 -> 10040 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filename.beambin14216 -> 14080 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_sets.beambin8520 -> 8384 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_trees.beambin5708 -> 5572 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen.beambin5512 -> 5476 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_event.beambin13468 -> 13556 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_fsm.beambin10876 -> 11080 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin13212 -> 13512 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_statem.beambin17832 -> 17972 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io.beambin6332 -> 6196 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib.beambin10684 -> 11956 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_format.beambin13576 -> 13440 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_fread.beambin7316 -> 7180 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_pretty.beambin17284 -> 17148 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lib.beambin14904 -> 14768 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lists.beambin30028 -> 29892 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/log_mf_h.beambin2648 -> 2512 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/maps.beambin3008 -> 2872 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/math.beambin1424 -> 1288 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ms_transform.beambin19824 -> 19688 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/orddict.beambin3080 -> 2944 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ordsets.beambin2028 -> 1892 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/otp_internal.beambin10552 -> 10416 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/pool.beambin3956 -> 3820 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proc_lib.beambin10788 -> 11320 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proplists.beambin4860 -> 4724 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc.beambin69264 -> 69148 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin75516 -> 75380 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/queue.beambin6340 -> 6204 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/rand.beambin19296 -> 19160 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/random.beambin1860 -> 1724 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/re.beambin13556 -> 13420 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sets.beambin6676 -> 6540 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell.beambin29948 -> 29812 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell_default.beambin4200 -> 4064 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/slave.beambin4888 -> 4752 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sofs.beambin37732 -> 37596 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.app2
-rw-r--r--bootstrap/lib/stdlib/ebin/string.beambin24536 -> 24400 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin22520 -> 22384 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor_bridge.beambin2136 -> 2000 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sys.beambin8536 -> 8400 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/timer.beambin5540 -> 5404 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode.beambin13176 -> 13040 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode_util.beambin193448 -> 193312 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/win32reg.beambin5560 -> 5424 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/zip.beambin26540 -> 26404 bytes
-rw-r--r--erts/Makefile12
-rw-r--r--erts/configure.in4
-rw-r--r--erts/doc/src/erl.xml7
-rw-r--r--erts/doc/src/erlang.xml28
-rw-r--r--erts/emulator/beam/beam_emu.c74
-rw-r--r--erts/emulator/beam/big.c25
-rw-r--r--erts/emulator/beam/big.h2
-rw-r--r--erts/emulator/beam/erl_alloc.c22
-rw-r--r--erts/emulator/beam/erl_alloc.h7
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c11
-rw-r--r--erts/emulator/beam/erl_cpu_topology.h8
-rw-r--r--erts/emulator/beam/erl_db_util.c11
-rw-r--r--erts/emulator/beam/erl_gc.c2
-rw-r--r--erts/emulator/beam/erl_init.c24
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h4
-rw-r--r--erts/emulator/beam/erl_port.h10
-rw-r--r--erts/emulator/beam/erl_port_task.c7
-rw-r--r--erts/emulator/beam/erl_port_task.h1
-rw-r--r--erts/emulator/beam/erl_process.c39
-rw-r--r--erts/emulator/beam/erl_process.h7
-rw-r--r--erts/emulator/beam/global.h2
-rw-r--r--erts/emulator/beam/io.c2
-rw-r--r--erts/emulator/beam/ops.tab128
-rw-r--r--erts/emulator/drivers/common/inet_drv.c75
-rw-r--r--erts/emulator/sys/unix/sys.c1
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl2
-rw-r--r--erts/emulator/test/code_SUITE.erl21
-rw-r--r--erts/emulator/test/driver_SUITE.erl2
-rw-r--r--erts/emulator/test/mtx_SUITE.erl2
-rw-r--r--erts/emulator/test/signal_SUITE.erl2
-rw-r--r--erts/emulator/test/statistics_SUITE.erl4
-rw-r--r--erts/emulator/test/trace_SUITE.erl4
-rw-r--r--erts/emulator/test/trace_meta_SUITE.erl2
-rwxr-xr-xerts/emulator/utils/beam_makeops151
-rw-r--r--erts/etc/common/erlexec.c105
-rw-r--r--erts/etc/unix/Makefile4
-rw-r--r--erts/start_scripts/Makefile2
-rw-r--r--lib/common_test/doc/src/ct_ssh.xml27
-rw-r--r--lib/common_test/src/ct_ssh.erl28
-rw-r--r--lib/common_test/test/ct_keep_logs_SUITE.erl29
-rw-r--r--lib/common_test/test/ct_test_support.erl2
-rw-r--r--lib/compiler/doc/src/compile.xml8
-rwxr-xr-xlib/compiler/src/genop.tab2
-rw-r--r--lib/compiler/test/compile_SUITE.erl16
-rw-r--r--lib/crypto/c_src/crypto.c3
-rw-r--r--lib/inets/doc/src/http_uri.xml19
-rw-r--r--lib/inets/doc/src/httpc.xml2
-rw-r--r--lib/inets/src/http_lib/http_uri.erl77
-rw-r--r--lib/inets/src/http_lib/http_util.erl4
-rw-r--r--lib/inets/test/uri_SUITE.erl104
-rw-r--r--lib/kernel/doc/src/disk_log.xml6
-rw-r--r--lib/kernel/doc/src/error_logger.xml15
-rw-r--r--lib/kernel/doc/src/kernel_app.xml23
-rw-r--r--lib/kernel/src/Makefile1
-rw-r--r--lib/kernel/src/disk_log.erl5
-rw-r--r--lib/kernel/src/disk_log.hrl4
-rw-r--r--lib/kernel/src/disk_log_1.erl28
-rw-r--r--lib/kernel/src/error_logger.erl20
-rw-r--r--lib/kernel/src/global.erl3
-rw-r--r--lib/kernel/src/group.erl3
-rw-r--r--lib/kernel/src/group_history.erl341
-rw-r--r--lib/kernel/src/kernel.app.src1
-rw-r--r--lib/kernel/test/code_SUITE.erl9
-rw-r--r--lib/kernel/test/disk_log_SUITE.erl17
-rw-r--r--lib/observer/src/etop.erl10
-rw-r--r--lib/observer/src/observer_alloc_wx.erl19
-rw-r--r--lib/observer/src/observer_pro_wx.erl180
-rw-r--r--lib/observer/src/observer_wx.erl6
-rw-r--r--lib/runtime_tools/src/observer_backend.erl19
-rw-r--r--lib/sasl/src/sasl_report.erl14
-rw-r--r--lib/ssh/doc/src/ssh.xml21
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl23
-rw-r--r--lib/ssh/src/ssh_file.erl17
-rw-r--r--lib/ssh/src/ssh_options.erl76
-rw-r--r--lib/ssh/src/ssh_transport.erl32
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_encode_decode.erl10
-rw-r--r--lib/ssh/test/ssh_algorithms_SUITE.erl166
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl9
-rw-r--r--lib/ssh/test/ssh_property_test_SUITE.erl3
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl122
-rw-r--r--lib/ssh/test/ssh_test_lib.erl6
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl68
-rw-r--r--lib/ssl/src/dtls_connection.erl38
-rw-r--r--lib/ssl/src/dtls_record.erl55
-rw-r--r--lib/ssl/src/ssl.erl11
-rw-r--r--lib/ssl/src/ssl_connection.erl79
-rw-r--r--lib/ssl/src/tls_connection.erl6
-rw-r--r--lib/ssl/test/ssl_bench_SUITE.erl20
-rw-r--r--lib/stdlib/doc/src/gen_event.xml10
-rw-r--r--lib/stdlib/doc/src/gen_server.xml12
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml22
-rw-r--r--lib/stdlib/src/erl_lint.erl73
-rw-r--r--lib/stdlib/src/error_logger_file_h.erl12
-rw-r--r--lib/stdlib/src/error_logger_tty_h.erl14
-rw-r--r--lib/stdlib/src/escript.erl3
-rw-r--r--lib/stdlib/src/gen.erl10
-rw-r--r--lib/stdlib/src/gen_event.erl71
-rw-r--r--lib/stdlib/src/gen_fsm.erl81
-rw-r--r--lib/stdlib/src/gen_server.erl289
-rw-r--r--lib/stdlib/src/gen_statem.erl26
-rw-r--r--lib/stdlib/src/io_lib.erl115
-rw-r--r--lib/stdlib/src/proc_lib.erl134
-rw-r--r--lib/stdlib/test/dummy_h.erl6
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl63
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl46
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl46
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl75
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl93
-rw-r--r--lib/stdlib/test/io_SUITE.erl59
-rw-r--r--lib/tools/doc/src/lcnt.xml10
-rw-r--r--lib/tools/doc/src/lcnt_chapter.xml30
-rw-r--r--lib/tools/test/lcnt_SUITE.erl85
-rw-r--r--lib/wx/c_src/egl_impl.h2
-rw-r--r--make/otp.mk.in1
-rw-r--r--make/otp_subdir.mk4
-rw-r--r--system/doc/programming_examples/bit_syntax.xml21
319 files changed, 2821 insertions, 1226 deletions
diff --git a/HOWTO/INSTALL-WIN32.md b/HOWTO/INSTALL-WIN32.md
index c74107d749..4304fb3fb8 100644
--- a/HOWTO/INSTALL-WIN32.md
+++ b/HOWTO/INSTALL-WIN32.md
@@ -612,7 +612,7 @@ tools:
We would recommend using 1.0.2d.
-* Building with wxWidgets. Download wxWidgets-3.0.2 or higher.
+* Building with wxWidgets. Download wxWidgets-3.0.3 or higher.
Install or unpack it to the pgm folder:
Cygwin:
@@ -622,19 +622,19 @@ tools:
MSYS2:
`DRIVE:/PATH/msys<32/64>/opt/local/pgm`
- If the `wxUSE_POSTSCRIPT` isn't enabled in `<path\to\pgm>\wxMSW-3.0.2\include\wx\msw\setup.h`,
+ If the `wxUSE_POSTSCRIPT` isn't enabled in `<path\to\pgm>\wxMSW-3.0.3\include\wx\msw\setup.h`,
enable it.
build: From a command prompt with the VC tools available (See the
instructions for OpenSSL build above for help on starting the
proper command prompt in RELEASE mode):
- C:\...\> cd <path\to\pgm>\wxMSW-3.0.2\build\msw
+ C:\...\> cd <path\to\pgm>\wxMSW-3.0.3\build\msw
C:\...\> nmake BUILD=release SHARED=0 DIR_SUFFIX_CPU= -f makefile.vc
Or - if building a 64bit version:
- C:\...\> cd <path\to\pgm>\wxMSW-3.0.2\build\msw
+ C:\...\> cd <path\to\pgm>\wxMSW-3.0.3\build\msw
C:\...\> nmake TARGET_CPU=amd64 BUILD=release SHARED=0 DIR_SUFFIX_CPU= -f makefile.vc
* Get the Erlang source distribution (from <http://www.erlang.org/download.html>).
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index 6a42dfe423..61a255e086 100644
--- a/bootstrap/bin/start.boot
+++ b/bootstrap/bin/start.boot
Binary files differ
diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot
index 6a42dfe423..61a255e086 100644
--- a/bootstrap/bin/start_clean.boot
+++ b/bootstrap/bin/start_clean.boot
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_a.beam b/bootstrap/lib/compiler/ebin/beam_a.beam
index bc936a29f4..1459567485 100644
--- a/bootstrap/lib/compiler/ebin/beam_a.beam
+++ b/bootstrap/lib/compiler/ebin/beam_a.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam
index bdd4b00b3f..7335dbab61 100644
--- a/bootstrap/lib/compiler/ebin/beam_asm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_asm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam
index 3c286dffbf..fa708d60a0 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 7840bbdf47..61b1b9b055 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 568c2f3ec4..4b4412532a 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 178f660d7a..8b97d6e227 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 605607445f..6960217ac0 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 0eafd9c839..b8cc501dd6 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 1028fd7e6c..d68a8b7fc5 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 4cdbb91dd5..8987254a04 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 2db71eafdf..5c019f7ed1 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 d92c009533..51761fff35 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_listing.beam b/bootstrap/lib/compiler/ebin/beam_listing.beam
index bd4a5760f3..19af25e71a 100644
--- a/bootstrap/lib/compiler/ebin/beam_listing.beam
+++ b/bootstrap/lib/compiler/ebin/beam_listing.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_opcodes.beam b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
index 9c7029898e..f8d1e43f1d 100644
--- a/bootstrap/lib/compiler/ebin/beam_opcodes.beam
+++ b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_peep.beam b/bootstrap/lib/compiler/ebin/beam_peep.beam
index 892d8a67c5..0f8397da3e 100644
--- a/bootstrap/lib/compiler/ebin/beam_peep.beam
+++ b/bootstrap/lib/compiler/ebin/beam_peep.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_receive.beam b/bootstrap/lib/compiler/ebin/beam_receive.beam
index 9b6ee8692e..1d4a96e02d 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_record.beam b/bootstrap/lib/compiler/ebin/beam_record.beam
index 96dbaa4338..1a5127146c 100644
--- a/bootstrap/lib/compiler/ebin/beam_record.beam
+++ b/bootstrap/lib/compiler/ebin/beam_record.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_reorder.beam b/bootstrap/lib/compiler/ebin/beam_reorder.beam
index 7500c0f725..ba3759c2d9 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 def7cd3314..c26800e0ab 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 31e2a2c1c2..6f9725b7c1 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 76a2a176f7..e2b26e648a 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 1d88c6a6e7..88e8398c31 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 97c24118b1..28fbf7ea01 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/beam_z.beam b/bootstrap/lib/compiler/ebin/beam_z.beam
index 6a95f140f1..b55e3c2383 100644
--- a/bootstrap/lib/compiler/ebin/beam_z.beam
+++ b/bootstrap/lib/compiler/ebin/beam_z.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index b9f10145c0..d66a51d75e 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_clauses.beam b/bootstrap/lib/compiler/ebin/cerl_clauses.beam
index 4c2e4e7f97..3d7e9e4d7a 100644
--- a/bootstrap/lib/compiler/ebin/cerl_clauses.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_clauses.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_inline.beam b/bootstrap/lib/compiler/ebin/cerl_inline.beam
index 605f17f057..a703e1759b 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/cerl_sets.beam b/bootstrap/lib/compiler/ebin/cerl_sets.beam
index 72bcb3527a..b5ec77d84b 100644
--- a/bootstrap/lib/compiler/ebin/cerl_sets.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_sets.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_trees.beam b/bootstrap/lib/compiler/ebin/cerl_trees.beam
index 6193ea155f..47f46fa679 100644
--- a/bootstrap/lib/compiler/ebin/cerl_trees.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_trees.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index 65809b44de..e5ed9dfd3e 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_lib.beam b/bootstrap/lib/compiler/ebin/core_lib.beam
index eaca62b945..ba2f8ff573 100644
--- a/bootstrap/lib/compiler/ebin/core_lib.beam
+++ b/bootstrap/lib/compiler/ebin/core_lib.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam
index 5ed8c04322..fa31a6555f 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_parse.beam b/bootstrap/lib/compiler/ebin/core_parse.beam
index 0555025360..07abc0dd36 100644
--- a/bootstrap/lib/compiler/ebin/core_parse.beam
+++ b/bootstrap/lib/compiler/ebin/core_parse.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_pp.beam b/bootstrap/lib/compiler/ebin/core_pp.beam
index 0513126ae0..a7cf3140ff 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 1f3d88e797..2ce3a55b85 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/erl_bifs.beam b/bootstrap/lib/compiler/ebin/erl_bifs.beam
index a385352783..e027c6a303 100644
--- a/bootstrap/lib/compiler/ebin/erl_bifs.beam
+++ b/bootstrap/lib/compiler/ebin/erl_bifs.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/rec_env.beam b/bootstrap/lib/compiler/ebin/rec_env.beam
index 1830d079b4..2f6b4f8af4 100644
--- a/bootstrap/lib/compiler/ebin/rec_env.beam
+++ b/bootstrap/lib/compiler/ebin/rec_env.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam b/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
index 6e9bd6255c..59c470fbae 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_dsetel.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 90296baf60..98be753e31 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_fold_lists.beam b/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
index 373debfc27..ba5e540511 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_fold_lists.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 92d636005d..714e073b9f 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 52391ec2c9..c418856bad 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 df54c8cf70..a40e6f9447 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 73a5a6c48b..eb0865231a 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 fac8b05528..cebb49597c 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 54af18ed4c..331e6ba032 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/compiler/ebin/v3_life.beam b/bootstrap/lib/compiler/ebin/v3_life.beam
index cbfc1f4f82..23a894f62a 100644
--- a/bootstrap/lib/compiler/ebin/v3_life.beam
+++ b/bootstrap/lib/compiler/ebin/v3_life.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application.beam b/bootstrap/lib/kernel/ebin/application.beam
index 8746279ae8..d1d381e09b 100644
--- a/bootstrap/lib/kernel/ebin/application.beam
+++ b/bootstrap/lib/kernel/ebin/application.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam
index cccab712e7..b3674c2cd5 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 94b902f08b..6253ba71e4 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 996accedea..851951db38 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/auth.beam b/bootstrap/lib/kernel/ebin/auth.beam
index 0daa7ca83a..4d7652d9a9 100644
--- a/bootstrap/lib/kernel/ebin/auth.beam
+++ b/bootstrap/lib/kernel/ebin/auth.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code.beam b/bootstrap/lib/kernel/ebin/code.beam
index f197c8a5b5..026dccb205 100644
--- a/bootstrap/lib/kernel/ebin/code.beam
+++ b/bootstrap/lib/kernel/ebin/code.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index b6e8faf492..26bfba1e62 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 1a15e400c5..1bf6220227 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 5efe11b918..3a281e9f5a 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/disk_log_server.beam b/bootstrap/lib/kernel/ebin/disk_log_server.beam
index e212e82d15..58fe6f7f39 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_server.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_sup.beam b/bootstrap/lib/kernel/ebin/disk_log_sup.beam
index 939e6f4d3d..2faecea6bd 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_sup.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_sup.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_ac.beam b/bootstrap/lib/kernel/ebin/dist_ac.beam
index 61f988cb71..2c08e04eb7 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 00d4d5198c..5f21527ec9 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 e19853bb22..bd838b7c7f 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/erl_ddll.beam b/bootstrap/lib/kernel/ebin/erl_ddll.beam
index c464b92096..13aeb8fecb 100644
--- a/bootstrap/lib/kernel/ebin/erl_ddll.beam
+++ b/bootstrap/lib/kernel/ebin/erl_ddll.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_distribution.beam b/bootstrap/lib/kernel/ebin/erl_distribution.beam
index 1bd3c7111c..4b304c3678 100644
--- a/bootstrap/lib/kernel/ebin/erl_distribution.beam
+++ b/bootstrap/lib/kernel/ebin/erl_distribution.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_epmd.beam b/bootstrap/lib/kernel/ebin/erl_epmd.beam
index 8af37f8bf4..1025a920b2 100644
--- a/bootstrap/lib/kernel/ebin/erl_epmd.beam
+++ b/bootstrap/lib/kernel/ebin/erl_epmd.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_reply.beam b/bootstrap/lib/kernel/ebin/erl_reply.beam
index 24ee703ca2..527052b53f 100644
--- a/bootstrap/lib/kernel/ebin/erl_reply.beam
+++ b/bootstrap/lib/kernel/ebin/erl_reply.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_signal_handler.beam b/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
index c5b85d9b69..2bc5d929b3 100644
--- a/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
+++ b/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_handler.beam b/bootstrap/lib/kernel/ebin/error_handler.beam
index f2df2a189e..2249d2c98c 100644
--- a/bootstrap/lib/kernel/ebin/error_handler.beam
+++ b/bootstrap/lib/kernel/ebin/error_handler.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_logger.beam b/bootstrap/lib/kernel/ebin/error_logger.beam
index a0a8f8b121..0de0bc3ab3 100644
--- a/bootstrap/lib/kernel/ebin/error_logger.beam
+++ b/bootstrap/lib/kernel/ebin/error_logger.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erts_debug.beam b/bootstrap/lib/kernel/ebin/erts_debug.beam
index 3c77d5a20f..ac33275203 100644
--- a/bootstrap/lib/kernel/ebin/erts_debug.beam
+++ b/bootstrap/lib/kernel/ebin/erts_debug.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file.beam b/bootstrap/lib/kernel/ebin/file.beam
index 4a50f9de78..2575312622 100644
--- a/bootstrap/lib/kernel/ebin/file.beam
+++ b/bootstrap/lib/kernel/ebin/file.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 7245ec2695..9b7a33bd36 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/file_server.beam b/bootstrap/lib/kernel/ebin/file_server.beam
index ace5489a3c..b58c1a2710 100644
--- a/bootstrap/lib/kernel/ebin/file_server.beam
+++ b/bootstrap/lib/kernel/ebin/file_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_sctp.beam b/bootstrap/lib/kernel/ebin/gen_sctp.beam
index 3fa0409227..8478258754 100644
--- a/bootstrap/lib/kernel/ebin/gen_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_tcp.beam b/bootstrap/lib/kernel/ebin/gen_tcp.beam
index c154687a9b..1356853176 100644
--- a/bootstrap/lib/kernel/ebin/gen_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_udp.beam b/bootstrap/lib/kernel/ebin/gen_udp.beam
index 447d4fdc49..7a79ecb6b4 100644
--- a/bootstrap/lib/kernel/ebin/gen_udp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam
index 98c21a15f3..db8034ffbe 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 2e1fc5d542..fe618952d7 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/global_search.beam b/bootstrap/lib/kernel/ebin/global_search.beam
index ae2a767188..4fc4d2414b 100644
--- a/bootstrap/lib/kernel/ebin/global_search.beam
+++ b/bootstrap/lib/kernel/ebin/global_search.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/group.beam b/bootstrap/lib/kernel/ebin/group.beam
index 8bf4af542f..53546fb74c 100644
--- a/bootstrap/lib/kernel/ebin/group.beam
+++ b/bootstrap/lib/kernel/ebin/group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/group_history.beam b/bootstrap/lib/kernel/ebin/group_history.beam
new file mode 100644
index 0000000000..51855d33ed
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/group_history.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/heart.beam b/bootstrap/lib/kernel/ebin/heart.beam
index 6eadb43fe4..104956f663 100644
--- a/bootstrap/lib/kernel/ebin/heart.beam
+++ b/bootstrap/lib/kernel/ebin/heart.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 6e35a4f5cf..44d14383ec 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.beam b/bootstrap/lib/kernel/ebin/inet.beam
index 473d6154da..eb0d71d97f 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_sctp.beam b/bootstrap/lib/kernel/ebin/inet6_sctp.beam
index 2199828859..079b21b6e5 100644
--- a/bootstrap/lib/kernel/ebin/inet6_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp.beam b/bootstrap/lib/kernel/ebin/inet6_tcp.beam
index 5a5bbfea43..0ce20bc589 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
index af6ce5f042..dd4c54cee6 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_udp.beam b/bootstrap/lib/kernel/ebin/inet6_udp.beam
index 0563b5db26..092cae80f2 100644
--- a/bootstrap/lib/kernel/ebin/inet6_udp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_config.beam b/bootstrap/lib/kernel/ebin/inet_config.beam
index d9482ebcb0..4b6899be8a 100644
--- a/bootstrap/lib/kernel/ebin/inet_config.beam
+++ b/bootstrap/lib/kernel/ebin/inet_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_db.beam b/bootstrap/lib/kernel/ebin/inet_db.beam
index dc505f54da..2b9990aea5 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 d3872cebbd..6bee965f89 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 883cfe6cdf..ea2bdf2a26 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_hosts.beam b/bootstrap/lib/kernel/ebin/inet_hosts.beam
index 4d9377b124..d607df4f2f 100644
--- a/bootstrap/lib/kernel/ebin/inet_hosts.beam
+++ b/bootstrap/lib/kernel/ebin/inet_hosts.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_parse.beam b/bootstrap/lib/kernel/ebin/inet_parse.beam
index f78e30cbb0..595a23397e 100644
--- a/bootstrap/lib/kernel/ebin/inet_parse.beam
+++ b/bootstrap/lib/kernel/ebin/inet_parse.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_res.beam b/bootstrap/lib/kernel/ebin/inet_res.beam
index 3147ff5dbc..59c641f85c 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/inet_sctp.beam b/bootstrap/lib/kernel/ebin/inet_sctp.beam
index 4eb47f1f50..5128048f88 100644
--- a/bootstrap/lib/kernel/ebin/inet_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp.beam b/bootstrap/lib/kernel/ebin/inet_tcp.beam
index 48e3986b0b..ebbbb55972 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
index 666b2b5beb..13237c1421 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_udp.beam b/bootstrap/lib/kernel/ebin/inet_udp.beam
index 804ee860dd..153456e968 100644
--- a/bootstrap/lib/kernel/ebin/inet_udp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app
index af771b205c..c499734bb1 100644
--- a/bootstrap/lib/kernel/ebin/kernel.app
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -44,6 +44,7 @@
global_group,
global_search,
group,
+ group_history,
heart,
hipe_unified_loader,
inet6_tcp,
diff --git a/bootstrap/lib/kernel/ebin/kernel.beam b/bootstrap/lib/kernel/ebin/kernel.beam
index 6bb295a61a..d4170b3652 100644
--- a/bootstrap/lib/kernel/ebin/kernel.beam
+++ b/bootstrap/lib/kernel/ebin/kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel_config.beam b/bootstrap/lib/kernel/ebin/kernel_config.beam
index 90b62dd556..ddd47e7ad8 100644
--- a/bootstrap/lib/kernel/ebin/kernel_config.beam
+++ b/bootstrap/lib/kernel/ebin/kernel_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/local_tcp.beam b/bootstrap/lib/kernel/ebin/local_tcp.beam
index f3b3291ad1..7d4e10d93f 100644
--- a/bootstrap/lib/kernel/ebin/local_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/local_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/local_udp.beam b/bootstrap/lib/kernel/ebin/local_udp.beam
index 0ddae3a57a..798eeeb7f6 100644
--- a/bootstrap/lib/kernel/ebin/local_udp.beam
+++ b/bootstrap/lib/kernel/ebin/local_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net.beam b/bootstrap/lib/kernel/ebin/net.beam
index a02b17cae2..772535169c 100644
--- a/bootstrap/lib/kernel/ebin/net.beam
+++ b/bootstrap/lib/kernel/ebin/net.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_adm.beam b/bootstrap/lib/kernel/ebin/net_adm.beam
index 6bb14d425d..94d6a5ce97 100644
--- a/bootstrap/lib/kernel/ebin/net_adm.beam
+++ b/bootstrap/lib/kernel/ebin/net_adm.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_kernel.beam b/bootstrap/lib/kernel/ebin/net_kernel.beam
index 7ca6040b70..d78e9b6c05 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 fb65ca94e8..2c4a0caa9d 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/pg2.beam b/bootstrap/lib/kernel/ebin/pg2.beam
index 1de40824fd..2d219d03d5 100644
--- a/bootstrap/lib/kernel/ebin/pg2.beam
+++ b/bootstrap/lib/kernel/ebin/pg2.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/ram_file.beam b/bootstrap/lib/kernel/ebin/ram_file.beam
index 0dc6ad73e0..1b23e89143 100644
--- a/bootstrap/lib/kernel/ebin/ram_file.beam
+++ b/bootstrap/lib/kernel/ebin/ram_file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam
index 7578e601ff..c73b59779b 100644
--- a/bootstrap/lib/kernel/ebin/rpc.beam
+++ b/bootstrap/lib/kernel/ebin/rpc.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/seq_trace.beam b/bootstrap/lib/kernel/ebin/seq_trace.beam
index 5ba162831d..88406b3e2e 100644
--- a/bootstrap/lib/kernel/ebin/seq_trace.beam
+++ b/bootstrap/lib/kernel/ebin/seq_trace.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/standard_error.beam b/bootstrap/lib/kernel/ebin/standard_error.beam
index 3c4cba4edd..f0860d3ae8 100644
--- a/bootstrap/lib/kernel/ebin/standard_error.beam
+++ b/bootstrap/lib/kernel/ebin/standard_error.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user.beam b/bootstrap/lib/kernel/ebin/user.beam
index 6d59ec7971..73be8706a4 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/user_drv.beam b/bootstrap/lib/kernel/ebin/user_drv.beam
index 615bd1bb15..ca29be601b 100644
--- a/bootstrap/lib/kernel/ebin/user_drv.beam
+++ b/bootstrap/lib/kernel/ebin/user_drv.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user_sup.beam b/bootstrap/lib/kernel/ebin/user_sup.beam
index 7277b79a76..c81217476b 100644
--- a/bootstrap/lib/kernel/ebin/user_sup.beam
+++ b/bootstrap/lib/kernel/ebin/user_sup.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 f1486678f0..dc30a0e212 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/kernel/include/inet.hrl b/bootstrap/lib/kernel/include/inet.hrl
index df788aca08..daa2e14b46 100644
--- a/bootstrap/lib/kernel/include/inet.hrl
+++ b/bootstrap/lib/kernel/include/inet.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. 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.
diff --git a/bootstrap/lib/stdlib/ebin/array.beam b/bootstrap/lib/stdlib/ebin/array.beam
index 6992a24d98..e9b416b68b 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/base64.beam b/bootstrap/lib/stdlib/ebin/base64.beam
index f89e3be956..8c42e9f22d 100644
--- a/bootstrap/lib/stdlib/ebin/base64.beam
+++ b/bootstrap/lib/stdlib/ebin/base64.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/beam_lib.beam b/bootstrap/lib/stdlib/ebin/beam_lib.beam
index 4c82a8a6c5..8cc54cf7b3 100644
--- a/bootstrap/lib/stdlib/ebin/beam_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/beam_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/binary.beam b/bootstrap/lib/stdlib/ebin/binary.beam
index 0863c4cf8d..ec03caa782 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/c.beam b/bootstrap/lib/stdlib/ebin/c.beam
index 5a69b635cb..541ae0e54a 100644
--- a/bootstrap/lib/stdlib/ebin/c.beam
+++ b/bootstrap/lib/stdlib/ebin/c.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/calendar.beam b/bootstrap/lib/stdlib/ebin/calendar.beam
index ca9e73c245..5ba087e5e9 100644
--- a/bootstrap/lib/stdlib/ebin/calendar.beam
+++ b/bootstrap/lib/stdlib/ebin/calendar.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam
index 586b0c35cd..10675b4bf2 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 de315c0517..2019fda39d 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_sup.beam b/bootstrap/lib/stdlib/ebin/dets_sup.beam
index 34732d5a99..05da1822d3 100644
--- a/bootstrap/lib/stdlib/ebin/dets_sup.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_sup.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_utils.beam b/bootstrap/lib/stdlib/ebin/dets_utils.beam
index 576992c128..0d90896c26 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 55e37c99a4..273211cf78 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/dict.beam b/bootstrap/lib/stdlib/ebin/dict.beam
index 0c38ee1717..9bd8311a9b 100644
--- a/bootstrap/lib/stdlib/ebin/dict.beam
+++ b/bootstrap/lib/stdlib/ebin/dict.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/digraph.beam b/bootstrap/lib/stdlib/ebin/digraph.beam
index 7b1ce6b4ce..237a5d3b30 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/digraph_utils.beam b/bootstrap/lib/stdlib/ebin/digraph_utils.beam
index 2b82c5f2d9..29e26659ce 100644
--- a/bootstrap/lib/stdlib/ebin/digraph_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/digraph_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin.beam b/bootstrap/lib/stdlib/ebin/edlin.beam
index dbc9a668b0..40dd9561c4 100644
--- a/bootstrap/lib/stdlib/ebin/edlin.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin_expand.beam b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
index f79a4044e6..591f6ab195 100644
--- a/bootstrap/lib/stdlib/ebin/edlin_expand.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index 5fc1d337bb..990cc1ab26 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_abstract_code.beam b/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
index 83f5ceac01..c98a937bc0 100644
--- a/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_anno.beam b/bootstrap/lib/stdlib/ebin/erl_anno.beam
index 212fe27e28..4dae00603f 100644
--- a/bootstrap/lib/stdlib/ebin/erl_anno.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_anno.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_bits.beam b/bootstrap/lib/stdlib/ebin/erl_bits.beam
index 794162a95e..c0dc007589 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_compile.beam b/bootstrap/lib/stdlib/ebin/erl_compile.beam
index 07f78c8599..be7381012b 100644
--- a/bootstrap/lib/stdlib/ebin/erl_compile.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_compile.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam
index a15d6d1b39..faf992ee0b 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 e38a940352..ab6f576b81 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_internal.beam b/bootstrap/lib/stdlib/ebin/erl_internal.beam
index 7f4519c7b7..bcf53a392a 100644
--- a/bootstrap/lib/stdlib/ebin/erl_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index c839164faf..98477b5d8f 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 ad45f99496..a2a236fe7d 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_posix_msg.beam b/bootstrap/lib/stdlib/ebin/erl_posix_msg.beam
index d2be4727e1..c6a52797f3 100644
--- a/bootstrap/lib/stdlib/ebin/erl_posix_msg.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_posix_msg.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_pp.beam b/bootstrap/lib/stdlib/ebin/erl_pp.beam
index 19e5fb932b..fc65556a86 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_scan.beam b/bootstrap/lib/stdlib/ebin/erl_scan.beam
index 608d8c2788..db02f2f493 100644
--- a/bootstrap/lib/stdlib/ebin/erl_scan.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_scan.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_tar.beam b/bootstrap/lib/stdlib/ebin/erl_tar.beam
index bcb8a3aa72..81adab4fad 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/error_logger_file_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
index 78dfc523b2..63e350df89 100644
--- a/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
+++ b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
index 9c913ce5fb..77a5216e39 100644
--- a/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
+++ b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/escript.beam b/bootstrap/lib/stdlib/ebin/escript.beam
index 1411e99456..ac03b99ee4 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 935a132190..1b3fc9c9cd 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/eval_bits.beam b/bootstrap/lib/stdlib/ebin/eval_bits.beam
index ecfedab133..251c1946ff 100644
--- a/bootstrap/lib/stdlib/ebin/eval_bits.beam
+++ b/bootstrap/lib/stdlib/ebin/eval_bits.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/file_sorter.beam b/bootstrap/lib/stdlib/ebin/file_sorter.beam
index 182eb8d648..d6bab6459e 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/filelib.beam b/bootstrap/lib/stdlib/ebin/filelib.beam
index 733b60a71d..249e7a3410 100644
--- a/bootstrap/lib/stdlib/ebin/filelib.beam
+++ b/bootstrap/lib/stdlib/ebin/filelib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/filename.beam b/bootstrap/lib/stdlib/ebin/filename.beam
index 516ec1fcdd..85d0c53a53 100644
--- a/bootstrap/lib/stdlib/ebin/filename.beam
+++ b/bootstrap/lib/stdlib/ebin/filename.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_sets.beam b/bootstrap/lib/stdlib/ebin/gb_sets.beam
index 2e1a95b10c..18acb2891c 100644
--- a/bootstrap/lib/stdlib/ebin/gb_sets.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_trees.beam b/bootstrap/lib/stdlib/ebin/gb_trees.beam
index ea9d7c7a66..6f5368d1d6 100644
--- a/bootstrap/lib/stdlib/ebin/gb_trees.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_trees.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen.beam b/bootstrap/lib/stdlib/ebin/gen.beam
index 8f36971bb5..c500cb1af1 100644
--- a/bootstrap/lib/stdlib/ebin/gen.beam
+++ b/bootstrap/lib/stdlib/ebin/gen.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_event.beam b/bootstrap/lib/stdlib/ebin/gen_event.beam
index aa01da81eb..aeb8aaa42f 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_fsm.beam b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
index 73eff4700f..404ec6735b 100644
--- a/bootstrap/lib/stdlib/ebin/gen_fsm.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_server.beam b/bootstrap/lib/stdlib/ebin/gen_server.beam
index f4fd0c415c..cc30bcbbb8 100644
--- a/bootstrap/lib/stdlib/ebin/gen_server.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_statem.beam b/bootstrap/lib/stdlib/ebin/gen_statem.beam
index 09cf9cbd50..847042c84a 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 5ac3d1f069..d774ba98a4 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.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam
index 4ab1fc644a..be6c16582a 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_format.beam b/bootstrap/lib/stdlib/ebin/io_lib_format.beam
index 27ea37e332..58a6b6fb1f 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_format.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_format.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_fread.beam b/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
index 78afdd522c..0fcefbd620 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_fread.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 b0756eecdf..9f00efb00a 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 158154cc15..8fe032f818 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/lists.beam b/bootstrap/lib/stdlib/ebin/lists.beam
index 3331a5808b..5fd6f76a23 100644
--- a/bootstrap/lib/stdlib/ebin/lists.beam
+++ b/bootstrap/lib/stdlib/ebin/lists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/log_mf_h.beam b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
index f305ba99f5..fbc8c8853e 100644
--- a/bootstrap/lib/stdlib/ebin/log_mf_h.beam
+++ b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/maps.beam b/bootstrap/lib/stdlib/ebin/maps.beam
index 3a0fa8ec1d..abb18a4e36 100644
--- a/bootstrap/lib/stdlib/ebin/maps.beam
+++ b/bootstrap/lib/stdlib/ebin/maps.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/math.beam b/bootstrap/lib/stdlib/ebin/math.beam
index de17278918..aaa6cb3e4d 100644
--- a/bootstrap/lib/stdlib/ebin/math.beam
+++ b/bootstrap/lib/stdlib/ebin/math.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam
index 1214b3591b..377d02bf80 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/orddict.beam b/bootstrap/lib/stdlib/ebin/orddict.beam
index 1fc4685614..f6f6627291 100644
--- a/bootstrap/lib/stdlib/ebin/orddict.beam
+++ b/bootstrap/lib/stdlib/ebin/orddict.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ordsets.beam b/bootstrap/lib/stdlib/ebin/ordsets.beam
index 736605b90f..2797b73ee4 100644
--- a/bootstrap/lib/stdlib/ebin/ordsets.beam
+++ b/bootstrap/lib/stdlib/ebin/ordsets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/otp_internal.beam b/bootstrap/lib/stdlib/ebin/otp_internal.beam
index e71354d8d4..76430a9d24 100644
--- a/bootstrap/lib/stdlib/ebin/otp_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/otp_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/pool.beam b/bootstrap/lib/stdlib/ebin/pool.beam
index da31679d1f..2bd926690a 100644
--- a/bootstrap/lib/stdlib/ebin/pool.beam
+++ b/bootstrap/lib/stdlib/ebin/pool.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/proc_lib.beam b/bootstrap/lib/stdlib/ebin/proc_lib.beam
index 25eb3b652d..934c3d96a1 100644
--- a/bootstrap/lib/stdlib/ebin/proc_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/proc_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/proplists.beam b/bootstrap/lib/stdlib/ebin/proplists.beam
index d0496eb80f..cdc79d74ea 100644
--- a/bootstrap/lib/stdlib/ebin/proplists.beam
+++ b/bootstrap/lib/stdlib/ebin/proplists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam
index e2428a9559..b5689663ac 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 74f933fb47..bf321e4047 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/queue.beam b/bootstrap/lib/stdlib/ebin/queue.beam
index 5a2cd23b83..95402c8d48 100644
--- a/bootstrap/lib/stdlib/ebin/queue.beam
+++ b/bootstrap/lib/stdlib/ebin/queue.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/rand.beam b/bootstrap/lib/stdlib/ebin/rand.beam
index c8a6a97976..3129fa7aa9 100644
--- a/bootstrap/lib/stdlib/ebin/rand.beam
+++ b/bootstrap/lib/stdlib/ebin/rand.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/random.beam b/bootstrap/lib/stdlib/ebin/random.beam
index 0073fc5281..42ba923e19 100644
--- a/bootstrap/lib/stdlib/ebin/random.beam
+++ b/bootstrap/lib/stdlib/ebin/random.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/re.beam b/bootstrap/lib/stdlib/ebin/re.beam
index 7de890492f..0d493e2a5c 100644
--- a/bootstrap/lib/stdlib/ebin/re.beam
+++ b/bootstrap/lib/stdlib/ebin/re.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sets.beam b/bootstrap/lib/stdlib/ebin/sets.beam
index 2cc06fb919..eb7eb95f4e 100644
--- a/bootstrap/lib/stdlib/ebin/sets.beam
+++ b/bootstrap/lib/stdlib/ebin/sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell.beam b/bootstrap/lib/stdlib/ebin/shell.beam
index 231440311b..ff0fd46dc8 100644
--- a/bootstrap/lib/stdlib/ebin/shell.beam
+++ b/bootstrap/lib/stdlib/ebin/shell.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell_default.beam b/bootstrap/lib/stdlib/ebin/shell_default.beam
index 0d4e21e544..d13255b49d 100644
--- a/bootstrap/lib/stdlib/ebin/shell_default.beam
+++ b/bootstrap/lib/stdlib/ebin/shell_default.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/slave.beam b/bootstrap/lib/stdlib/ebin/slave.beam
index cb606a0736..b976a1fd19 100644
--- a/bootstrap/lib/stdlib/ebin/slave.beam
+++ b/bootstrap/lib/stdlib/ebin/slave.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sofs.beam b/bootstrap/lib/stdlib/ebin/sofs.beam
index 8ef3635454..7d45fbc2ad 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/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
index ed73fa6985..33e8de4d90 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.app
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
diff --git a/bootstrap/lib/stdlib/ebin/string.beam b/bootstrap/lib/stdlib/ebin/string.beam
index 65fb114b50..5d48ed2bcd 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 e11d7c9375..7833f18605 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/supervisor_bridge.beam b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
index a6019f18af..714f92dc7c 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sys.beam b/bootstrap/lib/stdlib/ebin/sys.beam
index de5443003f..37dceb7660 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/timer.beam b/bootstrap/lib/stdlib/ebin/timer.beam
index a1803b06b5..24145e81d6 100644
--- a/bootstrap/lib/stdlib/ebin/timer.beam
+++ b/bootstrap/lib/stdlib/ebin/timer.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/unicode.beam b/bootstrap/lib/stdlib/ebin/unicode.beam
index d58ad7564e..359e1b9fdf 100644
--- a/bootstrap/lib/stdlib/ebin/unicode.beam
+++ b/bootstrap/lib/stdlib/ebin/unicode.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/unicode_util.beam b/bootstrap/lib/stdlib/ebin/unicode_util.beam
index 3251926a9c..7771cb469c 100644
--- a/bootstrap/lib/stdlib/ebin/unicode_util.beam
+++ b/bootstrap/lib/stdlib/ebin/unicode_util.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/win32reg.beam b/bootstrap/lib/stdlib/ebin/win32reg.beam
index 289ca96f59..c5369b13c3 100644
--- a/bootstrap/lib/stdlib/ebin/win32reg.beam
+++ b/bootstrap/lib/stdlib/ebin/win32reg.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam
index 09f873d8fa..032df45451 100644
--- a/bootstrap/lib/stdlib/ebin/zip.beam
+++ b/bootstrap/lib/stdlib/ebin/zip.beam
Binary files differ
diff --git a/erts/Makefile b/erts/Makefile
index e9928b6b47..12d2ec57a8 100644
--- a/erts/Makefile
+++ b/erts/Makefile
@@ -40,8 +40,8 @@ all: $(FLAVORS)
docs:
$(V_at)( cd doc/src && $(MAKE) $@ )
-.PHONY: debug opt clean
-debug opt clean:
+.PHONY: debug opt lcnt clean
+debug opt lcnt clean:
$(V_at)for d in emulator $(ERTSDIRS); do \
if test -d $$d; then \
( cd $$d && $(MAKE) $@ FLAVOR=$(FLAVOR) ) || exit $$? ; \
@@ -56,7 +56,9 @@ debug opt clean:
.PHONY: $(FLAVORS)
$(FLAVORS):
- $(V_at)( $(MAKE) opt FLAVOR=$@ )
+ $(V_at)for type in $(TYPES); do \
+ ( $(MAKE) $$type FLAVOR=$@ ); \
+ done
# Make erl script and erlc in $(ERL_TOP)/bin which runs the compiled version
# Note that erlc is not a script and requires extra handling on cygwin.
@@ -128,7 +130,9 @@ makefiles:
.PHONY: release
release:
$(V_at)for f in $(FLAVORS); do \
- ( cd emulator && $(MAKE) release FLAVOR=$$f ) \
+ for t in $(TYPES); do \
+ ( cd emulator && $(MAKE) release FLAVOR=$$f TYPE=$$t ) \
+ done \
done
$(V_at)for d in $(ERTSDIRS) $(XINSTDIRS); do \
if test -d $$d; then \
diff --git a/erts/configure.in b/erts/configure.in
index c82ebaa74c..830e3d7776 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -1002,6 +1002,7 @@ else
fi
FLAVORS=
+TYPES=opt
ERTS_BUILD_SMP_EMU=$enable_smp_support
AC_MSG_CHECKING(whether an emulator with smp support should be built)
case $ERTS_BUILD_SMP_EMU in
@@ -1210,6 +1211,7 @@ esac
AC_SUBST(ERTS_BUILD_PLAIN_EMU)
AC_SUBST(FLAVORS)
+AC_SUBST(TYPES)
case "$ERTS_BUILD_PLAIN_EMU-$ERTS_BUILD_SMP_EMU" in
no-no)
@@ -1365,7 +1367,7 @@ else
AC_MSG_CHECKING(whether lock counters should be enabled)
AC_MSG_RESULT($enable_lock_count)
if test "x$enable_lock_count" != "xno"; then
- EMU_THR_DEFS="$EMU_THR_DEFS -DERTS_ENABLE_LOCK_COUNT"
+ TYPES="$TYPES lcnt"
fi
case $host_os in
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 6046e94599..e1aa5ce76e 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -239,6 +239,13 @@
<item>
<p>Useful for debugging. Prints the arguments sent to the emulator.</p>
</item>
+ <tag><c><![CDATA[-emu_type Type]]></c></tag>
+ <item>
+ <p>Start an emulator of a different type. For example, to start
+ the lock-counter emualator, use <c>-emu_type lcnt</c>. (The emulator
+ must already be built. Use the <c>configure</c> option
+ <c>--enable-lock-counter</c> to build the lock-counter emulator.)</p>
+ </item>
<tag><c><![CDATA[-env Variable Value]]></c></tag>
<item>
<p>Sets the host OS environment variable <c><![CDATA[Variable]]></c> to
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index e93e9a2658..d9cc5ef936 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -1813,8 +1813,9 @@ true</pre>
<fsummary>Get the call stack back-trace of the last exception.</fsummary>
<type name="stack_item"/>
<desc>
- <p>Gets the call stack back-trace (<em>stacktrace</em>) of the
- last exception in the calling process as a list of
+ <p>Gets the call stack back-trace (<em>stacktrace</em>) for an
+ exception that has just been caught
+ in the calling process as a list of
<c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c>
tuples. Field <c><anno>Arity</anno></c> in the first tuple can be the
argument list of that function call instead of an arity integer,
@@ -1822,6 +1823,29 @@ true</pre>
<p>If there has not been any exceptions in a process, the
stacktrace is <c>[]</c>. After a code change for the process,
the stacktrace can also be reset to <c>[]</c>.</p>
+ <p><c>erlang:get_stacktrace/0</c> is only guaranteed to return
+ a stacktrace if called (directly or indirectly) from within the
+ scope of a <c>try</c> expression. That is, the following call works:</p>
+<pre>
+try Expr
+catch
+ C:R ->
+ {C,R,erlang:get_stacktrace()}
+end</pre>
+ <p>As does this call:</p>
+<pre>
+try Expr
+catch
+ C:R ->
+ {C,R,helper()}
+end
+
+helper() ->
+ erlang:get_stacktrace().</pre>
+
+ <warning><p>In a future release,
+ <c>erlang:get_stacktrace/0</c> will return <c>[]</c> if called
+ from outside a <c>try</c> expression.</p></warning>
<p>The stacktrace is the same data as operator <c>catch</c>
returns, for example:</p>
<pre>
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index a90e6a0ba8..79d751d13e 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -158,7 +158,9 @@ do { \
/*
* Register target (X or Y register).
*/
-#define REG_TARGET(Target) (*(((Target) & 1) ? &yb(Target-1) : &xb(Target)))
+
+#define REG_TARGET_PTR(Target) (((Target) & 1) ? &yb(Target-1) : &xb(Target))
+#define REG_TARGET(Target) (*REG_TARGET_PTR(Target))
/*
* Store a result into a register given a destination descriptor.
@@ -172,8 +174,6 @@ do { \
REG_TARGET(stb_reg) = (Result); \
} while (0)
-#define StoreSimpleDest(Src, Dest) Dest = (Src)
-
/*
* Store a result into a register and execute the next instruction.
* Dst points to the word with a destination descriptor, which MUST
@@ -420,7 +420,7 @@ void** beam_ops;
#define TestHeapPutList(Need, Reg) \
do { \
TestHeap((Need), 1); \
- PutList(Reg, r(0), r(0), StoreSimpleDest); \
+ PutList(Reg, r(0), r(0)); \
CHECK_TERM(r(0)); \
} while (0)
@@ -547,11 +547,11 @@ void** beam_ops;
GetR((N)+1, Dst2); \
} while (0)
-#define PutList(H, T, Dst, Store) \
- do { \
- HTOP[0] = (H); HTOP[1] = (T); \
- Store(make_list(HTOP), Dst); \
- HTOP += 2; \
+#define PutList(H, T, Dst) \
+ do { \
+ HTOP[0] = (H); HTOP[1] = (T); \
+ Dst = make_list(HTOP); \
+ HTOP += 2; \
} while (0)
#define Swap(R1, R2) \
@@ -568,11 +568,7 @@ void** beam_ops;
R2 = Tmp = V; \
} while (0)
-#define Move(Src, Dst, Store) \
- do { \
- Eterm term = (Src); \
- Store(term, Dst); \
- } while (0)
+#define Move(Src, Dst) Dst = (Src)
#define Move2Par(S1, D1, S2, D2) \
do { \
@@ -911,7 +907,7 @@ do { \
Target = _uint_size * Unit; \
} while (0)
-#define BsGetFloat2(Ms, Live, Sz, Flags, Dst, Store, Fail) \
+#define BsGetFloat2(Ms, Live, Sz, Flags, Dst, Fail) \
do { \
ErlBinMatchBuffer *_mb; \
Eterm _result; Sint _size; \
@@ -922,12 +918,12 @@ do { \
LIGHT_SWAPOUT; \
_result = erts_bs_get_float_2(c_p, _size, (Flags), _mb); \
LIGHT_SWAPIN; \
- HEAP_SPACE_VERIFIED(0); \
+ HEAP_SPACE_VERIFIED(0); \
if (is_non_value(_result)) { Fail; } \
- else { Store(_result, Dst); } \
+ else { Dst = _result; } \
} while (0)
-#define BsGetBinaryImm_2(Ms, Live, Sz, Flags, Dst, Store, Fail) \
+#define BsGetBinaryImm_2(Ms, Live, Sz, Flags, Dst, Fail) \
do { \
ErlBinMatchBuffer *_mb; \
Eterm _result; \
@@ -936,12 +932,12 @@ do { \
LIGHT_SWAPOUT; \
_result = erts_bs_get_binary_2(c_p, (Sz), (Flags), _mb); \
LIGHT_SWAPIN; \
- HEAP_SPACE_VERIFIED(0); \
+ HEAP_SPACE_VERIFIED(0); \
if (is_non_value(_result)) { Fail; } \
- else { Store(_result, Dst); } \
+ else { Dst = _result; } \
} while (0)
-#define BsGetBinary_2(Ms, Live, Sz, Flags, Dst, Store, Fail) \
+#define BsGetBinary_2(Ms, Live, Sz, Flags, Dst, Fail) \
do { \
ErlBinMatchBuffer *_mb; \
Eterm _result; Uint _size; \
@@ -951,27 +947,27 @@ do { \
LIGHT_SWAPOUT; \
_result = erts_bs_get_binary_2(c_p, _size, (Flags), _mb); \
LIGHT_SWAPIN; \
- HEAP_SPACE_VERIFIED(0); \
+ HEAP_SPACE_VERIFIED(0); \
if (is_non_value(_result)) { Fail; } \
- else { Store(_result, Dst); } \
+ else { Dst = _result; } \
} while (0)
-#define BsGetBinaryAll_2(Ms, Live, Unit, Dst, Store, Fail) \
- do { \
- ErlBinMatchBuffer *_mb; \
- Eterm _result; \
- TestHeap(ERL_SUB_BIN_SIZE, Live); \
- _mb = ms_matchbuffer(Ms); \
- if (((_mb->size - _mb->offset) % Unit) == 0) { \
- LIGHT_SWAPOUT; \
- _result = erts_bs_get_binary_all_2(c_p, _mb); \
- LIGHT_SWAPIN; \
- HEAP_SPACE_VERIFIED(0); \
- ASSERT(is_value(_result)); \
- Store(_result, Dst); \
- } else { \
- HEAP_SPACE_VERIFIED(0); \
- Fail; } \
+#define BsGetBinaryAll_2(Ms, Live, Unit, Dst, Fail) \
+ do { \
+ ErlBinMatchBuffer *_mb; \
+ Eterm _result; \
+ TestHeap(ERL_SUB_BIN_SIZE, Live); \
+ _mb = ms_matchbuffer(Ms); \
+ if (((_mb->size - _mb->offset) % Unit) == 0) { \
+ LIGHT_SWAPOUT; \
+ _result = erts_bs_get_binary_all_2(c_p, _mb); \
+ LIGHT_SWAPIN; \
+ HEAP_SPACE_VERIFIED(0); \
+ ASSERT(is_value(_result)); \
+ Dst = _result; \
+ } else { \
+ HEAP_SPACE_VERIFIED(0); \
+ Fail; } \
} while (0)
#define BsSkipBits2(Ms, Bits, Unit, Fail) \
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 4baee7900b..1f6feade1c 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -2266,21 +2266,6 @@ Eterm big_minus(Eterm x, Eterm y, Eterm *r)
}
/*
-** Subtract a digit from big number
-*/
-Eterm big_minus_small(Eterm x, Eterm y, Eterm *r)
-{
- Eterm* xp = big_val(x);
-
- if (BIG_SIGN(xp))
- return big_norm(r, D_add(BIG_V(xp),BIG_SIZE(xp), (ErtsDigit) y, BIG_V(r)),
- (short) BIG_SIGN(xp));
- else
- return big_norm(r, D_sub(BIG_V(xp),BIG_SIZE(xp), (ErtsDigit) y, BIG_V(r)),
- (short) BIG_SIGN(xp));
-}
-
-/*
** Multiply smallnums
*/
@@ -2412,16 +2397,6 @@ Eterm big_rem(Eterm x, Eterm y, Eterm *r)
}
}
-Eterm big_neg(Eterm x, Eterm *r)
-{
- Eterm* xp = big_val(x);
- dsize_t xsz = BIG_SIZE(xp);
- short xsgn = BIG_SIGN(xp);
-
- MOVE_DIGITS(BIG_V(r), BIG_V(xp), xsz);
- return big_norm(r, xsz, (short) !xsgn);
-}
-
Eterm big_band(Eterm x, Eterm y, Eterm *r)
{
Eterm* xp = big_val(x);
diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h
index 4a96d971c3..258038a157 100644
--- a/erts/emulator/beam/big.h
+++ b/erts/emulator/beam/big.h
@@ -118,9 +118,7 @@ Eterm big_minus(Eterm, Eterm, Eterm*);
Eterm big_times(Eterm, Eterm, Eterm*);
Eterm big_div(Eterm, Eterm, Eterm*);
Eterm big_rem(Eterm, Eterm, Eterm*);
-Eterm big_neg(Eterm, Eterm*);
-Eterm big_minus_small(Eterm, Uint, Eterm*);
Eterm big_plus_small(Eterm, Uint, Eterm*);
Eterm big_times_small(Eterm, Uint, Eterm*);
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index b2c1133ded..169e1e423d 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -3512,28 +3512,6 @@ void erts_allctr_wrapper_pre_unlock(void)
}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
- * Deprecated functions *
- * *
- * These functions are still defined since "non-OTP linked in drivers" may *
- * contain (illegal) calls to them. *
-\* */
-
-/* --- DO *NOT* USE THESE FUNCTIONS --- */
-
-void *sys_alloc(Uint sz)
-{ return erts_alloc_fnf(ERTS_ALC_T_UNDEF, sz); }
-void *sys_realloc(void *ptr, Uint sz)
-{ return erts_realloc_fnf(ERTS_ALC_T_UNDEF, ptr, sz); }
-void sys_free(void *ptr)
-{ erts_free(ERTS_ALC_T_UNDEF, ptr); }
-void *safe_alloc(Uint sz)
-{ return erts_alloc(ERTS_ALC_T_UNDEF, sz); }
-void *safe_realloc(void *ptr, Uint sz)
-{ return erts_realloc(ERTS_ALC_T_UNDEF, ptr, sz); }
-
-
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* NOTE: erts_alc_test() is only supposed to be used for testing. *
* *
diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h
index 56a3b73bf9..758d529f87 100644
--- a/erts/emulator/beam/erl_alloc.h
+++ b/erts/emulator/beam/erl_alloc.h
@@ -173,13 +173,6 @@ __decl_noreturn void erts_realloc_n_enomem(ErtsAlcType_t,void*,Uint)
__decl_noreturn void erts_alc_fatal_error(int,int,ErtsAlcType_t,...)
__noreturn;
-/* --- DO *NOT* USE THESE DEPRECATED FUNCTIONS --- Instead use: */
-void *safe_alloc(Uint) __deprecated; /* erts_alloc() */
-void *safe_realloc(void *, Uint) __deprecated; /* erts_realloc() */
-void sys_free(void *) __deprecated; /* erts_free() */
-void *sys_alloc(Uint ) __deprecated; /* erts_alloc_fnf() */
-void *sys_realloc(void *, Uint) __deprecated; /* erts_realloc_fnf() */
-
#undef ERTS_HAVE_IS_IN_LITERAL_RANGE
#if defined(ARCH_32) || defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
# define ERTS_HAVE_IS_IN_LITERAL_RANGE
diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c
index 28aaeeb479..4347f9f2b7 100644
--- a/erts/emulator/beam/erl_cpu_topology.c
+++ b/erts/emulator/beam/erl_cpu_topology.c
@@ -827,17 +827,6 @@ erts_sched_bind_atfork_child(int unbind)
return 0;
}
-char *
-erts_sched_bind_atvfork_child(int unbind)
-{
- if (unbind) {
- ERTS_SMP_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&cpuinfo_rwmtx)
- || erts_lc_rwmtx_is_rwlocked(&cpuinfo_rwmtx));
- return erts_get_unbind_from_cpu_str(cpuinfo);
- }
- return "false";
-}
-
void
erts_sched_bind_atfork_parent(int unbind)
{
diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h
index 45324ac4a0..cf139d95a9 100644
--- a/erts/emulator/beam/erl_cpu_topology.h
+++ b/erts/emulator/beam/erl_cpu_topology.h
@@ -85,22 +85,14 @@ void erts_sched_bind_atthrcreate_parent(int unbind);
int erts_sched_bind_atfork_prepare(void);
int erts_sched_bind_atfork_child(int unbind);
-char *erts_sched_bind_atvfork_child(int unbind);
void erts_sched_bind_atfork_parent(int unbind);
Eterm erts_fake_scheduler_bindings(Process *p, Eterm how);
Eterm erts_debug_cpu_groups_map(Process *c_p, int groups);
-
typedef void (*erts_cpu_groups_callback_t)(int,
ErtsSchedulerData *,
int,
void *);
-void erts_add_cpu_groups(int groups,
- erts_cpu_groups_callback_t callback,
- void *arg);
-void erts_remove_cpu_groups(erts_cpu_groups_callback_t callback,
- void *arg);
-
#endif
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index eaed15b14b..13eacaa8a9 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -1301,11 +1301,6 @@ int db_match_keeps_key(int keypos, Eterm match, Eterm guard, Eterm body)
return 0;
}
-/* This is used when tracing */
-Eterm erts_match_set_lint(Process *p, Eterm matchexpr) {
- return db_match_set_lint(p, matchexpr, DCOMP_TRACE);
-}
-
Eterm db_match_set_lint(Process *p, Eterm matchexpr, Uint flags)
{
Eterm l;
@@ -2017,7 +2012,8 @@ restart:
do_catch = 0;
fail_label = -1;
build_proc = psp;
- esdp->current_process = psp;
+ if (esdp)
+ esdp->current_process = psp;
#ifdef DEBUG
ASSERT(variables == mpsp->u.variables);
@@ -2686,7 +2682,8 @@ restart:
do_catch = 1;
if (in_flags & ERTS_PAM_COPY_RESULT) {
build_proc = c_p;
- esdp->current_process = c_p;
+ if (esdp)
+ esdp->current_process = c_p;
}
break;
case matchHalt:
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index a7a8da4ed8..2ff49c97b3 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -439,7 +439,7 @@ Eterm
erts_gc_after_bif_call(Process* p, Eterm result, Eterm* regs, Uint arity)
{
return erts_gc_after_bif_call_lhf(p, ERTS_INVALID_HFRAG_PTR,
- result, regs, arity);
+ result, regs, arity);
}
static ERTS_INLINE void reset_active_writer(Process *p)
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index fb38327d02..ac99f043e6 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -309,30 +309,6 @@ void erl_error(char *fmt, va_list args)
static int early_init(int *argc, char **argv);
-void
-erts_short_init(void)
-{
-
- int ncpu;
- int time_correction;
- ErtsTimeWarpMode time_warp_mode;
-
- set_default_time_adj(&time_correction,
- &time_warp_mode);
- ncpu = early_init(NULL, NULL);
- erl_init(ncpu,
- ERTS_DEFAULT_MAX_PROCESSES,
- 0,
- ERTS_DEFAULT_MAX_PORTS,
- 0,
- 0,
- time_correction,
- time_warp_mode,
- ERTS_NODE_TAB_DELAY_GC_DEFAULT,
- ERTS_DB_SPNCNT_NORMAL);
- erts_initialized = 1;
-}
-
static void
erl_init(int ncpu,
int proc_tab_sz,
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index bbdadafade..c305732d63 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -177,8 +177,8 @@ ERL_NIF_API_FUNC_DECL(int,enif_thread_type,(void));
ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char *format, ...));
ERL_NIF_API_FUNC_DECL(int,enif_select,(ErlNifEnv* env, ErlNifEvent e, enum ErlNifSelectFlags flags, void* obj, const ErlNifPid* pid, ERL_NIF_TERM ref));
ERL_NIF_API_FUNC_DECL(ErlNifResourceType*,enif_open_resource_type_x,(ErlNifEnv*, const char* name_str, const ErlNifResourceTypeInit*, ErlNifResourceFlags flags, ErlNifResourceFlags* tried));
-ERL_NIF_API_FUNC_DECL(int, enif_monitor_process,(ErlNifEnv*,void* obj,const ErlNifPid*,ErlDrvMonitor *monitor));
-ERL_NIF_API_FUNC_DECL(int, enif_demonitor_process,(ErlNifEnv*,void* obj,const ErlDrvMonitor *monitor));
+ERL_NIF_API_FUNC_DECL(int, enif_monitor_process,(ErlNifEnv*,void* obj,const ErlNifPid*,ErlNifMonitor *monitor));
+ERL_NIF_API_FUNC_DECL(int, enif_demonitor_process,(ErlNifEnv*,void* obj,const ErlNifMonitor *monitor));
ERL_NIF_API_FUNC_DECL(int, enif_compare_monitors,(const ErlNifMonitor*,const ErlNifMonitor*));
ERL_NIF_API_FUNC_DECL(ErlNifUInt64,enif_hash,(ErlNifHash type, ERL_NIF_TERM term, ErlNifUInt64 salt));
diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h
index 206078903d..5c947ad1c0 100644
--- a/erts/emulator/beam/erl_port.h
+++ b/erts/emulator/beam/erl_port.h
@@ -988,16 +988,6 @@ typedef enum {
ERTS_PORT_OP_DONE
} ErtsPortOpResult;
-ErtsPortOpResult
-erts_schedule_proc2port_signal(Process *,
- Port *,
- Eterm,
- Eterm *,
- ErtsProc2PortSigData *,
- int,
- ErtsPortTaskHandle *,
- ErtsProc2PortSigCallback);
-
int erts_deliver_port_exit(Port *, Eterm, Eterm, int, int);
/*
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index 4836b9e2d3..55526e1d5e 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -2161,13 +2161,6 @@ begin_port_cleanup(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p)
#endif
}
-int
-erts_port_is_scheduled(Port *pp)
-{
- erts_aint32_t flags = erts_smp_atomic32_read_acqb(&pp->sched.flags);
- return (flags & (ERTS_PTS_FLG_IN_RUNQ|ERTS_PTS_FLG_EXEC)) != 0;
-}
-
#ifdef ERTS_SMP
void
diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h
index 858ddfe029..ab536c6f27 100644
--- a/erts/emulator/beam/erl_port_task.h
+++ b/erts/emulator/beam/erl_port_task.h
@@ -267,7 +267,6 @@ int erts_port_task_schedule(Eterm,
ErtsPortTaskType,
...);
void erts_port_task_free_port(Port *);
-int erts_port_is_scheduled(Port *);
ErtsProc2PortSigData *erts_port_task_alloc_p2p_sig_data(void);
ErtsProc2PortSigData *erts_port_task_alloc_p2p_sig_data_extra(size_t extra, void **extra_ptr);
void erts_port_task_free_p2p_sig_data(ErtsProc2PortSigData *sigdp);
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 6f96833b21..7952e3031d 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -1590,12 +1590,6 @@ proclist_destroy(ErtsProcList *plp)
}
ErtsProcList *
-erts_proclist_copy(ErtsProcList *plp)
-{
- return proclist_copy(plp);
-}
-
-ErtsProcList *
erts_proclist_create(Process *p)
{
return proclist_create(p);
@@ -3327,13 +3321,6 @@ thr_prgr_fin_wait(void *vssi)
static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp);
-void
-erts_interupt_aux_thread_timed(ErtsMonotonicTime timeout_time)
-{
- /* TODO only poke when needed (based on timeout_time) */
- erts_sched_poke(ERTS_SCHED_SLEEP_INFO_IX(-1));
-}
-
static void *
aux_thread(void *unused)
{
@@ -9373,17 +9360,6 @@ erts_pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
}
/*
- * Like erts_pid2proc_not_running(), but hands over the process
- * in a suspended state unless (c_p is looked up).
- */
-Process *
-erts_pid2proc_suspend(Process *c_p, ErtsProcLocks c_p_locks,
- Eterm pid, ErtsProcLocks pid_locks)
-{
- return pid2proc_not_running(c_p, c_p_locks, pid, pid_locks, 1);
-}
-
-/*
* erts_pid2proc_nropt() is normally the same as
* erts_pid2proc_not_running(). However it is only
* to be used when 'not running' is a pure optimization,
@@ -9501,21 +9477,6 @@ handle_pend_bif_async_suspend(Process *suspendee,
}
}
-#else
-
-/*
- * Non-smp version of erts_pid2proc_suspend().
- */
-Process *
-erts_pid2proc_suspend(Process *c_p, ErtsProcLocks c_p_locks,
- Eterm pid, ErtsProcLocks pid_locks)
-{
- Process *rp = erts_pid2proc(c_p, c_p_locks, pid, pid_locks);
- if (rp)
- erts_suspend(rp, pid_locks, NULL);
- return rp;
-}
-
#endif /* ERTS_SMP */
/*
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index aa88ff26e8..d44e8c252d 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1611,7 +1611,6 @@ Uint64 erts_ensure_later_proc_interval(Uint64);
Uint64 erts_step_proc_interval(void);
ErtsProcList *erts_proclist_create(Process *);
-ErtsProcList *erts_proclist_copy(ErtsProcList *);
void erts_proclist_destroy(ErtsProcList *);
ERTS_GLB_INLINE int erts_proclist_same(ErtsProcList *, Process *);
@@ -2561,10 +2560,6 @@ ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, ErtsMonotonicTime end)
}
#endif
-Process *erts_pid2proc_suspend(Process *,
- ErtsProcLocks,
- Eterm,
- ErtsProcLocks);
#ifdef ERTS_SMP
Process *erts_pid2proc_not_running(Process *,
@@ -2606,8 +2601,6 @@ extern int erts_disable_proc_not_running_opt;
void erts_smp_notify_inc_runq(ErtsRunQueue *runq);
-void erts_interupt_aux_thread_timed(ErtsMonotonicTime timeout_time);
-
#ifdef ERTS_SMP
void erts_sched_finish_poke(ErtsSchedulerSleepInfo *, erts_aint32_t);
ERTS_GLB_INLINE void erts_sched_poke(ErtsSchedulerSleepInfo *ssi);
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index e3be6a5a22..fc95535ec3 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1132,7 +1132,6 @@ extern erts_tid_t erts_main_thread;
#endif
extern int erts_compat_rel;
extern int erts_use_sender_punish;
-void erts_short_init(void);
void erl_start(int, char**);
void erts_usage(void);
Eterm erts_preloaded(Process* p);
@@ -1439,7 +1438,6 @@ do { \
#define MatchSetGetSource(MPSP) erts_match_set_get_source(MPSP)
extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA);
-Eterm erts_match_set_lint(Process *p, Eterm matchexpr);
extern void erts_match_set_release_result(Process* p);
ERTS_GLB_INLINE void erts_match_set_release_result_trace(Process* p, Eterm);
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 93a5ed4397..d25e53ada0 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -1511,7 +1511,7 @@ port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg, Port* prt)
}
-ErtsPortOpResult
+static ErtsPortOpResult
erts_schedule_proc2port_signal(Process *c_p,
Port *prt,
Eterm caller,
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 9b5bd7a749..8abe871c14 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -182,15 +182,7 @@ i_jump_on_val x f I I
i_jump_on_val y f I I
%macro: get_list GetList -pack
-get_list x x x
-get_list x x y
-get_list x y x
-get_list x y y
-
-get_list y x x
-get_list y x y
-get_list y y x
-get_list y y y
+get_list xy xy xy
# The following get_list instructions using x(0) are frequently used.
get_list r x x
@@ -218,12 +210,10 @@ set_tuple_element s d P
# Get tuple element
%macro: i_get_tuple_element GetTupleElement -pack
-i_get_tuple_element x P x
-i_get_tuple_element y P x
+i_get_tuple_element xy P x
%cold
-i_get_tuple_element x P y
-i_get_tuple_element y P y
+i_get_tuple_element xy P y
%hot
%macro: i_get_tuple_element2 GetTupleElement2 -pack
@@ -270,11 +260,7 @@ system_limit j
move C=cxy x==0 | jump Lbl => move_jump Lbl C
%macro: move_jump MoveJump -nonext
-move_jump f n
-move_jump f c
-move_jump f x
-move_jump f y
-
+move_jump f ncxy
# Movement to and from the stack is common
# Try to pack as much as we can into one instruction
@@ -326,12 +312,10 @@ swap_temp R1 R2 Tmp | line Loc | call_ext_last Live Addr D | \
is_killed(Tmp, Live) => swap R1 R2 | line Loc | call_ext_last Live Addr D
%macro: swap_temp SwapTemp -pack
-swap_temp x x x
-swap_temp x y x
+swap_temp x xy x
%macro: swap Swap -pack
-swap x x
-swap x y
+swap x xy
move Src=x D1=x | move Src=x D2=x => move_dup Src D1 D2
move Src=x SD=x | move SD=x D=x => move_dup Src SD D
@@ -381,10 +365,7 @@ move_shift x y x
move_shift x x y
%macro: move_dup MoveDup -pack
-move_dup x x x
-move_dup x x y
-move_dup y x x
-move_dup y x y
+move_dup xy x xy
%macro: move2_par Move2Par -pack
@@ -409,7 +390,7 @@ move3 x x x x x x
move S=n D=y => init D
move S=c D=y => move S x | move x D
-%macro:move Move -pack -gen_dest
+%macro:move Move -pack
move x x
move x y
move y x
@@ -483,8 +464,7 @@ i_is_ne_exact_literal f y c
is_eq_exact Lbl Y=y X=x => is_eq_exact Lbl X Y
%macro: is_eq_exact EqualExact -fail_action -pack
-is_eq_exact f x x
-is_eq_exact f x y
+is_eq_exact f x xy
is_eq_exact f s s
%macro: is_lt IsLessThan -fail_action
@@ -538,7 +518,7 @@ i_put_tuple y I
#
put_list Const=c n Dst => move Const x | put_list x n Dst
-%macro:put_list PutList -pack -gen_dest
+%macro:put_list PutList -pack
put_list x n x
put_list y n x
@@ -630,9 +610,7 @@ is_tagged_tuple Fail=f c Arity Atom => jump Fail
%macro:is_tagged_tuple IsTaggedTuple -fail_action
-is_tagged_tuple f r A a
-is_tagged_tuple f x A a
-is_tagged_tuple f y A a
+is_tagged_tuple f rxy A a
# Test tuple & arity (head)
@@ -642,21 +620,16 @@ is_tuple Fail=f S=xy | test_arity Fail=f S=xy Arity => is_tuple_of_arity Fail S
%macro:is_tuple_of_arity IsTupleOfArity -fail_action
-is_tuple_of_arity f r A
-is_tuple_of_arity f x A
-is_tuple_of_arity f y A
+is_tuple_of_arity f rxy A
%macro: is_tuple IsTuple -fail_action
-is_tuple f r
-is_tuple f x
-is_tuple f y
+is_tuple f rxy
test_arity Fail Literal=q Arity => move Literal x | test_arity Fail x Arity
test_arity Fail=f c Arity => jump Fail
%macro: test_arity IsArity -fail_action
-test_arity f x A
-test_arity f y A
+test_arity f xy A
get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \
get_tuple_element Reg=x P3 D3=x | \
@@ -681,8 +654,7 @@ is_integer Fail=f S=x | allocate Need Regs => is_integer_allocate Fail S Need Re
is_integer_allocate f x I I
%macro: is_integer IsInteger -fail_action
-is_integer f x
-is_integer f y
+is_integer f xy
is_list Fail=f n =>
is_list Fail Literal=q => move Literal x | is_list Fail x
@@ -696,8 +668,7 @@ is_list f y
is_nonempty_list Fail=f S=x | allocate Need Rs => is_nonempty_list_allocate Fail S Need Rs
%macro:is_nonempty_list_allocate IsNonemptyListAllocate -fail_action -pack
-is_nonempty_list_allocate f r I t
-is_nonempty_list_allocate f x I t
+is_nonempty_list_allocate f rx I t
is_nonempty_list F=f x==0 | test_heap I1 I2 => is_non_empty_list_test_heap F I1 I2
@@ -708,12 +679,10 @@ is_nonempty_list Fail=f S=x | get_list S D1=x D2=x => \
is_nonempty_list_get_list Fail S D1 D2
%macro: is_nonempty_list_get_list IsNonemptyListGetList -fail_action -pack
-is_nonempty_list_get_list f r x x
-is_nonempty_list_get_list f x x x
+is_nonempty_list_get_list f rx x x
%macro: is_nonempty_list IsNonemptyList -fail_action
-is_nonempty_list f x
-is_nonempty_list f y
+is_nonempty_list f xy
%macro: is_atom IsAtom -fail_action
is_atom f x
@@ -735,8 +704,7 @@ is_nil Fail=f n =>
is_nil Fail=f qia => jump Fail
%macro: is_nil IsNil -fail_action
-is_nil f x
-is_nil f y
+is_nil f xy
is_binary Fail Literal=q => move Literal x | is_binary Fail x
is_binary Fail=f c => jump Fail
@@ -784,8 +752,7 @@ is_boolean Fail=f ac => jump Fail
%cold
%macro: is_boolean IsBoolean -fail_action
-is_boolean f x
-is_boolean f y
+is_boolean f xy
%hot
is_function2 Fail=f acq Arity => jump Fail
@@ -1079,8 +1046,7 @@ i_get_hash c I d
i_get s d
%macro: self Self
-self x
-self y
+self xy
%macro: node Node
node x
@@ -1091,8 +1057,7 @@ node y
i_fast_element j x I d
i_fast_element j y I d
-i_element j x s d
-i_element j y s d
+i_element j xy s d
bif1 f b s d
bif1_body b s d
@@ -1111,8 +1076,7 @@ i_move_call c f
%macro:move_call MoveCall -arg_f -size -nonext
move_call/2
-move_call x f
-move_call y f
+move_call xy f
move S=c x==0 | call_last Ar P=f D => i_move_call_last P D S
move S x==0 | call_last Ar P=f D => move_call_last S P D
@@ -1122,8 +1086,7 @@ i_move_call_last f P c
%macro:move_call_last MoveCallLast -arg_f -nonext -pack
move_call_last/3
-move_call_last x f Q
-move_call_last y f Q
+move_call_last xy f Q
move S=c x==0 | call_only Ar P=f => i_move_call_only P S
move S=x x==0 | call_only Ar P=f => move_call_only S P
@@ -1167,8 +1130,7 @@ i_make_fun I t
%hot
%macro: is_function IsFunction -fail_action
-is_function f x
-is_function f y
+is_function f xy
is_function Fail=f c => jump Fail
func_info M F A => i_func_info u M F A
@@ -1180,8 +1142,7 @@ func_info M F A => i_func_info u M F A
%cold
bs_start_match2 Fail=f ica X Y D => jump Fail
bs_start_match2 Fail Bin X Y D => i_bs_start_match2 Bin Fail X Y D
-i_bs_start_match2 x f I I d
-i_bs_start_match2 y f I I d
+i_bs_start_match2 xy f I I d
bs_save2 Reg Index => gen_bs_save(Reg, Index)
i_bs_save2 x I
@@ -1209,9 +1170,9 @@ i_bs_get_integer_32 x f I d
bs_get_binary2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \
gen_get_binary2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
-%macro: i_bs_get_binary_imm2 BsGetBinaryImm_2 -fail_action -gen_dest
-%macro: i_bs_get_binary2 BsGetBinary_2 -fail_action -gen_dest
-%macro: i_bs_get_binary_all2 BsGetBinaryAll_2 -fail_action -gen_dest
+%macro: i_bs_get_binary_imm2 BsGetBinaryImm_2 -fail_action
+%macro: i_bs_get_binary2 BsGetBinary_2 -fail_action
+%macro: i_bs_get_binary_all2 BsGetBinaryAll_2 -fail_action
i_bs_get_binary_imm2 f x I I I d
i_bs_get_binary2 f x I s I d
@@ -1224,7 +1185,7 @@ bs_get_float2 Fail=f Ms=x Live=u Sz=s Unit=u Flags=u Dst=d => \
bs_get_float2 Fail=f Ms=x Live=u Sz=q Unit=u Flags=u Dst=d => jump Fail
-%macro: i_bs_get_float2 BsGetFloat2 -fail_action -gen_dest
+%macro: i_bs_get_float2 BsGetFloat2 -fail_action
i_bs_get_float2 f x I s I d
# Miscellanous
@@ -1236,8 +1197,7 @@ bs_skip_bits2 Fail=f Ms=x Sz=sq Unit=u Flags=u => \
i_bs_skip_bits_imm2 f x I
%macro: i_bs_skip_bits2 BsSkipBits2 -fail_action
-i_bs_skip_bits2 f x x I
-i_bs_skip_bits2 f x y I
+i_bs_skip_bits2 f x xy I
%macro: i_bs_skip_bits_all2 BsSkipBitsAll2 -fail_action
i_bs_skip_bits_all2 f x I
@@ -1302,8 +1262,7 @@ bs_init2 Fail Sz Words=u==0 Regs Flags Dst => \
bs_init2 Fail Sz Words Regs Flags Dst => \
i_bs_init_fail_heap Sz Words Fail Regs Dst
-i_bs_init_fail x j I d
-i_bs_init_fail y j I d
+i_bs_init_fail xy j I d
i_bs_init_fail_heap s I j I d
@@ -1324,8 +1283,7 @@ bs_init_bits Fail Sz Words=u==0 Regs Flags Dst => \
bs_init_bits Fail Sz Words Regs Flags Dst => \
i_bs_init_bits_fail_heap Sz Words Fail Regs Dst
-i_bs_init_bits_fail x j I d
-i_bs_init_bits_fail y j I d
+i_bs_init_bits_fail xy j I d
i_bs_init_bits_fail_heap s I j I d
@@ -1493,8 +1451,7 @@ is_map Fail Lit=q | literal_is_map(Lit) =>
is_map Fail cq => jump Fail
%macro: is_map IsMap -fail_action
-is_map f x
-is_map f y
+is_map f xy
## Transform has_map_fields #{ K1 := _, K2 := _ } to has_map_elements
@@ -1514,16 +1471,10 @@ i_get_map_element Fail Src=xy Key=y Dst => \
move Key x | i_get_map_element Fail Src x Dst
%macro: i_get_map_element_hash GetMapElementHash -fail_action
-i_get_map_element_hash f x c I x
-i_get_map_element_hash f y c I x
-i_get_map_element_hash f x c I y
-i_get_map_element_hash f y c I y
+i_get_map_element_hash f xy c I xy
%macro: i_get_map_element GetMapElement -fail_action
-i_get_map_element f x x x
-i_get_map_element f y x x
-i_get_map_element f x x y
-i_get_map_element f y x y
+i_get_map_element f xy x xy
#
# Convert the plus operations to a generic plus instruction.
@@ -1589,12 +1540,9 @@ gc_bif2 Fail Live u$bif:erlang:bxor/2 S1 S2 Dst => \
gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst
-i_increment r I I d
-i_increment x I I d
-i_increment y I I d
+i_increment rxy I I d
-i_plus j I x x d
-i_plus j I x y d
+i_plus j I x xy d
i_plus j I s s d
i_minus j I x x d
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 2de908ffb3..bfebff5706 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -604,18 +604,6 @@ static size_t my_strnlen(const char *s, size_t maxlen)
#endif
-#if defined(HAVE_SYS_UN_H)
-
-/* Check that some character in the buffer != '\0' */
-static int is_nonzero(const char *s, size_t n)
-{
- size_t i;
- for (i = 0; i < n; i++) if (s[i] != '\0') return !0;
- return 0;
-}
-
-#endif
-
#ifdef VALGRIND
# include <valgrind/memcheck.h>
#else
@@ -4025,13 +4013,30 @@ static char* inet_set_address(int family, inet_address* dst,
int n;
if (*len == 0) return str_einval;
n = *((unsigned char*)(*src)); /* Length field */
- if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) {
+ if (*len < 1+n) return str_einval;
+ if (n +
+#ifdef __linux__
+ /* Make sure the address gets zero terminated
+ * except when the first byte is \0 because then it is
+ * sort of zero terminated although the zero termination
+ * comes before the address...
+ * This fix handles Linux's nonportable
+ * abstract socket address extension.
+ */
+ ((*len) > 1 && (*src)[1] == '\0' ? 0 : 1)
+#else
+ 1
+#endif
+ > sizeof(dst->sal.sun_path)) {
return str_einval;
}
sys_memzero((char*)dst, sizeof(struct sockaddr_un));
dst->sal.sun_family = family;
sys_memcpy(dst->sal.sun_path, (*src)+1, n);
*len = offsetof(struct sockaddr_un, sun_path) + n;
+#ifndef NO_SA_LEN
+ dst->sal.sun_len = *len;
+#endif
*src += 1 + n;
return NULL;
}
@@ -4176,15 +4181,16 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len)
if (*len < offsetof(struct sockaddr_un, sun_path)) return -1;
n = *len - offsetof(struct sockaddr_un, sun_path);
if (255 < n) return -1;
- /* Portability fix: Assume that the address is a zero terminated
- * string, except when the first byte is \0 i.e the
- * string length is 0. Then use the reported length instead.
- * This fix handles Linux's abstract socket address
- * nonportable extension.
- */
m = my_strnlen(src->sal.sun_path, n);
- if ((m == 0) && is_nonzero(src->sal.sun_path, n))
- m = n;
+#ifdef __linux__
+ /* Assume that the address is a zero terminated string,
+ * except when the first byte is \0 i.e the string length is 0,
+ * then use the reported length instead.
+ * This fix handles Linux's nonportable
+ * abstract socket address extension.
+ */
+ if (m == 0) m = n;
+#endif
dst[0] = INET_AF_LOCAL;
dst[1] = (char) ((unsigned char) m);
sys_memcpy(dst+2, src->sal.sun_path, m);
@@ -4241,15 +4247,16 @@ inet_address_to_erlang(char *dst, inet_address **src, SOCKLEN_T sz) {
if (sz < offsetof(struct sockaddr_un, sun_path)) return -1;
n = sz - offsetof(struct sockaddr_un, sun_path);
if (255 < n) return -1;
- /* Portability fix: Assume that the address is a zero terminated
- * string, except when the first byte is \0 i.e the
- * string length is 0. Then use the reported length instead.
- * This fix handles Linux's abstract socket address
- * nonportable extension.
- */
m = my_strnlen((*src)->sal.sun_path, n);
- if ((m == 0) && is_nonzero((*src)->sal.sun_path, n))
- m = n;
+#ifdef __linux__
+ /* Assume that the address is a zero terminated string,
+ * except when the first byte is \0 i.e the string length is 0,
+ * Then use the reported length instead.
+ * This fix handles Linux's nonportable
+ * abstract socket address extension.
+ */
+ if (m == 0) m = n;
+#endif
if (dst) {
dst[0] = INET_AF_LOCAL;
dst[1] = (char) ((unsigned char) m);
@@ -8680,6 +8687,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
else {
ptr = &peer;
sz = sizeof(peer);
+ sys_memzero((char *) &peer, sz);
if (IS_SOCKET_ERROR
(sock_peer
(desc->s, (struct sockaddr*)ptr, &sz)))
@@ -10830,10 +10838,11 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
#ifndef SO_ERROR
{
- int sz = sizeof(desc->inet.remote);
- int code = sock_peer(desc->inet.s,
- (struct sockaddr*) &desc->inet.remote, &sz);
-
+ int sz, code;
+ sz = sizeof(desc->inet.remote);
+ sys_memzero((char *) &desc->inet.remote, sz);
+ code = sock_peer(desc->inet.s,
+ (struct sockaddr*) &desc->inet.remote, &sz);
if (IS_SOCKET_ERROR(code)) {
desc->inet.state = INET_STATE_OPEN; /* restore state */
ret = async_error(INETP(desc), sock_errno());
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 0079912b10..b1bea3a960 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -885,7 +885,6 @@ void erts_replace_intr(void) {
void init_break_handler(void)
{
sys_signal(SIGINT, request_break);
- sys_signal(SIGHUP, generic_signal_handler);
#ifndef ETHR_UNUSABLE_SIGUSRX
sys_signal(SIGUSR1, generic_signal_handler);
#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index 1216863c51..95171d04ce 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -60,7 +60,7 @@ all() ->
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Config.
-end_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, _Config) ->
%% Reloading the module will clear all trace patterns, and
%% in a debug-compiled emulator run assertions of the counters
%% for the number of traced exported functions in this module.
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index d07166ed98..35f7baf2cf 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -229,27 +229,6 @@ multi_proc_purge(Config) when is_list(Config) ->
Pid9, Pid10, Pid11, Pid12, Pid13, Pid14, Pid15, Pid16]),
ok.
-body(F, Fakes) ->
- receive
- jog ->
- 40 = F(3),
- erlang:garbage_collect(),
- body(F, Fakes);
- drop_funs ->
- dropped_body()
- end.
-
-dropped_body() ->
- receive
- X -> exit(X)
- end.
-
-gc() ->
- erlang:garbage_collect(),
- gc1().
-gc1() -> ok.
-
-
%% Test the erlang:check_old_code/1 BIF.
t_check_old_code(Config) when is_list(Config) ->
Data = proplists:get_value(data_dir, Config),
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index 2fbf6eae61..e854a5f945 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -127,7 +127,7 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
0 = element(1, erts_debug:get_internal_state(check_io_debug)),
[{testcase, Case}|Config].
-end_per_testcase(Case, Config) ->
+end_per_testcase(Case, _Config) ->
erlang:display({end_per_testcase, Case}),
0 = element(1, erts_debug:get_internal_state(check_io_debug)),
ok.
diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl
index 12928ed6d8..0d6ab5cdb2 100644
--- a/erts/emulator/test/mtx_SUITE.erl
+++ b/erts/emulator/test/mtx_SUITE.erl
@@ -97,7 +97,7 @@ init_per_testcase(_Case, Config) ->
wait_deallocations(),
Config.
-end_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, _Config) ->
ok.
wait_deallocations() ->
diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl
index 7e516176f7..d788360812 100644
--- a/erts/emulator/test/signal_SUITE.erl
+++ b/erts/emulator/test/signal_SUITE.erl
@@ -53,7 +53,7 @@ init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
available_internal_state(true),
[{testcase, Func}|Config].
-end_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, _Config) ->
ok.
init_per_suite(Config) ->
diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl
index 65188b6e26..7690557fda 100644
--- a/erts/emulator/test/statistics_SUITE.erl
+++ b/erts/emulator/test/statistics_SUITE.erl
@@ -396,7 +396,7 @@ msb_swt_hog(false) ->
count(1000000),
msb_swt_hog(false).
-msb_scheduler_wall_time(Config) ->
+msb_scheduler_wall_time(_Config) ->
erlang:system_flag(scheduler_wall_time, true),
Dirty = erlang:system_info(dirty_cpu_schedulers) /= 0,
Hogs = lists:map(fun (_) ->
@@ -493,7 +493,7 @@ badarg(Config) when is_list(Config) ->
tok_loop() ->
tok_loop().
-run_queues_lengths_active_tasks(Config) ->
+run_queues_lengths_active_tasks(_Config) ->
TokLoops = lists:map(fun (_) ->
spawn_opt(fun () ->
tok_loop()
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index f846b0f4b9..643c2e0472 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -733,7 +733,7 @@ set_on_first_spawn(Config) when is_list(Config) ->
%% Tests trace(Pid, How, [set_on_link]).
-set_on_link(Config) ->
+set_on_link(_Config) ->
Listener = fun_spawn(fun process/0),
%% Create and trace a process with the set_on_link flag.
@@ -756,7 +756,7 @@ set_on_link(Config) ->
%% Tests trace(Pid, How, [set_on_first_spawn]).
-set_on_first_link(Config) ->
+set_on_first_link(_Config) ->
ct:timetrap({seconds, 10}),
Listener = fun_spawn(fun process/0),
diff --git a/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl
index b6a6fd5404..ef58978749 100644
--- a/erts/emulator/test/trace_meta_SUITE.erl
+++ b/erts/emulator/test/trace_meta_SUITE.erl
@@ -74,7 +74,7 @@ config(priv_dir,_) ->
init_per_testcase(_Case, Config) ->
Config.
-end_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, _Config) ->
shutdown(),
ok.
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 9813142585..05cd48d434 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -332,9 +332,9 @@ while (<>) {
if (/^\%macro:(.*)/) {
my($op, $macro, @flags) = split(' ', $1);
defined($macro) and $macro =~ /^-/ and
- &error("A macro must not start with a hyphen");
+ error("A macro must not start with a hyphen");
foreach (@flags) {
- /^-/ or &error("Flags for macros should start with a hyphen");
+ /^-/ or error("Flags for macros should start with a hyphen");
}
error("Macro for '$op' is already defined")
if defined $macro{$op};
@@ -347,7 +347,7 @@ while (<>) {
# Handle transformations.
#
if (/=>/) {
- &parse_transformation($_);
+ parse_transformation($_);
next;
}
@@ -357,8 +357,8 @@ while (<>) {
$op_num = undef;
if (s/^(\d+):\s*//) {
$op_num = $1;
- $op_num != 0 or &error("Opcode 0 invalid");
- &error("Opcode $op_num already defined")
+ $op_num != 0 or error("Opcode 0 invalid");
+ error("Opcode $op_num already defined")
if defined $gen_opname[$op_num];
}
@@ -369,11 +369,11 @@ while (<>) {
my($obsolete) = $1;
my($name) = $2;
my($arity) = $3;
- $name =~ /^[a-z]/ or &error("Opname must start with a lowercase letter");
+ $name =~ /^[a-z]/ or error("Opname must start with a lowercase letter");
defined $gen_arity{$name} and $gen_arity{$name} != $arity and
- &error("Opname $name already defined with arity $gen_arity{$name}");
+ error("Opname $name already defined with arity $gen_arity{$name}");
defined $unnumbered{$name,$arity} and
- &error("Opname $name already defined with arity $gen_arity{$name}");
+ error("Opname $name already defined with arity $gen_arity{$name}");
if (defined $op_num) { # Numbered generic operation
$gen_opname[$op_num] = $name;
@@ -395,16 +395,16 @@ while (<>) {
# Name Arg1 Arg2...
#
my($name, @args) = split;
- &error("too many operands")
+ error("too many operands")
if @args > $max_spec_operands;
- &syntax_check($name, @args);
+ syntax_check($name, @args);
my $arity = @args;
if (defined $gen_opnum{$name,$arity} and $obsolete[$gen_opnum{$name,$arity}]) {
error("specific instructions may not be specified for obsolete instructions");
}
- push(@{$specific_op{"$name/$arity"}}, [$name, $hot, @args]);
+ save_specific_ops($name, $arity, $hot, @args);
if (defined $op_num) {
- &error("specific instructions must not be numbered");
+ error("specific instructions must not be numbered");
} elsif (!defined($gen_arity{$name}) && !defined($unnumbered{$name,$arity})) {
#
# Create an unumbered generic instruction too.
@@ -446,7 +446,7 @@ $num_file_opcodes = @gen_opname;
# Produce output for the chosen target.
#
-&$target;
+&$target();
#
# Produce output needed by the emulator/loader.
@@ -462,7 +462,7 @@ sub emulator_output {
#
$name = "$outdir/beam_opcodes.c";
open(STDOUT, ">$name") || die "Failed to open $name for writing: $!\n";
- &comment('C');
+ comment('C');
print "#ifdef HAVE_CONFIG_H\n";
print "# include \"config.h\"\n";
print "#endif\n\n";
@@ -525,7 +525,7 @@ sub emulator_output {
# Call a generator to calculate size and generate macros
# for the emulator.
#
- my($size, $code, $pack) = &basic_generator($name, $hot, @args);
+ my($size, $code, $pack) = basic_generator($name, $hot, @args);
#
# Save the generated $code for later.
@@ -587,7 +587,7 @@ sub emulator_output {
# Generate transformations.
#
- &tr_gen(@transformations);
+ tr_gen(@transformations);
#
# Print the generic instruction table.
@@ -602,7 +602,7 @@ sub emulator_output {
my($arity) = $gen_arity[$i];
printf "/* %3d */ ", $i;
if (!defined $name) {
- &init_item("", 0, 0, 0, -1);
+ init_item("", 0, 0, 0, -1);
} else {
my($key) = "$name/$arity";
my($tr) = defined $gen_transform_offset{$key} ?
@@ -614,7 +614,7 @@ sub emulator_output {
$is_transformed{$name,$arity} or
error("instruction $key has no specific instruction");
$spec_op = -1 unless defined $spec_op;
- &init_item($name, $arity, $spec_op, $num_specific, $tr);
+ init_item($name, $arity, $spec_op, $num_specific, $tr);
}
}
print "};\n";
@@ -624,7 +624,7 @@ sub emulator_output {
#
$name = "$outdir/beam_opcodes.h";
open(STDOUT, ">$name") || die "Failed to open $name for writing: $!\n";
- &comment('C');
+ comment('C');
print "#ifndef __OPCODES_H__\n";
print "#define __OPCODES_H__\n\n";
@@ -662,14 +662,14 @@ sub emulator_output {
my $letter;
my $tag_num = 0;
- &comment('C', "The following operand types for generic instructions",
+ comment('C', "The following operand types for generic instructions",
"occur in beam files.");
foreach $letter (split('', $compiler_types)) {
print "#define TAG_$letter $tag_num\n";
$tag_num++;
}
print "\n";
- &comment('C', "The following operand types are only used in the loader.");
+ comment('C', "The following operand types are only used in the loader.");
foreach $letter (split('', $loader_types)) {
print "#define TAG_$letter $tag_num\n";
$tag_num++;
@@ -736,26 +736,26 @@ sub emulator_output {
$name = "$outdir/beam_tr_funcs.h";
open(STDOUT, ">$name") || die "Failed to open $name for writing: $!\n";
- &comment('C');
- &tr_gen_call(@call_table);
+ comment('C');
+ tr_gen_call(@call_table);
$name = "$outdir/beam_pred_funcs.h";
open(STDOUT, ">$name") || die "Failed to open $name for writing: $!\n";
- &comment('C');
- &tr_gen_call(@pred_table);
+ comment('C');
+ tr_gen_call(@pred_table);
#
# Implementation of operations for emulator.
#
$name = "$outdir/beam_hot.h";
open(STDOUT, ">$name") || die "Failed to open $name for writing: $!\n";
- &comment('C');
- &print_code(\%hot_code);
+ comment('C');
+ print_code(\%hot_code);
$name = "$outdir/beam_cold.h";
open(STDOUT, ">$name") || die "Failed to open $name for writing: $!\n";
- &comment('C');
- &print_code(\%cold_code);
+ comment('C');
+ print_code(\%cold_code);
}
@@ -818,7 +818,7 @@ sub compiler_output {
open(STDOUT, ">$outdir/$name") || die "Failed to open $name for writing: $!\n";
print "-module($module).\n";
- &comment('erlang');
+ comment('erlang');
print "-export([format_number/0]).\n";
print "-export([opcode/2,opname/1]).\n";
@@ -830,7 +830,7 @@ sub compiler_output {
for ($i = 0; $i < @gen_opname; $i++) {
next unless defined $gen_opname[$i];
print "%%" if $obsolete[$i];
- print "opcode(", &quote($gen_opname[$i]), ", $gen_arity[$i]) -> $i;\n";
+ print "opcode(", quote($gen_opname[$i]), ", $gen_arity[$i]) -> $i;\n";
}
print "opcode(Name, Arity) -> erlang:error(badarg, [Name,Arity]).\n\n";
@@ -838,7 +838,7 @@ sub compiler_output {
for ($i = 0; $i < @gen_opname; $i++) {
next unless defined $gen_opname[$i];
print "opname($i) -> {",
- &quote($gen_opname[$i]), ",$gen_arity[$i]};\n";
+ quote($gen_opname[$i]), ",$gen_arity[$i]};\n";
}
print "opname(Number) -> erlang:error(badarg, [Number]).\n";
@@ -847,7 +847,7 @@ sub compiler_output {
#
my($hrl_name) = "$outdir/${module}.hrl";
open(STDOUT, ">$hrl_name") || die "Failed to open $hrl_name for writing: $!\n";
- &comment('erlang');
+ comment('erlang');
for ($i = 0; $i < @tag_type && $i < 8; $i++) {
print "-define(tag_$tag_type[$i], $i).\n";
@@ -863,11 +863,33 @@ sub syntax_check {
my($name, @args) = @_;
my($i);
- &error("Bad opcode name '$name'")
+ error("Bad opcode name '$name'")
unless $name =~ /^[a-z][\w\d_]*$/;
for ($i = 0; $i < @args; $i++) {
- &error("Argument " . ($i+1) . ": invalid type '$args[$i]'")
- unless defined $arg_size{$args[$i]};
+ foreach my $type (split(//, $args[$i])) {
+ error("Argument " . ($i+1) . ": invalid type '$type'")
+ unless defined $arg_size{$type};
+ }
+ }
+}
+
+sub save_specific_ops {
+ my($name,$arity,$hot,@args) = @_;
+ my(@res) = ("");
+
+ foreach my $arg (@args) {
+ my @new_res = ();
+ foreach my $type (split(//, $arg)) {
+ foreach my $args (@res) {
+ push @new_res, "$args$type";
+ }
+ }
+ @res = @new_res;
+ }
+ my $key = "$name/$arity";
+ foreach my $args (@res) {
+ @args = split //, $args;
+ push @{$specific_op{$key}}, [$name,$hot,@args];
}
}
@@ -928,7 +950,6 @@ sub basic_generator {
my($tmp_arg_num) = 1;
my($pack_spec) = '';
my($var_decls) = '';
- my($gen_dest_arg) = 'StoreSimpleDest';
my($i);
my($no_prefetch) = 0;
@@ -964,7 +985,7 @@ sub basic_generator {
#
if ($flags =~ /-pack/ && $hot) {
- ($prefix, $pack_spec, @args) = &do_pack(@args);
+ ($prefix, $pack_spec, @args) = do_pack(@args);
}
#
@@ -994,11 +1015,11 @@ sub basic_generator {
push(@f_types, $_);
$prefix .= "GetR($size, $tmp);\n";
last SWITCH; };
- /d/ and do { $var_decls .= "Eterm dst; ";
- push(@f, "dst");
+ /d/ and do { $var_decls .= "Eterm dst; Eterm* dst_ptr; ";
+ push(@f, "*dst_ptr");
push(@f_types, $_);
$prefix .= "dst = Arg($size);\n";
- $gen_dest_arg = 'StoreResult';
+ $prefix .= "dst_ptr = REG_TARGET_PTR(dst);\n";
last SWITCH;
};
defined($incl_arg{$_})
@@ -1017,14 +1038,6 @@ sub basic_generator {
}
#
- # If requested, pass a pointer to the destination register.
- # The destination must be the last operand.
- #
- if ($flags =~ /-gen_dest/) {
- push(@f, $gen_dest_arg);
- }
-
- #
# Add a fail action macro if requested.
#
@@ -1195,7 +1208,7 @@ sub do_pack {
}
$down = "$instr[$ap]$down";
- my($unpack) = &make_unpack($tmpnum, $shift[$ap], $mask[$ap]);
+ my($unpack) = make_unpack($tmpnum, $shift[$ap], $mask[$ap]);
$args[$i] = "pack:$this_size:$reg" . "b($unpack)";
if (++$ap == $args_per_word) {
@@ -1259,7 +1272,7 @@ sub parse_transformation {
foreach (@from) {
if (/^(\w+)\((.*?)\)/) {
my($name, $arglist) = ($1, $2);
- $_ = (&compile_transform_function($name, split(/\s*,\s*/, $arglist)));
+ $_ = (compile_transform_function($name, split(/\s*,\s*/, $arglist)));
} else {
(@op) = split;
($rest_var,$_) = compile_transform(1, $rest_var, @op);
@@ -1275,7 +1288,7 @@ sub parse_transformation {
my @to;
if ($to =~ /^(\w+)\((.*?)\)/) {
my($name, $arglist) = ($1, $2);
- @to = (&compile_transform_function($name, split(/\s*,\s*/, $arglist)));
+ @to = (compile_transform_function($name, split(/\s*,\s*/, $arglist)));
} else {
@to = split(/\s*\|\s*/, $to);
foreach (@to) {
@@ -1297,7 +1310,7 @@ sub compile_transform {
my $arity = 0;
foreach (@ops) {
- my(@list) = &tr_parse_op($src, $_);
+ my(@list) = tr_parse_op($src, $_);
if ($list[1] eq '*') {
$rest_var = $list[0];
} elsif (defined $rest_var and $list[0] eq $rest_var) {
@@ -1334,7 +1347,7 @@ sub tr_parse_op {
if (/^([A-Z]\w*)(.*)/) {
$var = $1;
$_ = $2;
- &error("garbage after variable")
+ error("garbage after variable")
unless /^=(.*)/ or /^(\s*)$/;
$_ = $1;
}
@@ -1345,7 +1358,7 @@ sub tr_parse_op {
$type = $1;
$_ = $2;
foreach (split('', $type)) {
- &error("bad type in $op")
+ error("bad type in $op")
unless defined $type_bit{$_} or $type eq '*';
$_ eq 'r' and
error("$op: 'r' is not allowed in transformations")
@@ -1392,7 +1405,7 @@ sub tr_parse_op {
# Nothing more is allowed after the command.
- &error("garbage '$_' after operand: $op")
+ error("garbage '$_' after operand: $op")
unless /^\s*$/;
# Test that destination has no conditions.
@@ -1519,7 +1532,7 @@ sub tr_gen_from {
# Check that $name/$arity refers to a valid generic instruction.
#
- &error($where, "invalid generic op $name/$arity")
+ error($where, "invalid generic op $name/$arity")
unless defined $gen_opnum{$name,$arity};
$opnum = $gen_opnum{$name,$arity};
@@ -1547,11 +1560,11 @@ sub tr_gen_from {
$type_mask |= $type_bit{$_};
}
if ($cond ne 'is_eq') {
- push(@code, &make_op($types, 'is_type', $type_mask));
+ push(@code, make_op($types, 'is_type', $type_mask));
} else {
$cond = '';
- push(@code, &make_op("$types== $val", 'is_type_eq',
- $type_mask, $val));
+ push(@code, make_op("$types== $val", 'is_type_eq',
+ $type_mask, $val));
}
}
}
@@ -1560,12 +1573,12 @@ sub tr_gen_from {
my($m, $f, $a) = split(/:/, $val);
$ignored_var = '';
$may_fail = 1;
- push(@code, &make_op('', "$cond", "am_$m",
+ push(@code, make_op('', "$cond", "am_$m",
"am_$f", $a));
} elsif ($cond ne '') {
$ignored_var = '';
$may_fail = 1;
- push(@code, &make_op('', "$cond", $val));
+ push(@code, make_op('', "$cond", $val));
}
if ($var ne '') {
@@ -1591,7 +1604,7 @@ sub tr_gen_from {
$var_type{$var} = 'scalar';
$var{$var} = $var_num;
$var_num++;
- push(@code, &make_op($var, 'set_var', $var{$var}));
+ push(@code, make_op($var, 'set_var', $var{$var}));
}
}
if (is_instr($code[$#code], 'set_var')) {
@@ -1600,7 +1613,7 @@ sub tr_gen_from {
my $var = $ref->[1][1];
push(@code, make_op($comment, 'set_var_next_arg', $var));
} else {
- push(@code, &make_op($ignored_var, 'next_arg'));
+ push(@code, make_op($ignored_var, 'next_arg'));
}
}
@@ -1645,7 +1658,7 @@ sub tr_gen_to {
my(@args);
foreach $var (@ops) {
- &error($where, "variable '$var' unbound")
+ error($where, "variable '$var' unbound")
unless defined $var{$var};
if ($var_type{$var} eq 'scalar') {
push(@args, "var[$var{$var}]");
@@ -1668,7 +1681,7 @@ sub tr_gen_to {
#
my($key) = "$name/$arity";
- &error($where, "invalid generic op $name/$arity")
+ error($where, "invalid generic op $name/$arity")
unless defined $gen_opnum{$name,$arity};
my $opnum = $gen_opnum{$name,$arity};
@@ -1683,15 +1696,15 @@ sub tr_gen_to {
if ($type eq '*') {
push(@code, make_op($var, 'store_rest_args'));
} elsif ($var ne '') {
- &error($where, "variable '$var' unbound")
+ error($where, "variable '$var' unbound")
unless defined $var{$var};
my $op = make_op($var, 'store_var_next_arg', $var{$var});
op_slot_usage($op, $var{$var});
push(@code, $op);
} elsif ($type ne '') {
- push(@code, &make_op('', 'store_type', "TAG_$type"));
+ push(@code, make_op('', 'store_type', "TAG_$type"));
if ($type_val) {
- push(@code, &make_op('', 'store_val', $type_val));
+ push(@code, make_op('', 'store_val', $type_val));
}
push(@code, make_op('', 'next_arg'));
}
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index f73c4ef1ca..70520eea15 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -184,17 +184,6 @@ static char *plusz_val_switches[] = {
#endif
#define SMP_SUFFIX ".smp"
-#define DEBUG_SUFFIX ".debug"
-#define EMU_TYPE_SUFFIX_LENGTH strlen(DEBUG_SUFFIX)
-
-/*
- * Define flags for different memory architectures.
- */
-#define EMU_TYPE_SMP 0x0001
-
-#ifdef __WIN32__
-#define EMU_TYPE_DEBUG 0x0004
-#endif
void usage(const char *switchname);
static void usage_format(char *format, ...);
@@ -221,7 +210,7 @@ static void *erealloc(void *p, size_t size);
static void efree(void *p);
static char* strsave(char* string);
static int is_one_of_strings(char *str, char *strs[]);
-static char *write_str(char *to, char *from);
+static char *write_str(char *to, const char *from);
static void get_home(void);
static void add_epmd_port(void);
#ifdef __WIN32__
@@ -255,9 +244,8 @@ static int verbose = 0; /* If non-zero, print some extra information. */
static int start_detached = 0; /* If non-zero, the emulator should be
* started detached (in the background).
*/
-static int emu_type = 0; /* If non-zero, start beam.ARCH or beam.ARCH.exe
- * instead of beam or beam.exe, where ARCH is defined by flags. */
-static int emu_type_passed = 0; /* Types explicitly set */
+static int start_smp_emu = 0; /* Start the smp emulator. */
+static const char* emu_type = 0; /* Type of emulator (lcnt, valgrind, etc) */
#ifdef __WIN32__
static char *start_emulator_program = NULL; /* For detachec mode -
@@ -352,11 +340,11 @@ free_env_val(char *value)
}
/*
- * Add the architecture suffix to the program name if needed,
- * except on Windows, where we insert it just before ".DLL".
+ * Add the type and architecture suffix to the program name if needed.
+ * On Windows, we insert it just before ".DLL".
*/
static char*
-add_extra_suffixes(char *prog, int type)
+add_extra_suffixes(char *prog)
{
char *res;
char *p;
@@ -366,16 +354,10 @@ add_extra_suffixes(char *prog, int type)
int dll = 0;
#endif
- if (!type) {
- return prog;
- }
-
len = strlen(prog);
- /* Worst-case allocation */
- p = emalloc(len +
- EMU_TYPE_SUFFIX_LENGTH +
- + 1);
+ /* Allocate enough extra space for suffixes */
+ p = emalloc(len + 100);
res = p;
p = write_str(p, prog);
@@ -392,13 +374,11 @@ add_extra_suffixes(char *prog, int type)
}
#endif
-#ifdef __WIN32__
- if (type & EMU_TYPE_DEBUG) {
- p = write_str(p, DEBUG_SUFFIX);
- type &= ~(EMU_TYPE_DEBUG);
+ if (emu_type) {
+ p = write_str(p, ".");
+ p = write_str(p, emu_type);
}
-#endif
- if (type == EMU_TYPE_SMP) {
+ if (start_smp_emu) {
p = write_str(p, SMP_SUFFIX);
}
#ifdef __WIN32__
@@ -489,12 +469,11 @@ int main(int argc, char **argv)
cpuinfo = erts_cpu_info_create();
/* '-smp auto' is default */
#ifdef ERTS_HAVE_SMP_EMU
- emu_type |= EMU_TYPE_SMP;
+ start_smp_emu = 1;
#endif
#if defined(__WIN32__) && defined(WIN32_ALWAYS_DEBUG)
- emu_type_passed |= EMU_TYPE_DEBUG;
- emu_type |= EMU_TYPE_DEBUG;
+ emu_type = "debug";
#endif
/* We need to do this before the ordinary processing. */
@@ -519,57 +498,42 @@ int main(int argc, char **argv)
if (strcmp(argv[i+1], "auto") == 0) {
i++;
- smp_auto:
- emu_type_passed |= EMU_TYPE_SMP;
-#if defined(ERTS_HAVE_PLAIN_EMU) && !defined(ERTS_HAVE_SMP_EMU)
- emu_type &= ~EMU_TYPE_SMP;
-#else
- emu_type |= EMU_TYPE_SMP;
-#endif
- }
- else if (strcmp(argv[i+1], "enable") == 0) {
+ } else if (strcmp(argv[i+1], "enable") == 0) {
i++;
smp_enable:
- emu_type_passed |= EMU_TYPE_SMP;
-#ifdef ERTS_HAVE_SMP_EMU
- emu_type |= EMU_TYPE_SMP;
-#else
+ ;
+#if !defined(ERTS_HAVE_SMP_EMU)
usage_notsup("-smp enable", "");
#endif
- }
- else if (strcmp(argv[i+1], "disable") == 0) {
+ } else if (strcmp(argv[i+1], "disable") == 0) {
i++;
smp_disable:
- emu_type_passed &= ~EMU_TYPE_SMP;
#ifdef ERTS_HAVE_PLAIN_EMU
- emu_type &= ~EMU_TYPE_SMP;
+ start_smp_emu = 0;
#else
usage_notsup("-smp disable", " Use \"+S 1\" instead.");
#endif
- }
- else {
+ } else {
smp:
-
- emu_type_passed |= EMU_TYPE_SMP;
-#ifdef ERTS_HAVE_SMP_EMU
- emu_type |= EMU_TYPE_SMP;
-#else
+ ;
+#if !defined(ERTS_HAVE_SMP_EMU)
usage_notsup("-smp", "");
#endif
}
} else if (strcmp(argv[i], "-smpenable") == 0) {
goto smp_enable;
} else if (strcmp(argv[i], "-smpauto") == 0) {
- goto smp_auto;
+ ;
} else if (strcmp(argv[i], "-smpdisable") == 0) {
goto smp_disable;
-#ifdef __WIN32__
- } else if (strcmp(argv[i], "-debug") == 0) {
- emu_type_passed |= EMU_TYPE_DEBUG;
- emu_type |= EMU_TYPE_DEBUG;
-#endif
} else if (strcmp(argv[i], "-extra") == 0) {
break;
+ } else if (strcmp(argv[i], "-emu_type") == 0) {
+ if (i + 1 >= argc) {
+ usage(argv[i]);
+ }
+ emu_type = argv[i+1];
+ i++;
}
}
i++;
@@ -582,7 +546,7 @@ int main(int argc, char **argv)
if (strcmp(malloc_lib, "libc") != 0)
usage("+MYm");
}
- emu = add_extra_suffixes(emu, emu_type);
+ emu = add_extra_suffixes(emu);
emu_name = strsave(emu);
erts_snprintf(tmpStr, sizeof(tmpStr), "%s" DIRSEP "%s" BINARY_EXT, bindir, emu);
emu = strsave(tmpStr);
@@ -1176,7 +1140,11 @@ int main(int argc, char **argv)
{
execv(emu, Eargsp);
}
- error("Error %d executing \'%s\'.", errno, emu);
+ if (errno == ENOENT) {
+ error("The emulator \'%s\' does not exist.", emu);
+ } else {
+ error("Error %d executing \'%s\'.", errno, emu);
+ }
return 1;
#endif
}
@@ -1376,7 +1344,7 @@ is_one_of_strings(char *str, char *strs[])
return 0;
}
-static char *write_str(char *to, char *from)
+static char *write_str(char *to, const char *from)
{
while (*from)
*(to++) = *(from++);
@@ -1903,6 +1871,7 @@ read_args_file(char *filename)
#undef SAVE_CHAR
}
+
typedef struct {
char **argv;
int argc;
diff --git a/erts/etc/unix/Makefile b/erts/etc/unix/Makefile
index 2fa9cd047b..17de4d8878 100644
--- a/erts/etc/unix/Makefile
+++ b/erts/etc/unix/Makefile
@@ -24,7 +24,7 @@ include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
include ../../vsn.mk
-opt debug: etc
+opt debug lcnt: etc
.PHONY: etc
etc: etp-commands
@@ -44,4 +44,4 @@ clean:
include $(ERL_TOP)/make/otp_release_targets.mk
.PHONY: release_spec
-release_spec: etc \ No newline at end of file
+release_spec: etc
diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile
index ae2521474e..047e42170a 100644
--- a/erts/start_scripts/Makefile
+++ b/erts/start_scripts/Makefile
@@ -68,7 +68,7 @@ include $(LIBPATH)/stdlib/vsn.mk
##############################################################################
-debug opt script: rel $(INSTALL_SCRIPTS) $(RELEASES_SRC)
+debug opt lcnt script: rel $(INSTALL_SCRIPTS) $(RELEASES_SRC)
rel: $(REL_SCRIPTS)
diff --git a/lib/common_test/doc/src/ct_ssh.xml b/lib/common_test/doc/src/ct_ssh.xml
index 137e4c3f1d..63627d8840 100644
--- a/lib/common_test/doc/src/ct_ssh.xml
+++ b/lib/common_test/doc/src/ct_ssh.xml
@@ -1034,6 +1034,33 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</func>
<func>
+ <name>shell(SSH, ChannelId) -&gt; ok | {error, Reason}</name>
+ <fsummary>Equivalent to shell(SSH, ChannelId, DefaultTimeout).</fsummary>
+ <desc><marker id="shell-2"/>
+ <p>Equivalent to
+ <seealso marker="#shell-3"><c>ct_ssh:shell(SSH, ChannelId,
+ DefaultTimeout)</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>shell(SSH, ChannelId, Timeout) -&gt; ok | {error, Reason}</name>
+ <fsummary>Requests that the user default shell is executed at the
+ server end.</fsummary>
+ <type>
+ <v>SSH = connection()</v>
+ <v>ChannelId = integer()</v>
+ <v>Timeout = integer()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc><marker id="shell-3"/>
+ <p>Requests that the user default shell (typically defined in
+ <c>/etc/passwd</c> in Unix systems) is executed at the
+ server end.</p>
+ </desc>
+ </func>
+
+ <func>
<name>subsystem(SSH, ChannelId, Subsystem) -&gt; Status | {error, Reason}</name>
<fsummary>Equivalent to subsystem(SSH, ChannelId, Subsystem,
DefaultTimeout).</fsummary>
diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl
index 6ab3bf036c..5ac91f396b 100644
--- a/lib/common_test/src/ct_ssh.erl
+++ b/lib/common_test/src/ct_ssh.erl
@@ -68,7 +68,8 @@
send_and_receive/3, send_and_receive/4, send_and_receive/5,
send_and_receive/6,
exec/2, exec/3, exec/4,
- subsystem/3, subsystem/4]).
+ subsystem/3, subsystem/4,
+ shell/2, shell/3]).
%% STFP Functions
-export([sftp_connect/1,
@@ -94,6 +95,7 @@
-record(state, {ssh_ref, conn_type, target}).
+-type handle() :: pid().
%%%-----------------------------------------------------------------
%%%------------------------ SSH COMMANDS ---------------------------
@@ -490,6 +492,22 @@ subsystem(SSH, ChannelId, Subsystem, Timeout) ->
call(SSH, {subsystem,ChannelId,Subsystem,Timeout}).
+-spec shell(SSH, ChannelId) -> Result when
+ SSH :: handle() | ct:target_name(),
+ ChannelId :: ssh:ssh_channel_id(),
+ Result :: ok | {error,term()}.
+shell(SSH, ChannelId) ->
+ shell(SSH, ChannelId, ?DEFAULT_TIMEOUT).
+
+-spec shell(SSH, ChannelId, Timeout) -> Result when
+ SSH :: handle() | ct:target_name(),
+ ChannelId :: ssh:ssh_channel_id(),
+ Timeout :: timeout(),
+ Result :: ok | {error,term()}.
+shell(SSH, ChannelId, Timeout) ->
+ call(SSH, {shell,ChannelId,Timeout}).
+
+
%%%-----------------------------------------------------------------
%%%------------------------ SFTP COMMANDS --------------------------
@@ -1067,6 +1085,14 @@ handle_msg({subsystem,Chn,Subsystem,TO}, State) ->
Result = ssh_connection:subsystem(SSHRef, Chn, Subsystem, TO),
{Result,State};
+handle_msg({shell,Chn,TO}, State) ->
+ #state{ssh_ref=SSHRef, target=Target} = State,
+ try_log(heading(shell,Target),
+ "SSH Ref: ~p, Chn: ~p, Timeout: ~p",
+ [SSHRef,Chn,TO]),
+ Result = ssh_connection:shell(SSHRef, Chn),
+ {Result,State};
+
%% --- SFTP Commands ---
handle_msg({read_file,Srv,File}=Cmd, S=#state{ssh_ref=SSHRef}) ->
diff --git a/lib/common_test/test/ct_keep_logs_SUITE.erl b/lib/common_test/test/ct_keep_logs_SUITE.erl
index f0c85a88a2..6b7aaa57ac 100644
--- a/lib/common_test/test/ct_keep_logs_SUITE.erl
+++ b/lib/common_test/test/ct_keep_logs_SUITE.erl
@@ -70,8 +70,7 @@ keep_logs(Config) ->
Opts = [{suite,Suite},{label,keep_logs} | Opts0],
LogDir=?config(logdir,Opts),
- KeepLogsDir = filename:join(LogDir,unique_name("keep_logs-")),
- ok = file:make_dir(KeepLogsDir),
+ KeepLogsDir = create_dir(filename:join(LogDir,"keep_logs-")),
Opts1 = lists:keyreplace(logdir,1,Opts,{logdir,KeepLogsDir}),
ct:log("New LogDir = ~s", [KeepLogsDir]),
@@ -121,7 +120,11 @@ keep_logs(Config) ->
9 = length(L10),
0 = ct_test_support:run_ct_script_start([{keep_logs,10}|Opts1], Config),
L11 = filelib:wildcard(WC),
- 10 = length(L11).
+ 10 = length(L11),
+
+ {ok,Content} = file:list_dir(KeepLogsDir),
+ ct:log("Deleting dir: ~p~nContent: ~p~n",[KeepLogsDir,Content]),
+ ct_test_support:rm_dir(KeepLogsDir).
%% Test the keep_logs option togwther with the refresh_logs option
refresh_logs(Config) ->
@@ -129,8 +132,7 @@ refresh_logs(Config) ->
Suite = filename:join(DataDir, "keep_logs_SUITE"),
Opts0 = ct_test_support:get_opts(Config),
LogDir=?config(logdir,Opts0),
- KeepLogsDir = filename:join(LogDir,unique_name("refresh_logs-")),
- ok = file:make_dir(KeepLogsDir),
+ KeepLogsDir = create_dir(filename:join(LogDir,"refresh_logs-")),
Opts1 = lists:keyreplace(logdir,1,Opts0,{logdir,KeepLogsDir}),
ct:log("New LogDir = ~s", [KeepLogsDir]),
@@ -178,9 +180,20 @@ refresh_logs(Config) ->
L10 = filelib:wildcard(WC),
4 = length(L10),
- ok.
+ {ok,Content} = file:list_dir(KeepLogsDir),
+ ct:log("Deleting dir: ~p~nContent: ~p~n",[KeepLogsDir,Content]),
+ ct_test_support:rm_dir(KeepLogsDir).
+
%%%-----------------------------------------------------------------
%%% Internal
-unique_name(Prefix) ->
+create_dir(Prefix) ->
I = erlang:unique_integer([positive]),
- Prefix ++ integer_to_list(I).
+ Dir = Prefix ++ integer_to_list(I),
+ case filelib:is_dir(Dir) of
+ true ->
+ %% Try again
+ create_dir(Prefix);
+ false ->
+ ok = file:make_dir(Dir),
+ Dir
+ end.
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 935d032667..ba7aadfeec 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -45,6 +45,8 @@
-export([unique_timestamp/0]).
+-export([rm_dir/1]).
+
-include_lib("kernel/include/file.hrl").
%%%-----------------------------------------------------------------
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index fed68037c1..10164890f2 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -629,6 +629,14 @@ module.beam: module.erl \
<p>Turns off warnings for unused record types. Default is to
emit warnings for unused locally defined record types.</p>
</item>
+
+ <tag><c>nowarn_get_stacktrace</c></tag>
+ <item>
+ <p>Turns off warnings for using <c>get_stacktrace/0</c> in a context
+ where it will probably not work in a future release. For example,
+ by default there will be a warning if <c>get_stacktrace/0</c> is
+ used following a <c>catch</c> expression.</p>
+ </item>
</taglist>
<p>Another class of warnings is generated by the compiler
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index 5e0c2b3ebf..9efb9ad6f8 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -538,6 +538,8 @@ BEAM_FORMAT_NUMBER=0
157: has_map_fields/3
158: get_map_elements/3
+# OTP 20
+
## @spec is_tagged_tuple Lbl Reg N Atom
## @doc Test the type of Reg and jumps to Lbl if it is not a tuple.
## Test the arity of Reg and jumps to Lbl if it is not N.
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index f9bcb044ac..f5e904a50a 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -32,7 +32,7 @@
binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1,
other_output/1, kernel_listing/1, encrypted_abstr/1,
strict_record/1, utf8_atoms/1, utf8_functions/1, extra_chunks/1,
- cover/1, env/1, core/1,
+ cover/1, env/1, core_pp/1,
core_roundtrip/1, asm/1, optimized_guards/1,
sys_pre_attributes/1, dialyzer/1,
warnings/1, pre_load_check/1, env_compiler_options/1,
@@ -51,7 +51,7 @@ all() ->
binary, makedep, cond_and_ifdef, listings, listings_big,
other_output, kernel_listing, encrypted_abstr,
strict_record, utf8_atoms, utf8_functions, extra_chunks,
- cover, env, core, core_roundtrip, asm, optimized_guards,
+ cover, env, core_pp, core_roundtrip, asm, optimized_guards,
sys_pre_attributes, dialyzer, warnings, pre_load_check,
env_compiler_options, custom_debug_info, bc_options].
@@ -795,9 +795,9 @@ env_1(Simple, Target) ->
%% Test pretty-printing in Core Erlang format and then try to
%% compile the generated Core Erlang files.
-core(Config) when is_list(Config) ->
+core_pp(Config) when is_list(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Outdir = filename:join(PrivDir, "core"),
+ Outdir = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)),
ok = file:make_dir(Outdir),
TestBeams = get_unique_beam_files(),
@@ -805,11 +805,11 @@ core(Config) when is_list(Config) ->
{raw_abstract_v1,Abstr}}]}} =
beam_lib:chunks(Beam, [abstract_code]),
{Mod,Abstr} end || Beam <- TestBeams],
- test_lib:p_run(fun(F) -> do_core(F, Outdir) end, Abstr).
+ test_lib:p_run(fun(F) -> do_core_pp(F, Outdir) end, Abstr).
-do_core({M,A}, Outdir) ->
+do_core_pp({M,A}, Outdir) ->
try
- do_core_1(M, A, Outdir)
+ do_core_pp_1(M, A, Outdir)
catch
throw:{error,Error} ->
io:format("*** compilation failure '~p' for module ~s\n",
@@ -821,7 +821,7 @@ do_core({M,A}, Outdir) ->
error
end.
-do_core_1(M, A, Outdir) ->
+do_core_pp_1(M, A, Outdir) ->
{ok,M,Core0} = compile:forms(A, [to_core]),
CoreFile = filename:join(Outdir, atom_to_list(M)++".core"),
CorePP = core_pp:format(Core0),
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 3f0439ed80..793cff166c 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1089,9 +1089,6 @@ static void init_algorithms_types(ErlNifEnv* env)
#ifndef OPENSSL_NO_RC4
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc4");
#endif
-#if defined(HAVE_GCM)
- algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_gcm");
-#endif
#if defined(HAVE_CHACHA20_POLY1305)
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"chacha20_poly1305");
#endif
diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml
index 696b7dfa31..acf0d2163a 100644
--- a/lib/inets/doc/src/http_uri.xml
+++ b/lib/inets/doc/src/http_uri.xml
@@ -45,6 +45,7 @@
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>
@@ -53,22 +54,22 @@
<p>Type definitions that are related to URI:</p>
<taglist>
- <tag><c>uri() = string()</c></tag>
+ <tag><c>uri() = string() | unicode:unicode_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()</c></tag>
+ <tag><c>user_info() = string() | unicode:unicode_binary()</c></tag>
<item><p></p></item>
<tag><c>scheme() = atom()</c></tag>
<item><p>Example: http, https</p></item>
- <tag><c>host() = string()</c></tag>
+ <tag><c>host() = string() | unicode:unicode_binary()</c></tag>
<item><p></p></item>
<tag><c>port() = pos_integer()</c></tag>
<item><p></p></item>
- <tag><c>path() = string()</c></tag>
+ <tag><c>path() = string() | unicode:unicode_binary()</c></tag>
<item><p>Represents a file path or directory path</p></item>
- <tag><c>query() = string()</c></tag>
+ <tag><c>query() = string() | unicode:unicode_binary()</c></tag>
<item><p></p></item>
- <tag><c>fragment() = string()</c></tag>
+ <tag><c>fragment() = string() | unicode:unicode_binary()</c></tag>
<item><p></p></item>
</taglist>
@@ -83,7 +84,7 @@
<fsummary>Decodes a hexadecimal encoded URI.</fsummary>
<type>
- <v>HexEncodedURI = string() - A possibly hexadecimal encoded URI</v>
+ <v>HexEncodedURI = string() | unicode:unicode_binary() - A possibly hexadecimal encoded URI</v>
<v>URI = uri()</v>
</type>
@@ -98,7 +99,7 @@
<fsummary>Encodes a hexadecimal encoded URI.</fsummary>
<type>
<v>URI = uri()</v>
- <v>HexEncodedURI = string() - Hexadecimal encoded URI</v>
+ <v>HexEncodedURI = string() | unicode:unicode_binary() - Hexadecimal encoded URI</v>
</type>
<desc>
@@ -145,7 +146,7 @@
<p>Scheme validation fun is to be defined as follows:</p>
<code>
-fun(SchemeStr :: string()) ->
+fun(SchemeStr :: string() | unicode:unicode_binary()) ->
valid | {error, Reason :: term()}.
</code>
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 4217b3c4fb..46484977c9 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -344,7 +344,7 @@
<tag><c><![CDATA[ssl]]></c></tag>
<item>
<p>This is the <c>SSL/TLS</c> connectin configuration option.</p>
- <p>Defaults to <c>[]</c>. See <seealso marker="ssl:ssl">ssl:connect/[2, 3,4]</seealso> for availble options.</p>
+ <p>Defaults to <c>[]</c>. See <seealso marker="ssl:ssl">ssl:connect/[2,3,4]</seealso> for available options.</p>
</item>
<tag><c><![CDATA[autoredirect]]></c></tag>
diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl
index cb3e107ccf..4568d165e7 100644
--- a/lib/inets/src/http_lib/http_uri.erl
+++ b/lib/inets/src/http_lib/http_uri.erl
@@ -102,16 +102,23 @@ parse(AbsURI, Opts) ->
reserved() ->
sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?,
- $#, $[, $], $<, $>, $\", ${, $}, $|,
+ $#, $[, $], $<, $>, $\", ${, $}, $|, %"
$\\, $', $^, $%, $ ]).
-encode(URI) ->
+encode(URI) when is_list(URI) ->
Reserved = reserved(),
- lists:append([uri_encode(Char, Reserved) || Char <- URI]).
+ lists:append([uri_encode(Char, Reserved) || Char <- URI]);
+encode(URI) when is_binary(URI) ->
+ Reserved = reserved(),
+ << <<(uri_encode_binary(Char, Reserved))/binary>> || <<Char>> <= URI >>.
-decode(String) ->
- do_decode(String).
+decode(String) when is_list(String) ->
+ do_decode(String);
+decode(String) when is_binary(String) ->
+ do_decode_binary(String).
+do_decode([$+|Rest]) ->
+ [$ |do_decode(Rest)];
do_decode([$%,Hex1,Hex2|Rest]) ->
[hex2dec(Hex1)*16+hex2dec(Hex2)|do_decode(Rest)];
do_decode([First|Rest]) ->
@@ -119,6 +126,14 @@ do_decode([First|Rest]) ->
do_decode([]) ->
[].
+do_decode_binary(<<$+, Rest/bits>>) ->
+ <<$ , (do_decode_binary(Rest))/binary>>;
+do_decode_binary(<<$%, Hex:2/binary, Rest/bits>>) ->
+ <<(binary_to_integer(Hex, 16)), (do_decode_binary(Rest))/binary>>;
+do_decode_binary(<<First:1/binary, Rest/bits>>) ->
+ <<First/binary, (do_decode_binary(Rest))/binary>>;
+do_decode_binary(<<>>) ->
+ <<>>.
%%%========================================================================
%%% Internal functions
@@ -162,9 +177,30 @@ extract_scheme(Str, Opts) ->
{error, Error}
end;
_ ->
- {ok, list_to_atom(http_util:to_lower(Str))}
+ {ok, to_atom(http_util:to_lower(Str))}
end.
+to_atom(S) when is_list(S) ->
+ list_to_atom(S);
+to_atom(S) when is_binary(S) ->
+ binary_to_atom(S, unicode).
+
+parse_uri_rest(Scheme, DefaultPort, <<"//", URIPart/binary>>, Opts) ->
+ {Authority, PathQueryFragment} =
+ split_uri(URIPart, "[/?#]", {URIPart, <<"">>}, 1, 0),
+ {RawPath, QueryFragment} =
+ split_uri(PathQueryFragment, "[?#]", {PathQueryFragment, <<"">>}, 1, 0),
+ {Query, Fragment} =
+ split_uri(QueryFragment, "#", {QueryFragment, <<"">>}, 1, 0),
+ {UserInfo, HostPort} = split_uri(Authority, "@", {<<"">>, Authority}, 1, 1),
+ {Host, Port} = parse_host_port(Scheme, DefaultPort, HostPort, Opts),
+ Path = path(RawPath),
+ case lists:keyfind(fragment, 1, Opts) of
+ {fragment, true} ->
+ {ok, {Scheme, UserInfo, Host, Port, Path, Query, Fragment}};
+ _ ->
+ {ok, {Scheme, UserInfo, Host, Port, Path, Query}}
+ end;
parse_uri_rest(Scheme, DefaultPort, "//" ++ URIPart, Opts) ->
{Authority, PathQueryFragment} =
split_uri(URIPart, "[/?#]", {URIPart, ""}, 1, 0),
@@ -185,6 +221,11 @@ parse_uri_rest(Scheme, DefaultPort, "//" ++ URIPart, Opts) ->
%% In this version of the function, we no longer need
%% the Scheme argument, but just in case...
+parse_host_port(_Scheme, DefaultPort, <<"[", HostPort/binary>>, Opts) -> %ipv6
+ {Host, ColonPort} = split_uri(HostPort, "\\]", {HostPort, <<"">>}, 1, 1),
+ Host2 = maybe_ipv6_host_with_brackets(Host, Opts),
+ {_, Port} = split_uri(ColonPort, ":", {<<"">>, DefaultPort}, 0, 1),
+ {Host2, int_port(Port)};
parse_host_port(_Scheme, DefaultPort, "[" ++ HostPort, Opts) -> %ipv6
{Host, ColonPort} = split_uri(HostPort, "\\]", {HostPort, ""}, 1, 1),
Host2 = maybe_ipv6_host_with_brackets(Host, Opts),
@@ -198,12 +239,19 @@ parse_host_port(_Scheme, DefaultPort, HostPort, _Opts) ->
split_uri(UriPart, SplitChar, NoMatchResult, SkipLeft, SkipRight) ->
case re:run(UriPart, SplitChar, [{capture, first}]) of
{match, [{Match, _}]} ->
- {string:substr(UriPart, 1, Match + 1 - SkipLeft),
- string:substr(UriPart, Match + 1 + SkipRight, length(UriPart))};
+ {string:slice(UriPart, 0, Match + 1 - SkipLeft),
+ string:slice(UriPart, Match + SkipRight, string:length(UriPart))};
nomatch ->
NoMatchResult
end.
+maybe_ipv6_host_with_brackets(Host, Opts) when is_binary(Host) ->
+ case lists:keysearch(ipv6_host_with_brackets, 1, Opts) of
+ {value, {ipv6_host_with_brackets, true}} ->
+ <<"[", Host/binary, "]">>;
+ _ ->
+ Host
+ end;
maybe_ipv6_host_with_brackets(Host, Opts) ->
case lists:keysearch(ipv6_host_with_brackets, 1, Opts) of
{value, {ipv6_host_with_brackets, true}} ->
@@ -212,15 +260,18 @@ maybe_ipv6_host_with_brackets(Host, Opts) ->
Host
end.
-
int_port(Port) when is_integer(Port) ->
Port;
+int_port(Port) when is_binary(Port) ->
+ binary_to_integer(Port);
int_port(Port) when is_list(Port) ->
list_to_integer(Port);
%% This is the case where no port was found and there was no default port
int_port(no_default_port) ->
throw({error, no_default_port}).
+path(<<"">>) ->
+ <<"/">>;
path("") ->
"/";
path(Path) ->
@@ -234,6 +285,14 @@ uri_encode(Char, Reserved) ->
[Char]
end.
+uri_encode_binary(Char, Reserved) ->
+ case sets:is_element(Char, Reserved) of
+ true ->
+ << $%, (integer_to_binary(Char, 16))/binary >>;
+ false ->
+ <<Char>>
+ end.
+
hex2dec(X) when (X>=$0) andalso (X=<$9) -> X-$0;
hex2dec(X) when (X>=$A) andalso (X=<$F) -> X-$A+10;
hex2dec(X) when (X>=$a) andalso (X=<$f) -> X-$a+10.
diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl
index 20c342dd66..78b0aa2885 100644
--- a/lib/inets/src/http_lib/http_util.erl
+++ b/lib/inets/src/http_lib/http_util.erl
@@ -35,10 +35,10 @@
%%% Internal application API
%%%=========================================================================
to_upper(Str) ->
- string:to_upper(Str).
+ string:uppercase(Str).
to_lower(Str) ->
- string:to_lower(Str).
+ string:lowercase(Str).
%% Example: Mon, 09-Dec-2002 13:46:00 GMT
convert_netscapecookie_date([_D,_A,_Y, $,, $ ,
diff --git a/lib/inets/test/uri_SUITE.erl b/lib/inets/test/uri_SUITE.erl
index b26c645821..d055af59e3 100644
--- a/lib/inets/test/uri_SUITE.erl
+++ b/lib/inets/test/uri_SUITE.erl
@@ -25,6 +25,7 @@
-module(uri_SUITE).
+-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
-include("inets_test_lib.hrl").
@@ -50,7 +51,8 @@ all() ->
fragments,
escaped,
hexed_query,
- scheme_validation
+ scheme_validation,
+ encode_decode
].
%%--------------------------------------------------------------------
@@ -73,7 +75,10 @@ end_per_testcase(_Case, _Config) ->
ipv4(Config) when is_list(Config) ->
{ok, {http,[],"127.0.0.1",80,"/foobar.html",[]}} =
- http_uri:parse("http://127.0.0.1/foobar.html").
+ http_uri:parse("http://127.0.0.1/foobar.html"),
+
+ {ok, {http,<<>>,<<"127.0.0.1">>,80,<<"/foobar.html">>,<<>>}} =
+ http_uri:parse(<<"http://127.0.0.1/foobar.html">>).
ipv6(Config) when is_list(Config) ->
{ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} =
@@ -89,24 +94,52 @@ ipv6(Config) when is_list(Config) ->
[{foo, false}]),
{error,
{malformed_url, _, "http://2010:836B:4179::836B:4179/foobar.html"}} =
- http_uri:parse("http://2010:836B:4179::836B:4179/foobar.html").
+ http_uri:parse("http://2010:836B:4179::836B:4179/foobar.html"),
+
+ {ok, {http,<<>>,<<"2010:836B:4179::836B:4179">>,80,<<"/foobar.html">>,<<>>}} =
+ http_uri:parse(<<"http://[2010:836B:4179::836B:4179]/foobar.html">>),
+ {ok, {http,<<>>,<<"[2010:836B:4179::836B:4179]">>,80,<<"/foobar.html">>,<<>>}} =
+ http_uri:parse(<<"http://[2010:836B:4179::836B:4179]/foobar.html">>,
+ [{ipv6_host_with_brackets, true}]),
+ {ok, {http,<<>>,<<"2010:836B:4179::836B:4179">>,80,<<"/foobar.html">>,<<>>}} =
+ http_uri:parse(<<"http://[2010:836B:4179::836B:4179]/foobar.html">>,
+ [{ipv6_host_with_brackets, false}]),
+ {ok, {http,<<>>,<<"2010:836B:4179::836B:4179">>,80,<<"/foobar.html">>,<<>>}} =
+ http_uri:parse(<<"http://[2010:836B:4179::836B:4179]/foobar.html">>,
+ [{foo, false}]),
+ {error,
+ {malformed_url, _, <<"http://2010:836B:4179::836B:4179/foobar.html">>}} =
+ http_uri:parse(<<"http://2010:836B:4179::836B:4179/foobar.html">>).
host(Config) when is_list(Config) ->
{ok, {http,[],"localhost",8888,"/foobar.html",[]}} =
- http_uri:parse("http://localhost:8888/foobar.html").
+ http_uri:parse("http://localhost:8888/foobar.html"),
+
+ {ok, {http,<<>>,<<"localhost">>,8888,<<"/foobar.html">>,<<>>}} =
+ http_uri:parse(<<"http://localhost:8888/foobar.html">>).
userinfo(Config) when is_list(Config) ->
{ok, {http,"nisse:foobar","localhost",8888,"/foobar.html",[]}} =
- http_uri:parse("http://nisse:foobar@localhost:8888/foobar.html").
+ http_uri:parse("http://nisse:foobar@localhost:8888/foobar.html"),
+
+ {ok, {http,<<"nisse:foobar">>,<<"localhost">>,8888,<<"/foobar.html">>,<<>>}} =
+ http_uri:parse(<<"http://nisse:foobar@localhost:8888/foobar.html">>).
scheme(Config) when is_list(Config) ->
{error, no_scheme} = http_uri:parse("localhost/foobar.html"),
{error, {malformed_url, _, _}} =
- http_uri:parse("localhost:8888/foobar.html").
+ http_uri:parse("localhost:8888/foobar.html"),
+
+ {error, no_scheme} = http_uri:parse(<<"localhost/foobar.html">>),
+ {error, {malformed_url, _, _}} =
+ http_uri:parse(<<"localhost:8888/foobar.html">>).
queries(Config) when is_list(Config) ->
{ok, {http,[],"localhost",8888,"/foobar.html","?foo=bar&foobar=42"}} =
- http_uri:parse("http://localhost:8888/foobar.html?foo=bar&foobar=42").
+ http_uri:parse("http://localhost:8888/foobar.html?foo=bar&foobar=42"),
+
+ {ok, {http,<<>>,<<"localhost">>,8888,<<"/foobar.html">>,<<"?foo=bar&foobar=42">>}} =
+ http_uri:parse(<<"http://localhost:8888/foobar.html?foo=bar&foobar=42">>).
fragments(Config) when is_list(Config) ->
{ok, {http,[],"localhost",80,"/",""}} =
@@ -142,6 +175,41 @@ fragments(Config) when is_list(Config) ->
http_uri:parse("http://localhost?query#", [{fragment,true}]),
{ok, {http,[],"localhost",80,"/path","?query","#"}} =
http_uri:parse("http://localhost/path?query#", [{fragment,true}]),
+
+
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/">>,<<"">>}} =
+ http_uri:parse(<<"http://localhost#fragment">>),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/path">>,<<"">>}} =
+ http_uri:parse(<<"http://localhost/path#fragment">>),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/">>,<<"?query">>}} =
+ http_uri:parse(<<"http://localhost?query#fragment">>),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/path">>,<<"?query">>}} =
+ http_uri:parse(<<"http://localhost/path?query#fragment">>),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/">>,<<"">>,<<"#fragment">>}} =
+ http_uri:parse(<<"http://localhost#fragment">>, [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/path">>,<<"">>,<<"#fragment">>}} =
+ http_uri:parse(<<"http://localhost/path#fragment">>, [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/">>,<<"?query">>,<<"#fragment">>}} =
+ http_uri:parse(<<"http://localhost?query#fragment">>, [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/path">>,<<"?query">>,<<"#fragment">>}} =
+ http_uri:parse(<<"http://localhost/path?query#fragment">>,
+ [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/">>,<<"">>,<<"">>}} =
+ http_uri:parse(<<"http://localhost">>, [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/path">>,<<"">>,<<"">>}} =
+ http_uri:parse(<<"http://localhost/path">>, [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/">>,<<"?query">>,<<"">>}} =
+ http_uri:parse(<<"http://localhost?query">>, [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/path">>,<<"?query">>,<<"">>}} =
+ http_uri:parse(<<"http://localhost/path?query">>, [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/">>,<<"">>,<<"#">>}} =
+ http_uri:parse(<<"http://localhost#">>, [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/path">>,<<"">>,<<"#">>}} =
+ http_uri:parse(<<"http://localhost/path#">>, [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/">>,<<"?query">>,<<"#">>}} =
+ http_uri:parse(<<"http://localhost?query#">>, [{fragment,true}]),
+ {ok, {http,<<>>,<<"localhost">>,80,<<"/path">>,<<"?query">>,<<"#">>}} =
+ http_uri:parse(<<"http://localhost/path?query#">>, [{fragment,true}]),
ok.
escaped(Config) when is_list(Config) ->
@@ -152,7 +220,16 @@ escaped(Config) when is_list(Config) ->
{ok, {http,[],"www.somedomain.com",80,"/%25abc",[]}} =
http_uri:parse("http://www.somedomain.com/%25abc"),
{ok, {http,[],"www.somedomain.com",80,"/%25abc", "?foo=bar"}} =
- http_uri:parse("http://www.somedomain.com/%25abc?foo=bar").
+ http_uri:parse("http://www.somedomain.com/%25abc?foo=bar"),
+
+ {ok, {http,<<>>,<<"www.somedomain.com">>,80,<<"/%2Eabc">>,<<>>}} =
+ http_uri:parse(<<"http://www.somedomain.com/%2Eabc">>),
+ {ok, {http,<<>>,<<"www.somedomain.com">>,80,<<"/%252Eabc">>,<<>>}} =
+ http_uri:parse(<<"http://www.somedomain.com/%252Eabc">>),
+ {ok, {http,<<>>,<<"www.somedomain.com">>,80,<<"/%25abc">>,<<>>}} =
+ http_uri:parse(<<"http://www.somedomain.com/%25abc">>),
+ {ok, {http,<<>>,<<"www.somedomain.com">>,80,<<"/%25abc">>, <<"?foo=bar">>}} =
+ http_uri:parse(<<"http://www.somedomain.com/%25abc?foo=bar">>).
hexed_query(doc) ->
[{doc, "Solves OTP-6191"}];
@@ -196,6 +273,17 @@ scheme_validation(Config) when is_list(Config) ->
http_uri:parse("https://localhost#fragment",
[{scheme_validation_fun, none}]).
+encode_decode(Config) when is_list(Config) ->
+ ?assertEqual("foo%20bar", http_uri:encode("foo bar")),
+ ?assertEqual(<<"foo%20bar">>, http_uri:encode(<<"foo bar">>)),
+
+ ?assertEqual("foo bar", http_uri:decode("foo+bar")),
+ ?assertEqual(<<"foo bar">>, http_uri:decode(<<"foo+bar">>)),
+ ?assertEqual("foo bar", http_uri:decode("foo%20bar")),
+ ?assertEqual(<<"foo bar">>, http_uri:decode(<<"foo%20bar">>)),
+ ?assertEqual("foo\r\n", http_uri:decode("foo%0D%0A")),
+ ?assertEqual(<<"foo\r\n">>, http_uri:decode(<<"foo%0D%0A">>)).
+
%%--------------------------------------------------------------------
%% Internal Functions ------------------------------------------------
diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml
index aebeacee28..570d3ef9bd 100644
--- a/lib/kernel/doc/src/disk_log.xml
+++ b/lib/kernel/doc/src/disk_log.xml
@@ -968,6 +968,12 @@
<c>read_write</c>.
</p>
</item>
+ <tag><c>{quiet, Boolean}</c></tag>
+ <item>
+ <p>Specifies if messages will be sent to
+ <c>error_logger</c> on recoverable errors with
+ the log files. Defaults to <c>true</c>.</p>
+ </item>
</taglist>
<p><c>open/1</c> returns <c>{ok, <anno>Log</anno>}</c> if the
log file is successfully opened. If the file is
diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index 814e8eac46..27db00819f 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2016</year>
+ <year>1996</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -163,6 +163,19 @@ ok</pre>
</desc>
</func>
<func>
+ <name name="get_format_depth" arity="0"/>
+ <fsummary>Get the value of the Kernel application variable
+ <c>error_logger_format_depth</c>.</fsummary>
+ <desc>
+ <p>Returns <c>max(10, Depth)</c>, where <c>Depth</c> is the
+ value of
+ <seealso marker="kernel:kernel_app#error_logger_format_depth">
+ error_logger_format_depth</seealso>
+ in the Kernel application, if Depth is an integer. Otherwise,
+ <c>unlimited</c> is returned.</p>
+ </desc>
+ </func>
+ <func>
<name name="info_msg" arity="1"/>
<name name="info_msg" arity="2"/>
<fsummary>Send a standard information event to the error logger.</fsummary>
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index 28af155493..9fccb4c7ac 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -419,6 +419,29 @@ MaxT = TickTime + TickTime / 4</code>
using this service.</p>
<p>Defaults to <c>false</c>.</p>
</item>
+ <tag><c>shell_history = enabled | disabled </c></tag>
+ <item>
+ <p>Specifies whether shell history should be logged to disk
+ between usages of <c>erl</c>.</p>
+ </item>
+ <tag><c>shell_history_drop = [string()]</c></tag>
+ <item>
+ <p>Specific log lines that should not be persisted. For
+ example <c>["q().", "init:stop()."]</c> will allow to
+ ignore commands that shut the node down. Defaults to
+ <c>[]</c>.</p>
+ </item>
+ <tag><c>shell_history_file_bytes = integer()</c></tag>
+ <item>
+ <p>how many bytes the shell should remember. By default, the
+ value is set to 512kb, and the minimal value is 50kb.</p>
+ </item>
+ <tag><c>shell_history_path = string()</c></tag>
+ <item>
+ <p>Specifies where the shell history files will be stored.
+ defaults to the user's cache directory as returned by
+ <c>filename:basedir(user_cache, "erlang-history")</c>.</p>
+ </item>
<tag><c>shutdown_func = {Mod, Func}</c></tag>
<item>
<p>Where:</p>
diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile
index 2a89faaf13..78aa6192a9 100644
--- a/lib/kernel/src/Makefile
+++ b/lib/kernel/src/Makefile
@@ -85,6 +85,7 @@ MODULES = \
global_group \
global_search \
group \
+ group_history \
heart \
hipe_unified_loader \
inet \
diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl
index 50a20c918c..70cbf1c87c 100644
--- a/lib/kernel/src/disk_log.erl
+++ b/lib/kernel/src/disk_log.erl
@@ -638,6 +638,8 @@ check_arg([{mode, read_only}|Tail], Res) ->
check_arg(Tail, Res#arg{mode = read_only});
check_arg([{mode, read_write}|Tail], Res) ->
check_arg(Tail, Res#arg{mode = read_write});
+check_arg([{quiet, Boolean}|Tail], Res) when is_boolean(Boolean) ->
+ check_arg(Tail, Res#arg{quiet = Boolean});
check_arg(Arg, _) ->
{error, {badarg, Arg}}.
@@ -1276,7 +1278,8 @@ compare_arg(_Attr, _Val, _A) ->
do_open(A) ->
#arg{type = Type, format = Format, name = Name, head = Head0,
file = FName, repair = Repair, size = Size, mode = Mode,
- version = V} = A,
+ quiet = Quiet, version = V} = A,
+ disk_log_1:set_quiet(Quiet),
Head = mk_head(Head0, Format),
case do_open2(Type, Format, Name, FName, Repair, Size, Mode, Head, V) of
{ok, Ret, Extra, FormatType, NoItems} ->
diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl
index c9fda8bef5..a362881f40 100644
--- a/lib/kernel/src/disk_log.hrl
+++ b/lib/kernel/src/disk_log.hrl
@@ -69,13 +69,14 @@
| {file, FileName :: file:filename()}
| {linkto, LinkTo :: none | pid()}
| {repair, Repair :: true | false | truncate}
- | {type, Type :: dlog_type}
+ | {type, Type :: dlog_type()}
| {format, Format :: dlog_format()}
| {size, Size :: dlog_size()}
| {distributed, Nodes :: [node()]}
| {notify, boolean()}
| {head, Head :: dlog_head_opt()}
| {head_func, MFA :: {atom(), atom(), list()}}
+ | {quiet, boolean()}
| {mode, Mode :: dlog_mode()}.
-type dlog_options() :: [dlog_option()].
-type dlog_repair() :: 'truncate' | boolean().
@@ -102,6 +103,7 @@
head = none,
mode = read_write :: dlog_mode(),
notify = false :: boolean(),
+ quiet = false :: boolean(),
options = [] :: dlog_options()}).
-record(cache, %% Cache for logged terms (per file descriptor).
diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl
index d83c30f35f..10c22e1ad6 100644
--- a/lib/kernel/src/disk_log_1.erl
+++ b/lib/kernel/src/disk_log_1.erl
@@ -37,6 +37,7 @@
-export([get_wrap_size/1]).
-export([is_head/1]).
-export([position/3, truncate_at/3, fwrite/4, fclose/2]).
+-export([set_quiet/1, is_quiet/0]).
-compile({inline,[{scan_f2,7}]}).
@@ -500,7 +501,10 @@ lh(H, _F) -> % cannot happen
repair(In, File) ->
FSz = file_size(File),
- error_logger:info_msg("disk_log: repairing ~tp ...\n", [File]),
+ case is_quiet() of
+ true -> ok;
+ _ -> error_logger:info_msg("disk_log: repairing ~tp ...\n", [File])
+ end,
Tmp = add_ext(File, "TMP"),
{ok, {_Alloc, Out, {0, _}, _FileSize}} = new_int_file(Tmp, none),
scan_f_read(<<>>, In, Out, File, FSz, Tmp, ?MAX_CHUNK_SIZE, 0, 0).
@@ -769,8 +773,11 @@ mf_int_chunk(Handle, {FileNo, Pos}, Bin, N) ->
NFileNo = inc(FileNo, Handle#handle.maxF),
case catch int_open(FName, true, read_only, any) of
{error, _Reason} ->
- error_logger:info_msg("disk_log: chunk error. File ~tp missing.\n\n",
- [FName]),
+ case is_quiet() of
+ true -> ok;
+ _ -> error_logger:info_msg("disk_log: chunk error. File ~tp missing.\n\n",
+ [FName])
+ end,
mf_int_chunk(Handle, {NFileNo, 0}, [], N);
{ok, {_Alloc, FdC, _HeadSize, _FileSize}} ->
case chunk(FdC, FName, Pos, Bin, N) of
@@ -797,9 +804,12 @@ mf_int_chunk_read_only(Handle, {FileNo, Pos}, Bin, N) ->
NFileNo = inc(FileNo, Handle#handle.maxF),
case catch int_open(FName, true, read_only, any) of
{error, _Reason} ->
- error_logger:info_msg("disk_log: chunk error. File ~tp missing.\n\n",
- [FName]),
- mf_int_chunk_read_only(Handle, {NFileNo, 0}, [], N);
+ case is_quiet() of
+ true -> ok;
+ _ -> error_logger:info_msg("disk_log: chunk error. File ~tp missing.\n\n",
+ [FName])
+ end,
+ mf_int_chunk_read_only(Handle, {NFileNo, 0}, [], N);
{ok, {_Alloc, FdC, _HeadSize, _FileSize}} ->
case do_chunk_read_only(FdC, FName, Pos, Bin, N) of
{NewFdC, eof} ->
@@ -1549,6 +1559,12 @@ fclose(#cache{fd = Fd, c = C}, FileName) ->
_ = write_cache_close(Fd, FileName, C),
file:close(Fd).
+set_quiet(Bool) ->
+ put(quiet, Bool).
+
+is_quiet() ->
+ get(quiet) =:= true.
+
%% -> {Reply, #cache{}}; Reply = ok | Error
write_cache(Fd, _FileName, []) ->
{ok, #cache{fd = Fd}};
diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl
index 81f1bf8d97..9bf8547745 100644
--- a/lib/kernel/src/error_logger.erl
+++ b/lib/kernel/src/error_logger.erl
@@ -31,6 +31,8 @@
handle_event/2, handle_call/2, handle_info/2,
terminate/2]).
+-export([get_format_depth/0, limit_term/1]).
+
-define(buffer_size, 10).
%%-----------------------------------------------------------------
@@ -518,3 +520,21 @@ string_p1([H|T]) when is_list(H) ->
end;
string_p1([]) -> true;
string_p1(_) -> false.
+
+-spec limit_term(term()) -> term().
+
+limit_term(Term) ->
+ case get_format_depth() of
+ unlimited -> Term;
+ D -> io_lib:limit_term(Term, D)
+ end.
+
+-spec get_format_depth() -> 'unlimited' | pos_integer().
+
+get_format_depth() ->
+ case application:get_env(kernel, error_logger_format_depth) of
+ {ok, Depth} when is_integer(Depth) ->
+ max(10, Depth);
+ undefined ->
+ unlimited
+ end.
diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl
index 3d6415036c..a9e92b28b8 100644
--- a/lib/kernel/src/global.erl
+++ b/lib/kernel/src/global.erl
@@ -447,7 +447,8 @@ info() ->
init([]) ->
process_flag(trap_exit, true),
_ = ets:new(global_locks, [set, named_table, protected]),
- _ = ets:new(global_names, [set, named_table, protected]),
+ _ = ets:new(global_names, [set, named_table, protected,
+ {read_concurrency, true}]),
_ = ets:new(global_names_ext, [set, named_table, protected]),
_ = ets:new(global_pid_names, [bag, named_table, protected]),
diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl
index b5e73117dd..0eeaaad8d2 100644
--- a/lib/kernel/src/group.erl
+++ b/lib/kernel/src/group.erl
@@ -33,7 +33,7 @@ start(Drv, Shell, Options) ->
server(Drv, Shell, Options) ->
process_flag(trap_exit, true),
edlin:init(),
- put(line_buffer, proplists:get_value(line_buffer, Options, [])),
+ put(line_buffer, proplists:get_value(line_buffer, Options, group_history:load())),
put(read_mode, list),
put(user_drv, Drv),
put(expand_fun,
@@ -783,6 +783,7 @@ save_line_buffer("\n", Lines) ->
save_line_buffer(Line, [Line|_Lines]=Lines) ->
save_line_buffer(Lines);
save_line_buffer(Line, Lines) ->
+ group_history:add(Line),
save_line_buffer([Line|Lines]).
save_line_buffer(Lines) ->
diff --git a/lib/kernel/src/group_history.erl b/lib/kernel/src/group_history.erl
new file mode 100644
index 0000000000..5ca5e2d1dd
--- /dev/null
+++ b/lib/kernel/src/group_history.erl
@@ -0,0 +1,341 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. 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%
+%%
+-module(group_history).
+-export([load/0, add/1]).
+
+%% Make a minimal size that should encompass set of lines and then make
+%% a file rotation for N files of this size.
+-define(DEFAULT_HISTORY_FILE, "erlang-shell-log").
+-define(MAX_HISTORY_FILES, 10).
+-define(DEFAULT_SIZE, 1024*512). % 512 kb total default
+-define(DEFAULT_STATUS, disabled).
+-define(MIN_HISTORY_SIZE, (50*1024)). % 50 kb, in bytes
+-define(DEFAULT_DROP, []).
+-define(DISK_LOG_FORMAT, internal). % since we want repairs
+-define(LOG_NAME, '$#group_history').
+-define(VSN, {0,1,0}).
+
+%%%%%%%%%%%%%%
+%%% PUBLIC %%%
+%%%%%%%%%%%%%%
+
+%% @doc Loads the shell history from memory. This function should only be
+%% called from group:server/3 to inject itself in the previous commands
+%% stack.
+-spec load() -> [string()].
+load() ->
+ wait_for_kernel_safe_sup(),
+ case history_status() of
+ enabled ->
+ case open_log() of
+ {ok, ?LOG_NAME} ->
+ read_full_log(?LOG_NAME);
+ {repaired, ?LOG_NAME, {recovered, Good}, {badbytes, Bad}} ->
+ report_repairs(?LOG_NAME, Good, Bad),
+ read_full_log(?LOG_NAME);
+ {error, {need_repair, _FileName}} ->
+ repair_log(?LOG_NAME);
+ {error, {arg_mismatch, repair, true, false}} ->
+ repair_log(?LOG_NAME);
+ {error, {name_already_open, _}} ->
+ show_rename_warning(),
+ read_full_log(?LOG_NAME);
+ {error, {size_mismatch, Current, New}} ->
+ show_size_warning(Current, New),
+ resize_log(?LOG_NAME, Current, New),
+ load();
+ {error, {invalid_header, {vsn, Version}}} ->
+ upgrade_version(?LOG_NAME, Version),
+ load();
+ {error, Reason} ->
+ handle_open_error(Reason),
+ disable_history(),
+ []
+ end;
+ _ ->
+ []
+ end.
+
+%% @doc adds a log line to the erlang history log, if configured to do so.
+-spec add(iodata()) -> ok.
+add(Line) -> add(Line, history_status()).
+
+add(Line, enabled) ->
+ case lists:member(Line, to_drop()) of
+ false ->
+ case disk_log:log(?LOG_NAME, Line) of
+ ok ->
+ ok;
+ {error, no_such_log} ->
+ _ = open_log(), % a wild attempt we hope works!
+ disk_log:log(?LOG_NAME, Line);
+ {error, _Other} ->
+ % just ignore, we're too late
+ ok
+ end;
+ true ->
+ ok
+ end;
+add(_Line, disabled) ->
+ ok.
+
+%%%%%%%%%%%%%%%
+%%% PRIVATE %%%
+%%%%%%%%%%%%%%%
+
+%% Because loading the shell happens really damn early, processes we depend on
+%% might not be there yet. Luckily, the load function is called from the shell
+%% after a new process has been spawned, so we can block in here
+wait_for_kernel_safe_sup() ->
+ case whereis(kernel_safe_sup) of
+ undefined ->
+ timer:sleep(50),
+ wait_for_kernel_safe_sup();
+ _ ->
+ ok
+ end.
+
+%% Repair the log out of band
+repair_log(Name) ->
+ Opts = lists:keydelete(size, 1, log_options()),
+ case disk_log:open(Opts) of
+ {repaired, ?LOG_NAME, {recovered, Good}, {badbytes, Bad}} ->
+ report_repairs(?LOG_NAME, Good, Bad);
+ _ ->
+ ok
+ end,
+ _ = disk_log:close(Name),
+ load().
+
+%% Return whether the shell history is enabled or not
+-spec history_status() -> enabled | disabled.
+history_status() ->
+ case is_user() orelse application:get_env(kernel, shell_history) of
+ true -> disabled; % don't run for user proc
+ {ok, enabled} -> enabled;
+ undefined -> ?DEFAULT_STATUS;
+ _ -> disabled
+ end.
+
+%% Return whether the user process is running this
+-spec is_user() -> boolean().
+is_user() ->
+ case process_info(self(), registered_name) of
+ {registered_name, user} -> true;
+ _ -> false
+ end.
+
+%% Open a disk_log file while ensuring the required path is there.
+open_log() ->
+ Opts = log_options(),
+ _ = ensure_path(Opts),
+ disk_log:open(Opts).
+
+%% Return logger options
+log_options() ->
+ Path = find_path(),
+ File = filename:join([Path, ?DEFAULT_HISTORY_FILE]),
+ Size = find_wrap_values(),
+ [{name, ?LOG_NAME},
+ {file, File},
+ {repair, true},
+ {format, internal},
+ {type, wrap},
+ {size, Size},
+ {distributed, []},
+ {notify, false},
+ {head, {vsn, ?VSN}},
+ {quiet, true},
+ {mode, read_write}].
+
+-spec ensure_path([{file, string()} | {atom(), _}, ...]) -> ok | {error, term()}.
+ensure_path(Opts) ->
+ {file, Path} = lists:keyfind(file, 1, Opts),
+ filelib:ensure_dir(Path).
+
+%% @private read the logs from an already open file. Treat closed files
+%% as wrong and returns an empty list to avoid crash loops in the shell.
+-spec read_full_log(term()) -> [string()].
+read_full_log(Name) ->
+ case disk_log:chunk(Name, start) of
+ {error, no_such_log} ->
+ show_unexpected_close_warning(),
+ [];
+ eof ->
+ [];
+ {Cont, Logs} ->
+ lists:reverse(maybe_drop_header(Logs) ++ read_full_log(Name, Cont))
+ end.
+
+read_full_log(Name, Cont) ->
+ case disk_log:chunk(Name, Cont) of
+ {error, no_such_log} ->
+ show_unexpected_close_warning(),
+ [];
+ eof ->
+ [];
+ {NextCont, Logs} ->
+ maybe_drop_header(Logs) ++ read_full_log(Name, NextCont)
+ end.
+
+maybe_drop_header([{vsn, _} | Rest]) -> Rest;
+maybe_drop_header(Logs) -> Logs.
+
+-spec handle_open_error(_) -> ok.
+handle_open_error({arg_mismatch, OptName, CurrentVal, NewVal}) ->
+ show('$#erlang-history-arg-mismatch',
+ "Log file argument ~p changed value from ~p to ~p "
+ "and cannot be automatically updated. Please clear the "
+ "history files and try again.~n",
+ [OptName, CurrentVal, NewVal]);
+handle_open_error({not_a_log_file, FileName}) ->
+ show_invalid_file_warning(FileName);
+handle_open_error({invalid_index_file, FileName}) ->
+ show_invalid_file_warning(FileName);
+handle_open_error({invalid_header, Term}) ->
+ show('$#erlang-history-invalid-header',
+ "Shell history expects to be able to use the log files "
+ "which currently have unknown headers (~p) and may belong to "
+ "another mechanism. History logging will be "
+ "disabled.~n",
+ [Term]);
+handle_open_error({file_error, FileName, Reason}) ->
+ show('$#erlang-history-file-error',
+ "Error handling File ~s. Reason: ~p~n"
+ "History logging will be disabled.~n",
+ [FileName, Reason]);
+handle_open_error(Err) ->
+ show_unexpected_warning({disk_log, open, 1}, Err).
+
+find_wrap_values() ->
+ ConfSize = case application:get_env(kernel, shell_history_file_bytes) of
+ undefined -> ?DEFAULT_SIZE;
+ {ok, S} -> S
+ end,
+ SizePerFile = max(?MIN_HISTORY_SIZE, ConfSize div ?MAX_HISTORY_FILES),
+ FileCount = if SizePerFile > ?MIN_HISTORY_SIZE ->
+ ?MAX_HISTORY_FILES
+ ; SizePerFile =< ?MIN_HISTORY_SIZE ->
+ max(1, ConfSize div SizePerFile)
+ end,
+ {SizePerFile, FileCount}.
+
+report_repairs(_, _, 0) ->
+ %% just a regular close repair
+ ok;
+report_repairs(_, Good, Bad) ->
+ show('$#erlang-history-report-repairs',
+ "The shell history log file was corrupted and was repaired. "
+ "~p bytes were recovered and ~p were lost.~n", [Good, Bad]).
+
+resize_log(Name, _OldSize, NewSize) ->
+ show('$#erlang-history-resize-attempt',
+ "Attempting to resize the log history file to ~p...", [NewSize]),
+ Opts = lists:keydelete(size, 1, log_options()),
+ _ = case disk_log:open(Opts) of
+ {error, {need_repair, _}} ->
+ _ = repair_log(Name),
+ disk_log:open(Opts);
+ _ ->
+ ok
+ end,
+ case disk_log:change_size(Name, NewSize) of
+ ok ->
+ show('$#erlang-history-resize-result',
+ "ok~n", []);
+ {error, {new_size_too_small, _}} ->
+ show('$#erlang-history-resize-result',
+ "failed (new size is too small)~n", []),
+ disable_history();
+ {error, Reason} ->
+ show('$#erlang-history-resize-result',
+ "failed (~p)~n", [Reason]),
+ disable_history()
+ end.
+
+upgrade_version(_Name, Unsupported) ->
+ %% We only know of one version and can't support a newer one
+ show('$#erlang-history-upgrade',
+ "The version for the shell logs found on disk (~p) is "
+ "not supported by the current version (~p)~n",
+ [Unsupported, ?VSN]),
+ disable_history().
+
+disable_history() ->
+ show('$#erlang-history-disable', "Disabling shell history logging.~n", []),
+ application:set_env(kernel, shell_history, force_disabled).
+
+find_path() ->
+ case application:get_env(kernel, shell_history_path) of
+ undefined -> filename:basedir(user_cache, "erlang-history");
+ {ok, Path} -> Path
+ end.
+
+to_drop() ->
+ case application:get_env(kernel, shell_history_drop) of
+ undefined ->
+ application:set_env(kernel, shell_history_drop, ?DEFAULT_DROP),
+ ?DEFAULT_DROP;
+ {ok, V} when is_list(V) -> [Ln++"\n" || Ln <- V];
+ {ok, _} -> ?DEFAULT_DROP
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Output functions %%%
+%%%%%%%%%%%%%%%%%%%%%%%%
+show_rename_warning() ->
+ show('$#erlang-history-rename-warn',
+ "A history file with a different path has already "
+ "been started for the shell of this node. The old "
+ "name will keep being used for this session.~n",
+ []).
+
+show_invalid_file_warning(FileName) ->
+ show('$#erlang-history-invalid-file',
+ "Shell history expects to be able to use the file ~s "
+ "which currently exists and is not a file usable for "
+ "history logging purposes. History logging will be "
+ "disabled.~n", [FileName]).
+
+show_unexpected_warning({M,F,A}, Term) ->
+ show('$#erlang-history-unexpected-return',
+ "unexpected return value from ~p:~p/~p: ~p~n"
+ "shell history will be disabled for this session.~n",
+ [M,F,A,Term]).
+
+show_unexpected_close_warning() ->
+ show('$#erlang-history-unexpected-close',
+ "The shell log file has mysteriousy closed. Ignoring "
+ "currently unread history.~n", []).
+
+show_size_warning(_Current, _New) ->
+ show('$#erlang-history-size',
+ "The configured log history file size is different from "
+ "the size of the log file on disk.~n", []).
+
+show(Key, Format, Args) ->
+ case get(Key) of
+ undefined ->
+ io:format(standard_error, Format, Args),
+ put(Key, true),
+ ok;
+ true ->
+ ok
+ end.
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 25e4ddd95c..1128ee3ec5 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -44,6 +44,7 @@
global_group,
global_search,
group,
+ group_history,
heart,
hipe_unified_loader,
inet6_tcp,
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 7831777726..afc32283ba 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -107,14 +107,19 @@ init_per_testcase(big_boot_embedded, Config) ->
_Else ->
{skip, "Needs crypto!"}
end;
-init_per_testcase(on_load_embedded, Config) ->
+init_per_testcase(on_load_embedded, Config0) ->
LibRoot = code:lib_dir(),
LinkName = filename:join(LibRoot, "on_load_app-1.0"),
- [{link_name,LinkName}|Config];
+ Config = [{link_name,LinkName}|Config0],
+ init_per_testcase(Config);
init_per_testcase(_Func, Config) ->
+ init_per_testcase(Config).
+
+init_per_testcase(Config) ->
P = code:get_path(),
[{code_path, P}|Config].
+
end_per_testcase(module_status, Config) ->
code:purge(?TESTMOD),
code:delete(?TESTMOD),
diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl
index 069df5a11d..2b11a0381f 100644
--- a/lib/kernel/test/disk_log_SUITE.erl
+++ b/lib/kernel/test/disk_log_SUITE.erl
@@ -2493,6 +2493,7 @@ error_repair(Conf) when is_list(Conf) ->
del(File, No),
ok = file:del_dir(Dir),
+ error_logger:add_report_handler(?MODULE, self()),
%% repair a file
P1 = pps(),
{ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap},
@@ -2509,6 +2510,8 @@ error_repair(Conf) when is_list(Conf) ->
ok = disk_log:close(n),
true = (P1 == pps()),
del(File, No),
+ receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok
+ after 1000 -> ct:fail(failed) end,
%% yet another repair
P2 = pps(),
@@ -2525,6 +2528,8 @@ error_repair(Conf) when is_list(Conf) ->
ok = disk_log:close(n),
true = (P2 == pps()),
del(File, No),
+ receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok
+ after 1000 -> ct:fail(failed) end,
%% Repair, large term
Big = term_to_binary(lists:duplicate(66000,$a)),
@@ -2540,6 +2545,8 @@ error_repair(Conf) when is_list(Conf) ->
ok = disk_log:close(n),
Got = Big,
del(File, No),
+ receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok
+ after 1000 -> ct:fail(failed) end,
%% A term a little smaller than a chunk, then big terms.
BigSmall = mk_bytes(1024*64-8-12),
@@ -2560,6 +2567,8 @@ error_repair(Conf) when is_list(Conf) ->
{type, halt}, {format, internal}]),
ok = disk_log:close(n),
file:delete(File),
+ receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok
+ after 1000 -> ct:fail(failed) end,
%% The header is recovered.
{ok,n} =
@@ -2573,12 +2582,13 @@ error_repair(Conf) when is_list(Conf) ->
crash(File, 30),
{repaired,n,{recovered,3},{badbytes,15}} =
disk_log:open([{name, n}, {file, File}, {type, halt},
- {format, internal},{repair,true},
+ {format, internal},{repair,true}, {quiet, true},
{head_func, {?MODULE, head_fun, [{ok,"head"}]}}]),
["head",'of',terms] = get_all_terms(n),
ok = disk_log:close(n),
-
+ error_logger:delete_report_handler(?MODULE),
file:delete(File),
+ {messages, []} = process_info(self(), messages),
ok.
@@ -5007,6 +5017,9 @@ init(Tester) ->
handle_event({error_report, _GL, {Pid, crash_report, Report}}, Tester) ->
Tester ! {crash_report, Pid, Report},
{ok, Tester};
+handle_event({info_msg, _GL, {Pid, F,A}}, Tester) ->
+ Tester ! {info_msg, Pid, F, A},
+ {ok, Tester};
handle_event(_Event, State) ->
{ok, State}.
diff --git a/lib/observer/src/etop.erl b/lib/observer/src/etop.erl
index 925f4456bb..2093e6a0d7 100644
--- a/lib/observer/src/etop.erl
+++ b/lib/observer/src/etop.erl
@@ -180,10 +180,16 @@ stop(Opts) ->
end,
unregister(etop_server).
-update(#opts{store=Store,node=Node,tracing=Tracing}=Opts) ->
+update(#opts{store=Store,node=Node,tracing=Tracing,intv=Interval}=Opts) ->
Pid = spawn_link(Node,observer_backend,etop_collect,[self()]),
Info = receive {Pid,I} -> I
- after 1000 -> exit(connection_lost)
+ after Interval ->
+ %% Took more than the update interval to fetch
+ %% data. Either the connection is lost or the
+ %% fetching took too long...
+ io:format("Timeout when waiting for process info from "
+ "node ~p; exiting~n", [Node]),
+ exit(timeout)
end,
#etop_info{procinfo=ProcInfo} = Info,
ProcInfo1 =
diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl
index 9506a2b380..ef425f0874 100644
--- a/lib/observer/src/observer_alloc_wx.erl
+++ b/lib/observer/src/observer_alloc_wx.erl
@@ -194,14 +194,17 @@ code_change(_, _, State) ->
%%%%%%%%%%
restart_fetcher(Node, #state{panel=Panel, wins=Wins0, time=Ti} = State) ->
- SysInfo = observer_wx:try_rpc(Node, observer_backend, sys_info, []),
- Info = alloc_info(SysInfo),
- Max = lists:foldl(fun calc_max/2, #{}, Info),
- {Wins, Samples} = add_data(Info, {0, queue:new()}, Wins0, Ti, true),
- erlang:send_after(1000 div ?DISP_FREQ, self(), {refresh, 0}),
- wxWindow:refresh(Panel),
- precalc(State#state{active=true, appmon=Node, time=Ti#ti{tick=0},
- wins=Wins, samples=Samples, max=Max}).
+ case rpc:call(Node, observer_backend, sys_info, []) of
+ {badrpc, _} -> State;
+ SysInfo ->
+ Info = alloc_info(SysInfo),
+ Max = lists:foldl(fun calc_max/2, #{}, Info),
+ {Wins, Samples} = add_data(Info, {0, queue:new()}, Wins0, Ti, true),
+ erlang:send_after(1000 div ?DISP_FREQ, self(), {refresh, 0}),
+ wxWindow:refresh(Panel),
+ precalc(State#state{active=true, appmon=Node, time=Ti#ti{tick=0},
+ wins=Wins, samples=Samples, max=Max})
+ end.
precalc(#state{samples=Data0, paint=Paint, time=Ti, wins=Wins0}=State) ->
Wins = [precalc(Ti, Data0, Paint, Win) || Win <- Wins0],
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index ffa6f6d3b4..3083297f31 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -67,12 +67,14 @@
-record(holder, {parent,
info,
- etop,
+ next=[],
sort=#sort{},
accum=[],
+ next_accum=[],
attrs,
node,
- backend_pid
+ backend_pid,
+ old_backend=false
}).
-record(state, {parent,
@@ -226,7 +228,7 @@ handle_info({holder_updated, Count}, State0=#state{grid=Grid}) ->
wxListCtrl:setItemCount(Grid, Count),
Count > 0 andalso wxListCtrl:refreshItems(Grid, 0, Count-1),
-
+ observer_wx:set_status(io_lib:format("Number of Processes: ~w", [Count])),
{noreply, State};
handle_info(refresh_interval, #state{holder=Holder}=State) ->
@@ -459,13 +461,13 @@ rm_selected(_, [], [], AccIds, AccPids) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_table_holder(Parent, Accum0, Attrs) ->
- Backend = spawn_link(node(), observer_backend,etop_collect,[self()]),
+ process_flag(trap_exit, true),
+ Backend = spawn_link(node(), observer_backend, procs_info, [self()]),
Accum = case Accum0 of
true -> true;
false -> []
end,
table_holder(#holder{parent=Parent,
- etop=#etop_info{},
info=array:new(),
node=node(),
backend_pid=Backend,
@@ -474,7 +476,7 @@ init_table_holder(Parent, Accum0, Attrs) ->
}).
table_holder(#holder{info=Info, attrs=Attrs,
- node=Node, backend_pid=Backend}=S0) ->
+ node=Node, backend_pid=Backend, old_backend=Old}=S0) ->
receive
{get_row, From, Row, Col} ->
get_row(From, Row, Col, Info),
@@ -482,14 +484,25 @@ table_holder(#holder{info=Info, attrs=Attrs,
{get_attr, From, Row} ->
get_attr(From, Row, Attrs),
table_holder(S0);
+ {procs_info, Backend, Procs} ->
+ State = handle_update(Procs, S0),
+ table_holder(State);
+ {'EXIT', Backend, normal} when Old =:= false ->
+ S1 = update_complete(S0),
+ table_holder(S1#holder{backend_pid=undefined});
{Backend, EtopInfo=#etop_info{}} ->
- State = handle_update(EtopInfo, S0),
+ State = handle_update_old(EtopInfo, S0),
table_holder(State#holder{backend_pid=undefined});
refresh when is_pid(Backend)->
table_holder(S0); %% Already updating
refresh ->
- Pid = spawn_link(Node,observer_backend,etop_collect,[self()]),
- table_holder(S0#holder{backend_pid=Pid});
+ Pid = case Old of
+ true ->
+ spawn_link(Node, observer_backend, etop_collect, [self()]);
+ false ->
+ spawn_link(Node, observer_backend, procs_info, [self()])
+ end,
+ table_holder(S0#holder{backend_pid=Pid});
{change_sort, Col} ->
State = change_sort(Col, S0),
table_holder(State);
@@ -502,7 +515,6 @@ table_holder(#holder{info=Info, attrs=Attrs,
{get_name_or_pid, From, Indices} ->
get_name_or_pid(From, Indices, Info),
table_holder(S0);
-
{get_node, From} ->
From ! {self(), Node},
table_holder(S0);
@@ -511,36 +523,50 @@ table_holder(#holder{info=Info, attrs=Attrs,
true ->
table_holder(S0);
false ->
- self() ! refresh,
- table_holder(S0#holder{node=NewNode})
- end;
+ _ = rpc:call(NewNode, code, ensure_loaded, [observer_backend]),
+ case rpc:call(NewNode, erlang, function_exported,
+ [observer_backend,procs_info, 1]) of
+ true ->
+ self() ! refresh,
+ table_holder(S0#holder{node=NewNode, old_backend=false});
+ false ->
+ self() ! refresh,
+ table_holder(S0#holder{node=NewNode, old_backend=true});
+ _ ->
+ table_holder(S0)
+ end
+ end;
{accum, Bool} ->
table_holder(change_accum(Bool,S0));
{get_accum, From} ->
From ! {self(), S0#holder.accum == true},
table_holder(S0);
{dump, Fd} ->
- EtopInfo = (S0#holder.etop)#etop_info{procinfo=array:to_list(Info)},
- %% The empty #etop_info{} below is a dummy previous info
- %% value. It is used by etop to calculate the scheduler
- %% utilization since last update. When dumping to file,
- %% there is no previous measurement to use, so we just add
- %% a dummy here, and the value shown will be since the
- %% tool was started.
- etop_txt:do_update(Fd, EtopInfo, #etop_info{}, #opts{node=Node}),
- file:close(Fd),
- table_holder(S0);
+ Collector = spawn_link(Node, observer_backend, etop_collect,[self()]),
+ receive
+ {Collector, EtopInfo=#etop_info{}} ->
+ etop_txt:do_update(Fd, EtopInfo, #etop_info{}, #opts{node=Node}),
+ file:close(Fd),
+ table_holder(S0);
+ {'EXIT', Collector, _} ->
+ table_holder(S0)
+ end;
stop ->
ok;
- What ->
- io:format("Table holder got ~p~n",[What]),
+ {'EXIT', Backend, normal} ->
+ table_holder(S0);
+ {'EXIT', Backend, _Reason} ->
+ %% Node crashed will be noticed soon..
+ table_holder(S0#holder{backend_pid=undefined});
+ _What ->
+ %% io:format("~p: Table holder got ~p~n",[?MODULE, _What]),
table_holder(S0)
end.
change_sort(Col, S0=#holder{parent=Parent, info=Data, sort=Sort0}) ->
{Sort, ProcInfo}=sort(Col, Sort0, Data),
Parent ! {holder_updated, array:size(Data)},
- S0#holder{info=ProcInfo, sort=Sort}.
+ S0#holder{info=array:from_list(ProcInfo), sort=Sort}.
change_accum(true, S0) ->
S0#holder{accum=true};
@@ -548,23 +574,45 @@ change_accum(false, S0=#holder{info=Info}) ->
self() ! refresh,
S0#holder{accum=lists:sort(array:to_list(Info))}.
-handle_update(EI=#etop_info{procinfo=ProcInfo0},
- S0=#holder{parent=Parent, sort=Sort=#sort{sort_key=KeyField}}) ->
- {ProcInfo1, S1} = accum(ProcInfo0, S0),
+handle_update_old(#etop_info{procinfo=ProcInfo0},
+ S0=#holder{parent=Parent, sort=Sort=#sort{sort_key=KeyField}}) ->
+ {ProcInfo1, Accum} = accum(ProcInfo0, S0),
{_SO, ProcInfo} = sort(KeyField, Sort#sort{sort_key=undefined}, ProcInfo1),
- Parent ! {holder_updated, array:size(ProcInfo)},
- S1#holder{info=ProcInfo, etop=EI#etop_info{procinfo=[]}}.
+ Info = array:from_list(ProcInfo),
+ Parent ! {holder_updated, array:size(Info)},
+ S0#holder{info=Info, accum=Accum}.
+
+handle_update(ProcInfo0, S0=#holder{next=Next, sort=#sort{sort_key=KeyField}}) ->
+ {ProcInfo1, Accum} = accum(ProcInfo0, S0),
+ Sort = sort_fun(KeyField, true),
+ Merge = merge_fun(KeyField),
+ Merged = Merge(Sort(ProcInfo1), Next),
+ case Accum of
+ true -> S0#holder{next=Merged};
+ _List -> S0#holder{next=Merged, next_accum=Accum}
+ end.
-accum(ProcInfo, State=#holder{accum=true}) ->
- {ProcInfo, State};
-accum(ProcInfo0, State=#holder{accum=Previous}) ->
+update_complete(#holder{parent=Parent, sort=#sort{sort_incr=Incr},
+ next=ProcInfo, accum=Accum, next_accum=NextAccum}=S0) ->
+ Info = case Incr of
+ true -> array:from_list(ProcInfo);
+ false -> array:from_list(lists:reverse(ProcInfo))
+ end,
+ Parent ! {holder_updated, array:size(Info)},
+ S0#holder{info=Info, accum= Accum =:= true orelse NextAccum,
+ next=[], next_accum=[]}.
+
+accum(ProcInfo, #holder{accum=true}) ->
+ {ProcInfo, true};
+accum(ProcInfo0, #holder{accum=Previous, next_accum=Next}) ->
+ Accum = [{Pid, Reds} || #etop_proc_info{pid=Pid, reds=Reds} <- ProcInfo0],
ProcInfo = lists:sort(ProcInfo0),
- {accum2(ProcInfo,Previous,[]), State#holder{accum=ProcInfo}}.
+ {accum2(ProcInfo,Previous,[]), lists:merge(lists:sort(Accum), Next)}.
-accum2([PI=#etop_proc_info{pid=Pid, reds=Reds, runtime=RT}|PIs],
- [#etop_proc_info{pid=Pid, reds=OldReds, runtime=OldRT}|Old], Acc) ->
- accum2(PIs, Old, [PI#etop_proc_info{reds=Reds-OldReds, runtime=RT-OldRT}|Acc]);
-accum2(PIs=[#etop_proc_info{pid=Pid}|_], [#etop_proc_info{pid=OldPid}|Old], Acc)
+accum2([PI=#etop_proc_info{pid=Pid, reds=Reds}|PIs],
+ [{Pid, OldReds}|Old], Acc) ->
+ accum2(PIs, Old, [PI#etop_proc_info{reds=Reds-OldReds}|Acc]);
+accum2(PIs=[#etop_proc_info{pid=Pid}|_], [{OldPid,_}|Old], Acc)
when Pid > OldPid ->
accum2(PIs, Old, Acc);
accum2([PI|PIs], Old, Acc) ->
@@ -575,14 +623,52 @@ sort(Col, Opt, Table)
when not is_list(Table) ->
sort(Col,Opt,array:to_list(Table));
sort(Col, Opt=#sort{sort_key=Col, sort_incr=Bool}, Table) ->
- {Opt#sort{sort_incr=not Bool},
- array:from_list(lists:reverse(Table))};
-sort(Col, S=#sort{sort_incr=true}, Table) ->
- {S#sort{sort_key=Col},
- array:from_list(lists:keysort(col_to_element(Col), Table))};
-sort(Col, S=#sort{sort_incr=false}, Table) ->
- {S#sort{sort_key=Col},
- array:from_list(lists:reverse(lists:keysort(col_to_element(Col), Table)))}.
+ {Opt#sort{sort_incr=not Bool},lists:reverse(Table)};
+sort(Col, S=#sort{sort_incr=Incr}, Table) ->
+ Sort = sort_fun(Col, Incr),
+ {S#sort{sort_key=Col}, Sort(Table)}.
+
+sort_fun(?COL_NAME, true) ->
+ fun(Table) -> lists:sort(fun sort_name/2, Table) end;
+sort_fun(?COL_NAME, false) ->
+ fun(Table) -> lists:sort(fun sort_name_rev/2, Table) end;
+sort_fun(Col, true) ->
+ N = col_to_element(Col),
+ fun(Table) -> lists:keysort(N, Table) end;
+sort_fun(Col, false) ->
+ N = col_to_element(Col),
+ fun(Table) -> lists:reverse(lists:keysort(N, Table)) end.
+
+merge_fun(?COL_NAME) ->
+ fun(A,B) -> lists:merge(fun sort_name/2, A, B) end;
+merge_fun(Col) ->
+ KeyField = col_to_element(Col),
+ fun(A,B) -> lists:keymerge(KeyField, A, B) end.
+
+
+sort_name(#etop_proc_info{name={_,_,_}=A}, #etop_proc_info{name={_,_,_}=B}) ->
+ A =< B;
+sort_name(#etop_proc_info{name=A}, #etop_proc_info{name=B})
+ when is_atom(A), is_atom(B) ->
+ A =< B;
+sort_name(#etop_proc_info{name=Reg}, #etop_proc_info{name={M,_F,_A}})
+ when is_atom(Reg) ->
+ Reg < M;
+sort_name(#etop_proc_info{name={M,_,_}}, #etop_proc_info{name=Reg})
+ when is_atom(Reg) ->
+ M < Reg.
+
+sort_name_rev(#etop_proc_info{name={_,_,_}=A}, #etop_proc_info{name={_,_,_}=B}) ->
+ A >= B;
+sort_name_rev(#etop_proc_info{name=A}, #etop_proc_info{name=B})
+ when is_atom(A), is_atom(B) ->
+ A >= B;
+sort_name_rev(#etop_proc_info{name=Reg}, #etop_proc_info{name={M,_F,_A}})
+ when is_atom(Reg) ->
+ Reg >= M;
+sort_name_rev(#etop_proc_info{name={M,_,_}}, #etop_proc_info{name=Reg})
+ when is_atom(Reg) ->
+ M >= Reg.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 739761e7fd..9b9e80f479 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -143,7 +143,8 @@ setup(#state{frame = Frame} = State) ->
wxFrame:setTitle(Frame, atom_to_list(node())),
wxStatusBar:setStatusText(StatusBar, atom_to_list(node())),
- wxNotebook:connect(Notebook, command_notebook_page_changed, [{skip, true}]),
+ wxNotebook:connect(Notebook, command_notebook_page_changed,
+ [{skip, true}, {id, ?ID_NOTEBOOK}]),
wxFrame:connect(Frame, close_window, []),
wxMenu:connect(Frame, command_menu_selected),
wxFrame:show(Frame),
@@ -230,12 +231,13 @@ setup(#state{frame = Frame} = State) ->
%%Callbacks
handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changed, nSel=Next}},
- #state{active_tab=Previous, node=Node, panels=Panels} = State) ->
+ #state{active_tab=Previous, node=Node, panels=Panels, status_bar=SB} = State) ->
{_, Obj, _} = lists:nth(Next+1, Panels),
case wx_object:get_pid(Obj) of
Previous ->
{noreply, State};
Pid ->
+ wxStatusBar:setStatusText(SB, ""),
Previous ! not_active,
Pid ! {active, Node},
{noreply, State#state{active_tab=Pid}}
diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index 1e0d2d642e..d36af257ce 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -23,7 +23,7 @@
-export([vsn/0]).
%% observer stuff
--export([sys_info/0, get_port_list/0,
+-export([sys_info/0, get_port_list/0, procs_info/1,
get_table/3, get_table_list/2, fetch_stats/2]).
%% etop stuff
@@ -293,6 +293,23 @@ fetch_stats_loop(Parent, Time) ->
try erlang:memory() catch _:_ -> [] end},
fetch_stats_loop(Parent, Time)
end.
+
+%%
+%% Chunk sending process info to etop/observer
+%%
+procs_info(Collector) ->
+ All = processes(),
+ Send = fun Send (Pids) ->
+ try lists:split(10000, Pids) of
+ {First, Rest} ->
+ Collector ! {procs_info, self(), etop_collect(First, [])},
+ Send(Rest)
+ catch _:_ ->
+ Collector ! {procs_info, self(), etop_collect(Pids, [])}
+ end
+ end,
+ Send(All).
+
%%
%% etop backend
%%
diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl
index 14702b0ad2..eb454155d5 100644
--- a/lib/sasl/src/sasl_report.erl
+++ b/lib/sasl/src/sasl_report.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -68,12 +68,12 @@ write_report2(IO, Fd, Head, progress, Report) ->
Format = format_key_val(Report),
write_report_action(IO, Fd, Head, "~s", [Format]);
write_report2(IO, Fd, Head, crash_report, Report) ->
- Depth = get_depth(),
+ Depth = error_logger:get_format_depth(),
Format = proc_lib:format(Report, latin1, Depth),
write_report_action(IO, Fd, Head, "~s", [Format]).
supervisor_format(Args0) ->
- case get_depth() of
+ case error_logger:get_format_depth() of
unlimited ->
{" Supervisor: ~p~n"
" Context: ~p~n"
@@ -102,14 +102,6 @@ format_key_val([{Tag,Data}|Rep]) ->
format_key_val(_) ->
[].
-get_depth() ->
- case application:get_env(kernel, error_logger_format_depth) of
- {ok, Depth} when is_integer(Depth) ->
- max(10, Depth);
- undefined ->
- unlimited
- end.
-
sup_get(Tag, Report) ->
case lists:keysearch(Tag, 1, Report) of
{value, {_, Value}} ->
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 84b7cdd7a1..5c9ce3d5fb 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -246,10 +246,12 @@
<tag><c><![CDATA[{pref_public_key_algs, list()}]]></c></tag>
<item>
<p>List of user (client) public key algorithms to try to use.</p>
- <p>The default value is
- <c><![CDATA[['ssh-rsa','ssh-dss','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521'] ]]></c>
+ <p>The default value is the <c>public_key</c> entry in
+ <seealso marker="#default_algorithms/0">ssh:default_algorithms/0</seealso>.
+ </p>
+ <p>If there is no public key of a specified type available, the corresponding entry is ignored.
+ Note that the available set is dependent on the underlying cryptolib and current user's public keys.
</p>
- <p>If there is no public key of a specified type available, the corresponding entry is ignored.</p>
</item>
<tag><c><![CDATA[{preferred_algorithms, algs_list()}]]></c></tag>
@@ -293,6 +295,15 @@
connection. For <c>gen_tcp</c> the time is in milli-seconds and the default value is
<c>infinity</c>.</p>
</item>
+
+ <tag><c><![CDATA[{auth_methods, string()}]]></c></tag>
+ <item>
+ <p>Comma-separated string that determines which
+ authentication methods that the client shall support and
+ in which order they are tried. Defaults to
+ <c><![CDATA["publickey,keyboard-interactive,password"]]></c></p>
+ </item>
+
<tag><c><![CDATA[{user, string()}]]></c></tag>
<item>
<p>Provides a username. If this option is not given, <c>ssh</c>
@@ -300,6 +311,7 @@
<c><![CDATA[USER]]></c> on UNIX,
<c><![CDATA[USERNAME]]></c> on Windows).</p>
</item>
+
<tag><c><![CDATA[{password, string()}]]></c></tag>
<item>
<p>Provides a password for password authentication.
@@ -307,6 +319,7 @@
password, if the password authentication method is
attempted.</p>
</item>
+
<tag><c><![CDATA[{key_cb, key_cb()}]]></c></tag>
<item>
<p>Module implementing the behaviour <seealso
@@ -316,6 +329,7 @@
module via the options passed to it under the key 'key_cb_private'.
</p>
</item>
+
<tag><c><![CDATA[{quiet_mode, atom() = boolean()}]]></c></tag>
<item>
<p>If <c>true</c>, the client does not print anything on authorization.</p>
@@ -466,6 +480,7 @@
authentication methods that the server is to support and
in what order they are tried. Defaults to
<c><![CDATA["publickey,keyboard-interactive,password"]]></c></p>
+ <p>Note that the client is free to use any order and to exclude methods.</p>
</item>
<tag><c><![CDATA[{auth_method_kb_interactive_data, PromptTexts}]]></c>
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 342583306b..6a6b9896cb 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -434,11 +434,7 @@ init_ssh_record(Role, Socket, Opts) ->
init_ssh_record(Role, _Socket, PeerAddr, Opts) ->
KeyCb = ?GET_OPT(key_cb, Opts),
- AuthMethods =
- case Role of
- server -> ?GET_OPT(auth_methods, Opts);
- client -> undefined
- end,
+ AuthMethods = ?GET_OPT(auth_methods, Opts),
S0 = #ssh{role = Role,
key_cb = KeyCb,
opts = Opts,
@@ -1705,15 +1701,18 @@ handle_ssh_msg_ext_info(#ssh_msg_ext_info{data=Data}, D0) ->
lists:foldl(fun ext_info/2, D0, Data).
-ext_info({"server-sig-algs",SigAlgs}, D0 = #data{ssh_params=#ssh{role=client}=Ssh0}) ->
+ext_info({"server-sig-algs",SigAlgs}, D0 = #data{ssh_params=#ssh{role=client,
+ userauth_pubkeys=ClientSigAlgs}=Ssh0}) ->
%% Make strings to eliminate risk of beeing bombed with odd strings that fills the atom table:
SupportedAlgs = lists:map(fun erlang:atom_to_list/1, ssh_transport:supported_algorithms(public_key)),
- Ssh = Ssh0#ssh{userauth_pubkeys =
- [list_to_atom(SigAlg) || SigAlg <- string:tokens(SigAlgs,","),
- %% length of SigAlg is implicitly checked by member:
- lists:member(SigAlg, SupportedAlgs)
- ]},
- D0#data{ssh_params = Ssh};
+ ServerSigAlgs = [list_to_atom(SigAlg) || SigAlg <- string:tokens(SigAlgs,","),
+ %% length of SigAlg is implicitly checked by the comparison
+ %% in member/2:
+ lists:member(SigAlg, SupportedAlgs)
+ ],
+ CommonAlgs = [Alg || Alg <- ServerSigAlgs,
+ lists:member(Alg, ClientSigAlgs)],
+ D0#data{ssh_params = Ssh0#ssh{userauth_pubkeys = CommonAlgs} };
ext_info(_, D0) ->
%% Not implemented
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index 6692432fcf..33792da38f 100644
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -75,10 +75,7 @@ host_key(Algorithm, Opts) ->
Password = proplists:get_value(identity_pass_phrase(Algorithm), Opts, ignore),
case decode(File, Password) of
{ok,Key} ->
- case ssh_transport:valid_key_sha_alg(Key,Algorithm) of
- true -> {ok,Key};
- false -> {error,bad_keytype_in_file}
- end;
+ check_key_type(Key, Algorithm);
{error,DecodeError} ->
{error,DecodeError}
end.
@@ -104,10 +101,20 @@ is_host_key(Key, PeerName, Algorithm, Opts) ->
user_key(Algorithm, Opts) ->
File = file_name(user, identity_key_filename(Algorithm), Opts),
Password = proplists:get_value(identity_pass_phrase(Algorithm), Opts, ignore),
- decode(File, Password).
+ case decode(File, Password) of
+ {ok, Key} ->
+ check_key_type(Key, Algorithm);
+ Error ->
+ Error
+ end.
%% Internal functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+check_key_type(Key, Algorithm) ->
+ case ssh_transport:valid_key_sha_alg(Key,Algorithm) of
+ true -> {ok,Key};
+ false -> {error,bad_keytype_in_file}
+ end.
file_base_name('ssh-rsa' ) -> "ssh_host_rsa_key";
file_base_name('rsa-sha2-256' ) -> "ssh_host_rsa_key";
diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
index 0886d5b34d..12c0190082 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -293,12 +293,6 @@ default(server) ->
class => user_options
},
- {auth_methods, def} =>
- #{default => ?SUPPORTED_AUTH_METHODS,
- chk => fun check_string/1,
- class => user_options
- },
-
{auth_method_kb_interactive_data, def} =>
#{default => undefined, % Default value can be constructed when User is known
chk => fun({S1,S2,S3,B}) ->
@@ -398,6 +392,12 @@ default(server) ->
class => user_options
},
+ {preferred_algorithms, def} =>
+ #{default => ssh:default_algorithms(),
+ chk => fun check_preferred_algorithms/1,
+ class => user_options
+ },
+
%%%%% Undocumented
{infofun, def} =>
#{default => fun(_,_,_) -> void end,
@@ -436,12 +436,24 @@ default(client) ->
},
{pref_public_key_algs, def} =>
- #{default =>
- ssh_transport:supported_algorithms(public_key),
- chk =>
- fun check_pref_public_key_algs/1,
- class =>
- ssh
+ #{default => ssh_transport:default_algorithms(public_key) -- ['rsa-sha2-256',
+ 'rsa-sha2-512'],
+ chk => fun check_pref_public_key_algs/1,
+ class => user_options
+ },
+
+ {preferred_algorithms, def} =>
+ #{default => [{K,Vs} || {K,Vs0} <- ssh:default_algorithms(),
+ Vs <- [case K of
+ public_key ->
+ Vs0 -- ['rsa-sha2-256',
+ 'rsa-sha2-512'];
+ _ ->
+ Vs0
+ end]
+ ],
+ chk => fun check_preferred_algorithms/1,
+ class => user_options
},
{dh_gex_limits, def} =>
@@ -509,12 +521,6 @@ default(common) ->
class => user_options
},
- {preferred_algorithms, def} =>
- #{default => ssh:default_algorithms(),
- chk => fun check_preferred_algorithms/1,
- class => user_options
- },
-
{id_string, def} =>
#{default => undefined, % FIXME: see ssh_transport:ssh_vsn/0
chk => fun(random) ->
@@ -582,6 +588,21 @@ default(common) ->
class => user_options
},
+ {auth_methods, def} =>
+ #{default => ?SUPPORTED_AUTH_METHODS,
+ chk => fun(As) ->
+ try
+ Sup = string:tokens(?SUPPORTED_AUTH_METHODS, ","),
+ New = string:tokens(As, ","),
+ [] == [X || X <- New,
+ not lists:member(X,Sup)]
+ catch
+ _:_ -> false
+ end
+ end,
+ class => user_options
+ },
+
%%%%% Undocumented
{transport, def} =>
#{default => ?DEFAULT_TRANSPORT,
@@ -808,16 +829,23 @@ valid_hash(X, _) -> error_in_check(X, "Expect atom or list in fingerprint spec"
%%%----------------------------------------------------------------
check_preferred_algorithms(Algs) ->
+ [error_in_check(K,"Bad preferred_algorithms key")
+ || {K,_} <- Algs,
+ not lists:keymember(K,1,ssh:default_algorithms())],
+
try alg_duplicates(Algs, [], [])
of
[] ->
{true,
- [try ssh_transport:supported_algorithms(Key)
- of
- DefAlgs -> handle_pref_alg(Key,Vals,DefAlgs)
- catch
- _:_ -> error_in_check(Key,"Bad preferred_algorithms key")
- end || {Key,Vals} <- Algs]
+ [case proplists:get_value(Key, Algs) of
+ undefined ->
+ {Key,DefAlgs};
+ Vals ->
+ handle_pref_alg(Key,Vals,SupAlgs)
+ end
+ || {{Key,DefAlgs}, {Key,SupAlgs}} <- lists:zip(ssh:default_algorithms(),
+ ssh_transport:supported_algorithms())
+ ]
};
Dups ->
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 25c64a4f25..1a15798080 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -92,10 +92,7 @@ default_algorithms(cipher) ->
default_algorithms(mac) ->
supported_algorithms(mac, same(['AEAD_AES_128_GCM',
'AEAD_AES_256_GCM']));
-default_algorithms(public_key) ->
- supported_algorithms(public_key, ['rsa-sha2-256',
- 'rsa-sha2-384',
- 'rsa-sha2-512']);
+
default_algorithms(Alg) ->
supported_algorithms(Alg, []).
@@ -122,10 +119,9 @@ supported_algorithms(public_key) ->
{'ecdsa-sha2-nistp384', [{public_keys,ecdsa}, {hashs,sha384}, {ec_curve,secp384r1}]},
{'ecdsa-sha2-nistp521', [{public_keys,ecdsa}, {hashs,sha512}, {ec_curve,secp521r1}]},
{'ecdsa-sha2-nistp256', [{public_keys,ecdsa}, {hashs,sha256}, {ec_curve,secp256r1}]},
+ {'ssh-rsa', [{public_keys,rsa}, {hashs,sha} ]},
{'rsa-sha2-256', [{public_keys,rsa}, {hashs,sha256} ]},
- {'rsa-sha2-384', [{public_keys,rsa}, {hashs,sha384} ]},
{'rsa-sha2-512', [{public_keys,rsa}, {hashs,sha512} ]},
- {'ssh-rsa', [{public_keys,rsa}, {hashs,sha} ]},
{'ssh-dss', [{public_keys,dss}, {hashs,sha} ]} % Gone in OpenSSH 7.3.p1
]);
@@ -724,14 +720,28 @@ kex_ext_info(Role, Opts) ->
end.
ext_info_message(#ssh{role=client,
- send_ext_info=true} = Ssh0) ->
- %% FIXME: no extensions implemented
- {ok, "", Ssh0};
+ send_ext_info=true,
+ opts=Opts} = Ssh0) ->
+ %% Since no extension sent by the client is implemented, we add a fake one
+ %% to be able to test the framework.
+ %% Remove this when there is one and update ssh_protocol_SUITE whare it is used.
+ case proplists:get_value(ext_info_client, ?GET_OPT(tstflg,Opts)) of
+ true ->
+ Msg = #ssh_msg_ext_info{nr_extensions = 1,
+ data = [{"[email protected]", "Testing,PleaseIgnore"}]
+ },
+ {SshPacket, Ssh} = ssh_packet(Msg, Ssh0),
+ {ok, SshPacket, Ssh};
+ _ ->
+ {ok, "", Ssh0}
+ end;
ext_info_message(#ssh{role=server,
- send_ext_info=true} = Ssh0) ->
+ send_ext_info=true,
+ opts = Opts} = Ssh0) ->
AlgsList = lists:map(fun erlang:atom_to_list/1,
- ssh_transport:default_algorithms(public_key)),
+ proplists:get_value(public_key,
+ ?GET_OPT(preferred_algorithms, Opts))),
Msg = #ssh_msg_ext_info{nr_extensions = 1,
data = [{"server-sig-algs", string:join(AlgsList,",")}]
},
diff --git a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
index 410a9ea983..0995182623 100644
--- a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
+++ b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
@@ -284,8 +284,18 @@ fix_asym(#ssh_msg_global_request{name=N} = M) -> M#ssh_msg_global_request{name =
fix_asym(#ssh_msg_debug{message=D,language=L} = M) -> M#ssh_msg_debug{message = binary_to_list(D),
language = binary_to_list(L)};
fix_asym(#ssh_msg_kexinit{cookie=C} = M) -> M#ssh_msg_kexinit{cookie = <<C:128>>};
+
+fix_asym(#ssh_msg_kexdh_reply{public_host_key = Key} = M) -> M#ssh_msg_kexdh_reply{public_host_key = key_sigalg(Key)};
+fix_asym(#ssh_msg_kex_dh_gex_reply{public_host_key = Key} = M) -> M#ssh_msg_kex_dh_gex_reply{public_host_key = key_sigalg(Key)};
+fix_asym(#ssh_msg_kex_ecdh_reply{public_host_key = Key} = M) -> M#ssh_msg_kex_ecdh_reply{public_host_key = key_sigalg(Key)};
+
fix_asym(M) -> M.
+%%% Keys now contains an sig-algorithm name
+key_sigalg(#'RSAPublicKey'{} = Key) -> {Key,'ssh-rsa'};
+key_sigalg({_, #'Dss-Parms'{}} = Key) -> {Key,'ssh-dss'};
+key_sigalg({#'ECPoint'{}, {namedCurve,OID}} = Key) -> {Key,"ecdsa-sha2-256"}.
+
%%% Message codes 30 and 31 are overloaded depending on kex family so arrange the decoder
%%% input as the test object does
decode_state(<<30,_/binary>>=Msg, KexFam) -> <<KexFam/binary, Msg/binary>>;
diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl
index 6e6269d3e0..736461624d 100644
--- a/lib/ssh/test/ssh_algorithms_SUITE.erl
+++ b/lib/ssh/test/ssh_algorithms_SUITE.erl
@@ -68,7 +68,7 @@ groups() ->
TagGroupSet ++ AlgoTcSet.
-tags() -> [kex,cipher,mac,compression].
+tags() -> [kex,cipher,mac,compression,public_key].
two_way_tags() -> [cipher,mac,compression].
%%--------------------------------------------------------------------
@@ -123,20 +123,35 @@ init_per_group(Group, Config) ->
Tag = proplists:get_value(name,
hd(proplists:get_value(tc_group_path, Config))),
Alg = Group,
- PA =
- case split(Alg) of
- [_] ->
- [Alg];
- [A1,A2] ->
- [{client2server,[A1]},
- {server2client,[A2]}]
- end,
- ct:log("Init tests for tag=~p alg=~p",[Tag,PA]),
- PrefAlgs = {preferred_algorithms,[{Tag,PA}]},
- start_std_daemon([PrefAlgs],
- [{pref_algs,PrefAlgs} | Config])
+ init_per_group(Tag, Alg, Config)
end.
+
+init_per_group(public_key=Tag, Alg, Config) ->
+ ct:log("Init tests for public_key ~p",[Alg]),
+ PrefAlgs = {preferred_algorithms,[{Tag,[Alg]}]},
+ %% Daemon started later in init_per_testcase
+ [{pref_algs,PrefAlgs},
+ {tag_alg,{Tag,Alg}}
+ | Config];
+
+init_per_group(Tag, Alg, Config) ->
+ PA =
+ case split(Alg) of
+ [_] ->
+ [Alg];
+ [A1,A2] ->
+ [{client2server,[A1]},
+ {server2client,[A2]}]
+ end,
+ ct:log("Init tests for tag=~p alg=~p",[Tag,PA]),
+ PrefAlgs = {preferred_algorithms,[{Tag,PA}]},
+ start_std_daemon([PrefAlgs],
+ [{pref_algs,PrefAlgs},
+ {tag_alg,{Tag,Alg}}
+ | Config]).
+
+
end_per_group(_Alg, Config) ->
case proplists:get_value(srvr_pid,Config) of
Pid when is_pid(Pid) ->
@@ -148,23 +163,49 @@ end_per_group(_Alg, Config) ->
-init_per_testcase(sshc_simple_exec_os_cmd, Config) ->
- start_pubkey_daemon([proplists:get_value(pref_algs,Config)], Config);
-init_per_testcase(_TC, Config) ->
- Config.
+init_per_testcase(TC, Config) ->
+ init_per_testcase(TC, proplists:get_value(tag_alg,Config), Config).
-end_per_testcase(sshc_simple_exec_os_cmd, Config) ->
- case proplists:get_value(srvr_pid,Config) of
- Pid when is_pid(Pid) ->
- ssh:stop_daemon(Pid),
- ct:log("stopped ~p",[proplists:get_value(srvr_addr,Config)]);
- _ ->
- ok
+init_per_testcase(_, {public_key,Alg}, Config) ->
+ Opts = pubkey_opts(Config),
+ case {ssh_file:user_key(Alg,Opts), ssh_file:host_key(Alg,Opts)} of
+ {{ok,_}, {ok,_}} ->
+ start_pubkey_daemon([proplists:get_value(pref_algs,Config)],
+ [{extra_daemon,true}|Config]);
+ {{ok,_}, _} ->
+ {skip, "No host key"};
+
+ {_, {ok,_}} ->
+ {skip, "No user key"};
+
+ _ ->
+ {skip, "Neither host nor user key"}
end;
-end_per_testcase(_TC, Config) ->
+
+init_per_testcase(sshc_simple_exec_os_cmd, _, Config) ->
+ start_pubkey_daemon([proplists:get_value(pref_algs,Config)],
+ [{extra_daemon,true}|Config]);
+
+init_per_testcase(_, _, Config) ->
Config.
+
+end_per_testcase(_TC, Config) ->
+ case proplists:get_value(extra_daemon, Config, false) of
+ true ->
+ case proplists:get_value(srvr_pid,Config) of
+ Pid when is_pid(Pid) ->
+ ssh:stop_daemon(Pid),
+ ct:log("stopped ~p",[proplists:get_value(srvr_addr,Config)]),
+ Config;
+ _ ->
+ Config
+ end;
+ _ ->
+ Config
+ end.
+
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -260,8 +301,9 @@ sshc_simple_exec_os_cmd(Config) ->
%%--------------------------------------------------------------------
%% Connect to the ssh server of the OS
-sshd_simple_exec(_Config) ->
+sshd_simple_exec(Config) ->
ConnectionRef = ssh_test_lib:connect(22, [{silently_accept_hosts, true},
+ proplists:get_value(pref_algs,Config),
{user_interaction, false}]),
{ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:exec(ConnectionRef, ChannelId0,
@@ -318,29 +360,32 @@ concat(A1, A2) -> list_to_atom(lists:concat([A1," + ",A2])).
split(Alg) -> ssh_test_lib:to_atoms(string:tokens(atom_to_list(Alg), " + ")).
specific_test_cases(Tag, Alg, SshcAlgos, SshdAlgos, TypeSSH) ->
- [simple_exec, simple_sftp] ++
- case supports(Tag, Alg, SshcAlgos) of
- true when TypeSSH == openSSH ->
- [sshc_simple_exec_os_cmd];
- _ ->
- []
- end ++
- case supports(Tag, Alg, SshdAlgos) of
- true ->
- [sshd_simple_exec];
- _ ->
- []
- end ++
- case {Tag,Alg} of
- {kex,_} when Alg == 'diffie-hellman-group-exchange-sha1' ;
- Alg == 'diffie-hellman-group-exchange-sha256' ->
- [simple_exec_groups,
- simple_exec_groups_no_match_too_large,
- simple_exec_groups_no_match_too_small
- ];
- _ ->
- []
- end.
+ case Tag of
+ public_key -> [];
+ _ -> [simple_exec, simple_sftp]
+ end
+ ++ case supports(Tag, Alg, SshcAlgos) of
+ true when TypeSSH == openSSH ->
+ [sshc_simple_exec_os_cmd];
+ _ ->
+ []
+ end ++
+ case supports(Tag, Alg, SshdAlgos) of
+ true ->
+ [sshd_simple_exec];
+ _ ->
+ []
+ end ++
+ case {Tag,Alg} of
+ {kex,_} when Alg == 'diffie-hellman-group-exchange-sha1' ;
+ Alg == 'diffie-hellman-group-exchange-sha256' ->
+ [simple_exec_groups,
+ simple_exec_groups_no_match_too_large,
+ simple_exec_groups_no_match_too_small
+ ];
+ _ ->
+ []
+ end.
supports(Tag, Alg, Algos) ->
lists:all(fun(A) ->
@@ -370,19 +415,30 @@ start_std_daemon(Opts, Config) ->
ct:log("started ~p:~p ~p",[Host,Port,Opts]),
[{srvr_pid,Pid},{srvr_addr,{Host,Port}} | Config].
+
start_pubkey_daemon(Opts0, Config) ->
- Opts = [{auth_methods,"publickey"}|Opts0],
- {Pid, Host, Port} = ssh_test_lib:std_daemon1(Config, Opts),
- ct:log("started pubkey_daemon ~p:~p ~p",[Host,Port,Opts]),
+ ct:log("starting pubkey_daemon",[]),
+ Opts = pubkey_opts(Config) ++ Opts0,
+ {Pid, Host, Port} = ssh_test_lib:daemon([{failfun, fun ssh_test_lib:failfun/2}
+ | Opts]),
+ ct:log("started ~p:~p ~p",[Host,Port,Opts]),
[{srvr_pid,Pid},{srvr_addr,{Host,Port}} | Config].
+pubkey_opts(Config) ->
+ SystemDir = filename:join(proplists:get_value(priv_dir,Config), "system"),
+ [{auth_methods,"publickey"},
+ {system_dir, SystemDir}].
+
+
setup_pubkey(Config) ->
DataDir = proplists:get_value(data_dir, Config),
UserDir = proplists:get_value(priv_dir, Config),
- ssh_test_lib:setup_dsa(DataDir, UserDir),
- ssh_test_lib:setup_rsa(DataDir, UserDir),
- ssh_test_lib:setup_ecdsa("256", DataDir, UserDir),
+ Keys =
+ [ssh_test_lib:setup_dsa(DataDir, UserDir),
+ ssh_test_lib:setup_rsa(DataDir, UserDir),
+ ssh_test_lib:setup_ecdsa("256", DataDir, UserDir)],
+ ssh_test_lib:write_auth_keys(Keys, UserDir), % 'authorized_keys' shall contain ALL pub keys
Config.
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index b80c3ed5e2..62e2a585e4 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -612,7 +612,7 @@ exec_key_differs(Config, UserPKAlgs) ->
{_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, SystemUserDir},
{preferred_algorithms,
- [{public_key,['ssh-rsa']}]}]),
+ [{public_key,['ssh-rsa'|UserPKAlgs]}]}]),
ct:sleep(500),
IO = ssh_test_lib:start_io_server(),
@@ -1173,13 +1173,10 @@ login_bad_pwd_no_retry3(Config) ->
login_bad_pwd_no_retry(Config, "password,publickey,keyboard-interactive").
login_bad_pwd_no_retry4(Config) ->
- login_bad_pwd_no_retry(Config, "password,other,keyboard-interactive").
+ login_bad_pwd_no_retry(Config, "password,keyboard-interactive").
login_bad_pwd_no_retry5(Config) ->
- login_bad_pwd_no_retry(Config, "password,other,keyboard-interactive,password,password").
-
-
-
+ login_bad_pwd_no_retry(Config, "password,keyboard-interactive,password,password").
login_bad_pwd_no_retry(Config, AuthMethods) ->
diff --git a/lib/ssh/test/ssh_property_test_SUITE.erl b/lib/ssh/test/ssh_property_test_SUITE.erl
index 9b2a84d8e4..5ea60d8a8f 100644
--- a/lib/ssh/test/ssh_property_test_SUITE.erl
+++ b/lib/ssh/test/ssh_property_test_SUITE.erl
@@ -55,6 +55,9 @@ groups() ->
init_per_suite(Config) ->
ct_property_test:init_per_suite(Config).
+end_per_suite(Config) ->
+ Config.
+
%%% One group in this suite happens to support only QuickCheck, so skip it
%%% if we run proper.
init_per_group(client_server, Config) ->
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index 5a6e0638a7..0385e30ad1 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -59,7 +59,8 @@ all() ->
{group,service_requests},
{group,authentication},
{group,packet_size_error},
- {group,field_size_error}
+ {group,field_size_error},
+ {group,ext_info}
].
groups() ->
@@ -90,7 +91,12 @@ groups() ->
bad_service_name_then_correct
]},
{authentication, [], [client_handles_keyboard_interactive_0_pwds
- ]}
+ ]},
+ {ext_info, [], [no_ext_info_s1,
+ no_ext_info_s2,
+ ext_info_s,
+ ext_info_c
+ ]}
].
@@ -644,7 +650,113 @@ client_info_line(_Config) ->
ok
end.
+%%%--------------------------------------------------------------------
+%%% The server does not send the extension because
+%%% the client does not tell the server to send it
+no_ext_info_s1(Config) ->
+ %% Start the dameon
+ Server = {Pid,_,_} = ssh_test_lib:daemon([{send_ext_info,true},
+ {system_dir, system_dir(Config)}]),
+ {ok,AfterKexState} = connect_and_kex([{server,Server}|Config]),
+ {ok,_} =
+ ssh_trpt_test_lib:exec(
+ [{send, #ssh_msg_service_request{name = "ssh-userauth"}},
+ {match, #ssh_msg_service_accept{name = "ssh-userauth"}, receive_msg}
+ ], AfterKexState),
+ ssh:stop_daemon(Pid).
+
+%%%--------------------------------------------------------------------
+%%% The server does not send the extension because
+%%% the server is not configured to send it
+no_ext_info_s2(Config) ->
+ %% Start the dameon
+ Server = {Pid,_,_} = ssh_test_lib:daemon([{send_ext_info,false},
+ {system_dir, system_dir(Config)}]),
+ {ok,AfterKexState} = connect_and_kex([{extra_options,[{recv_ext_info,true}]},
+ {server,Server}
+ | Config]),
+ {ok,_} =
+ ssh_trpt_test_lib:exec(
+ [{send, #ssh_msg_service_request{name = "ssh-userauth"}},
+ {match, #ssh_msg_service_accept{name = "ssh-userauth"}, receive_msg}
+ ], AfterKexState),
+ ssh:stop_daemon(Pid).
+
+%%%--------------------------------------------------------------------
+%%% The server sends the extension
+ext_info_s(Config) ->
+ %% Start the dameon
+ Server = {Pid,_,_} = ssh_test_lib:daemon([{send_ext_info,true},
+ {system_dir, system_dir(Config)}]),
+ {ok,AfterKexState} = connect_and_kex([{extra_options,[{recv_ext_info,true}]},
+ {server,Server}
+ | Config]),
+ {ok,_} =
+ ssh_trpt_test_lib:exec(
+ [{match, #ssh_msg_ext_info{_='_'}, receive_msg}
+ ],
+ AfterKexState),
+ ssh:stop_daemon(Pid).
+
+%%%--------------------------------------------------------------------
+%%% The client sends the extension
+ext_info_c(Config) ->
+ {User,_Pwd} = server_user_password(Config),
+
+ %% Create a listening socket as server socket:
+ {ok,InitialState} = ssh_trpt_test_lib:exec(listen),
+ HostPort = ssh_trpt_test_lib:server_host_port(InitialState),
+
+ Parent = self(),
+ %% Start a process handling one connection on the server side:
+ Pid =
+ spawn_link(
+ fun() ->
+ Result =
+ ssh_trpt_test_lib:exec(
+ [{set_options, [print_ops, print_messages]},
+ {accept, [{system_dir, system_dir(Config)},
+ {user_dir, user_dir(Config)},
+ {recv_ext_info, true}
+ ]},
+ receive_hello,
+ {send, hello},
+
+ {send, ssh_msg_kexinit},
+ {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+
+ {match, #ssh_msg_kexdh_init{_='_'}, receive_msg},
+ {send, ssh_msg_kexdh_reply},
+
+ {send, #ssh_msg_newkeys{}},
+ {match, #ssh_msg_newkeys{_='_'}, receive_msg},
+
+ {match, #ssh_msg_ext_info{_='_'}, receive_msg},
+
+ close_socket,
+ print_state
+ ],
+ InitialState),
+ Parent ! {result,self(),Result}
+ end),
+
+ %% connect to it with a regular Erlang SSH client
+ %% (expect error due to the close_socket in daemon):
+ {error,_} = std_connect(HostPort, Config,
+ [{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
+ {cipher,?DEFAULT_CIPHERS}
+ ]},
+ {tstflg, [{ext_info_client,true}]},
+ {send_ext_info, true}
+ ]
+ ),
+ %% Check that the daemon got expected result:
+ receive
+ {result, Pid, {ok,_}} -> ok;
+ {result, Pid, Error} -> ct:fail("Error: ~p",[Error])
+ end.
+
%%%================================================================
%%%==== Internal functions ========================================
%%%================================================================
@@ -751,10 +863,12 @@ connect_and_kex(Config, InitialState) ->
[{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
{cipher,?DEFAULT_CIPHERS}
]},
- {silently_accept_hosts, true},
+ {silently_accept_hosts, true},
{recv_ext_info, false},
{user_dir, user_dir(Config)},
- {user_interaction, false}]},
+ {user_interaction, false}
+ | proplists:get_value(extra_options,Config,[])
+ ]},
receive_hello,
{send, hello},
{send, ssh_msg_kexinit},
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 36ae2525da..7b273fecef 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -500,8 +500,12 @@ setup_ecdsa_auth_keys(_Size, Dir, UserDir) ->
setup_auth_keys(Keys, Dir) ->
AuthKeys = public_key:ssh_encode(Keys, auth_keys),
AuthKeysFile = filename:join(Dir, "authorized_keys"),
- file:write_file(AuthKeysFile, AuthKeys).
+ ok = file:write_file(AuthKeysFile, AuthKeys),
+ AuthKeys.
+write_auth_keys(Keys, Dir) ->
+ AuthKeysFile = filename:join(Dir, "authorized_keys"),
+ file:write_file(AuthKeysFile, Keys).
del_dirs(Dir) ->
case file:list_dir(Dir) of
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index a3d596a1c9..4d6aa93d4e 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -107,6 +107,9 @@ init_per_testcase(erlang_server_openssh_client_public_key_rsa, Config) ->
chk_key(sshc, 'ssh-rsa', ".ssh/id_rsa", Config);
init_per_testcase(erlang_client_openssh_server_publickey_dsa, Config) ->
chk_key(sshd, 'ssh-dss', ".ssh/id_dsa", Config);
+init_per_testcase(erlang_client_openssh_server_publickey_rsa, Config) ->
+ chk_key(sshd, 'ssh-rsa', ".ssh/id_rsa", Config);
+
init_per_testcase(erlang_server_openssh_client_renegotiate, Config) ->
case os:type() of
{unix,_} -> ssh:start(), Config;
@@ -322,65 +325,44 @@ erlang_client_openssh_server_setenv(Config) when is_list(Config) ->
%% setenv not meaningfull on erlang ssh daemon!
%%--------------------------------------------------------------------
-erlang_client_openssh_server_publickey_rsa() ->
- [{doc, "Validate using rsa publickey."}].
-erlang_client_openssh_server_publickey_rsa(Config) when is_list(Config) ->
- {ok,[[Home]]} = init:get_argument(home),
- KeyFile = filename:join(Home, ".ssh/id_rsa"),
- case file:read_file(KeyFile) of
- {ok, Pem} ->
- case public_key:pem_decode(Pem) of
- [{_,_, not_encrypted}] ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{pref_public_key_algs, ['ssh-rsa','ssh-dss']},
- {user_interaction, false},
- silently_accept_hosts]),
- {ok, Channel} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- ok = ssh_connection:close(ConnectionRef, Channel),
- ok = ssh:close(ConnectionRef);
- _ ->
- {skip, {error, "Has pass phrase can not be used by automated test case"}}
- end;
- _ ->
- {skip, "no ~/.ssh/id_rsa"}
- end.
-
+erlang_client_openssh_server_publickey_rsa(Config) ->
+ erlang_client_openssh_server_publickey_X(Config, 'ssh-rsa').
+
+erlang_client_openssh_server_publickey_dsa(Config) ->
+ erlang_client_openssh_server_publickey_X(Config, 'ssh-dss').
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_publickey_dsa() ->
- [{doc, "Validate using dsa publickey."}].
-erlang_client_openssh_server_publickey_dsa(Config) when is_list(Config) ->
+
+erlang_client_openssh_server_publickey_X(Config, Alg) ->
ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{pref_public_key_algs, ['ssh-dss','ssh-rsa']},
- {user_interaction, false},
- silently_accept_hosts]),
+ ssh_test_lib:connect(?SSH_DEFAULT_PORT,
+ [{pref_public_key_algs, [Alg]},
+ {user_interaction, false},
+ {auth_methods, "publickey"},
+ silently_accept_hosts]),
{ok, Channel} =
- ssh_connection:session_channel(ConnectionRef, infinity),
+ ssh_connection:session_channel(ConnectionRef, infinity),
ok = ssh_connection:close(ConnectionRef, Channel),
ok = ssh:close(ConnectionRef).
%%--------------------------------------------------------------------
erlang_server_openssh_client_public_key_dsa() ->
- [{timetrap, {seconds,(?TIMEOUT div 1000)+10}},
- {doc, "Validate using dsa publickey."}].
+ [{timetrap, {seconds,(?TIMEOUT div 1000)+10}}].
erlang_server_openssh_client_public_key_dsa(Config) when is_list(Config) ->
- erlang_server_openssh_client_public_key_X(Config, ssh_dsa).
+ erlang_server_openssh_client_public_key_X(Config, 'ssh-dss').
-erlang_server_openssh_client_public_key_rsa() ->
- [{timetrap, {seconds,(?TIMEOUT div 1000)+10}},
- {doc, "Validate using rsa publickey."}].
+erlang_server_openssh_client_public_key_rsa() ->
+ [{timetrap, {seconds,(?TIMEOUT div 1000)+10}}].
erlang_server_openssh_client_public_key_rsa(Config) when is_list(Config) ->
- erlang_server_openssh_client_public_key_X(Config, ssh_rsa).
+ erlang_server_openssh_client_public_key_X(Config, 'ssh-rsa').
-erlang_server_openssh_client_public_key_X(Config, _PubKeyAlg) ->
+erlang_server_openssh_client_public_key_X(Config, Alg) ->
SystemDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
KnownHosts = filename:join(PrivDir, "known_hosts"),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {preferred_algorithms,[{public_key, [Alg]}]},
+ {auth_methods, "publickey"},
{failfun, fun ssh_test_lib:failfun/2}]),
ct:sleep(500),
@@ -401,7 +383,7 @@ erlang_server_openssh_client_renegotiate(Config) ->
KnownHosts = filename:join(PrivDir, "known_hosts"),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
- {failfun, fun ssh_test_lib:failfun/2}]),
+ {failfun, fun ssh_test_lib:failfun/2}]),
ct:sleep(500),
RenegLimitK = 3,
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 9937373e6e..f078b87bce 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -53,7 +53,7 @@
%% Data handling
-export([encode_data/3, passive_receive/2, next_record_if_active/1, handle_common_event/4,
- send/3, socket/5]).
+ send/3, socket/5, setopts/3, getopts/3]).
%% gen_statem state functions
-export([init/3, error/3, downgrade/3, %% Initiation and take down states
@@ -203,6 +203,11 @@ select_sni_extension(_) ->
socket(Pid, Transport, Socket, Connection, _) ->
dtls_socket:socket(Pid, Transport, Socket, Connection).
+setopts(Transport, Socket, Other) ->
+ dtls_socket:setopts(Transport, Socket, Other).
+getopts(Transport, Socket, Tag) ->
+ dtls_socket:getopts(Transport, Socket, Tag).
+
%%====================================================================
%% tls_connection_sup API
%%====================================================================
@@ -688,16 +693,18 @@ next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
{no_record, State#state{unprocessed_handshake_events = N-1}};
next_record(#state{protocol_buffers =
- #protocol_buffers{dtls_cipher_texts = [CT | Rest]}
+ #protocol_buffers{dtls_cipher_texts = [#ssl_tls{epoch = Epoch} = CT | Rest]}
= Buffers,
- connection_states = ConnStates0} = State) ->
- case dtls_record:decode_cipher_text(CT, ConnStates0) of
- {Plain, ConnStates} ->
- {Plain, State#state{protocol_buffers =
- Buffers#protocol_buffers{dtls_cipher_texts = Rest},
- connection_states = ConnStates}};
- #alert{} = Alert ->
- {Alert, State}
+ connection_states = ConnectionStates} = State) ->
+ CurrentRead = dtls_record:get_connection_state_by_epoch(Epoch, ConnectionStates, read),
+ case dtls_record:replay_detect(CT, CurrentRead) of
+ false ->
+ decode_cipher_text(State#state{connection_states = ConnectionStates}) ;
+ true ->
+ %% Ignore replayed record
+ next_record(State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_cipher_texts = Rest},
+ connection_states = ConnectionStates})
end;
next_record(#state{role = server,
socket = {Listener, {Client, _}},
@@ -770,6 +777,17 @@ next_event(StateName, Record,
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end.
+decode_cipher_text(#state{protocol_buffers = #protocol_buffers{dtls_cipher_texts = [ CT | Rest]} = Buffers,
+ connection_states = ConnStates0} = State) ->
+ case dtls_record:decode_cipher_text(CT, ConnStates0) of
+ {Plain, ConnStates} ->
+ {Plain, State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_cipher_texts = Rest},
+ connection_states = ConnStates}};
+ #alert{} = Alert ->
+ {Alert, State}
+ end.
+
dtls_version(hello, Version, #state{role = server} = State) ->
State#state{negotiated_version = Version}; %%Inital version
dtls_version(_,_, State) ->
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index 6a418c6fb1..8a7f8c1d0a 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -46,7 +46,7 @@
is_higher/2, supported_protocol_versions/0,
is_acceptable_version/2, hello_version/2]).
--export([save_current_connection_state/2, next_epoch/2]).
+-export([save_current_connection_state/2, next_epoch/2, get_connection_state_by_epoch/3, replay_detect/2]).
-export([init_connection_state_seq/2, current_connection_state_epoch/2]).
@@ -55,6 +55,8 @@
-type dtls_version() :: ssl_record:ssl_version().
-type dtls_atom_version() :: dtlsv1 | 'dtlsv1.2'.
+-define(REPLAY_WINDOW_SIZE, 64).
+
-compile(inline).
%%====================================================================
@@ -73,7 +75,7 @@ init_connection_states(Role, BeastMitigation) ->
Initial = initial_connection_state(ConnectionEnd, BeastMitigation),
Current = Initial#{epoch := 0},
InitialPending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation),
- Pending = InitialPending#{epoch => undefined},
+ Pending = InitialPending#{epoch => undefined, replay_window => init_replay_window(?REPLAY_WINDOW_SIZE)},
#{saved_read => Current,
current_read => Current,
pending_read => Pending,
@@ -96,11 +98,13 @@ save_current_connection_state(#{current_write := Current} = States, write) ->
next_epoch(#{pending_read := Pending,
current_read := #{epoch := Epoch}} = States, read) ->
- States#{pending_read := Pending#{epoch := Epoch + 1}};
+ States#{pending_read := Pending#{epoch := Epoch + 1,
+ replay_window := init_replay_window(?REPLAY_WINDOW_SIZE)}};
next_epoch(#{pending_write := Pending,
current_write := #{epoch := Epoch}} = States, write) ->
- States#{pending_write := Pending#{epoch := Epoch + 1}}.
+ States#{pending_write := Pending#{epoch := Epoch + 1,
+ replay_window := init_replay_window(?REPLAY_WINDOW_SIZE)}}.
get_connection_state_by_epoch(Epoch, #{current_write := #{epoch := Epoch} = Current},
write) ->
@@ -411,6 +415,7 @@ hello_version(Version, Versions) ->
lowest_protocol_version(Versions)
end.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -419,6 +424,7 @@ initial_connection_state(ConnectionEnd, BeastMitigation) ->
ssl_record:initial_security_params(ConnectionEnd),
epoch => undefined,
sequence_number => 0,
+ replay_window => init_replay_window(?REPLAY_WINDOW_SIZE),
beast_mitigation => BeastMitigation,
compression_state => undefined,
cipher_state => undefined,
@@ -499,8 +505,9 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
{PlainFragment, CipherState} ->
{Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
- ReadState = ReadState0#{compression_state => CompressionS1,
+ ReadState0 = ReadState0#{compression_state => CompressionS1,
cipher_state => CipherState},
+ ReadState = update_replay_window(Seq, ReadState0),
ConnnectionStates = set_connection_state_by_epoch(ReadState, Epoch, ConnnectionStates0, read),
{CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
#alert{} = Alert ->
@@ -523,7 +530,8 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
{Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
- ReadState = ReadState1#{compression_state => CompressionS1},
+ ReadState2 = ReadState1#{compression_state => CompressionS1},
+ ReadState = update_replay_window(Seq, ReadState2),
ConnnectionStates = set_connection_state_by_epoch(ReadState, Epoch, ConnnectionStates0, read),
{CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
false ->
@@ -555,3 +563,38 @@ mac_hash({Major, Minor}, MacAlg, MacSecret, Epoch, SeqNo, Type, Length, Fragment
calc_aad(Type, {MajVer, MinVer}, Epoch, SeqNo) ->
<<?UINT16(Epoch), ?UINT48(SeqNo), ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
+
+init_replay_window(Size) ->
+ #{size => Size,
+ top => Size,
+ bottom => 0,
+ mask => 0 bsl 64
+ }.
+
+replay_detect(#ssl_tls{sequence_number = SequenceNumber}, #{replay_window := Window}) ->
+ is_replay(SequenceNumber, Window).
+
+
+is_replay(SequenceNumber, #{bottom := Bottom}) when SequenceNumber < Bottom ->
+ true;
+is_replay(SequenceNumber, #{size := Size,
+ top := Top,
+ bottom := Bottom,
+ mask := Mask}) when (SequenceNumber >= Bottom) andalso (SequenceNumber =< Top) ->
+ Index = (SequenceNumber rem Size),
+ (Index band Mask) == 1;
+
+is_replay(_, _) ->
+ false.
+
+update_replay_window(SequenceNumber, #{replay_window := #{size := Size,
+ top := Top,
+ bottom := Bottom,
+ mask := Mask0} = Window0} = ConnectionStates) ->
+ NoNewBits = SequenceNumber - Top,
+ Index = SequenceNumber rem Size,
+ Mask = (Mask0 bsl NoNewBits) bor Index,
+ Window = Window0#{top => SequenceNumber,
+ bottom => Bottom + NoNewBits,
+ mask => Mask},
+ ConnectionStates#{replay_window := Window}.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index d9e47c43ad..5421bdef99 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -455,7 +455,16 @@ setopts(#sslsocket{pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) ->
_:_ ->
{error, {options, {not_a_proplist, Options0}}}
end;
-
+setopts(#sslsocket{pid = {{udp, _}, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, Options) when is_list(Options) ->
+ try dtls_socket:setopts(Transport, ListenSocket, Options) of
+ ok ->
+ ok;
+ {error, InetError} ->
+ {error, {options, {socket_options, Options, InetError}}}
+ catch
+ _:Error ->
+ {error, {options, {socket_options, Options, Error}}}
+ end;
setopts(#sslsocket{pid = {_, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, Options) when is_list(Options) ->
try tls_socket:setopts(Transport, ListenSocket, Options) of
ok ->
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index e935e07b65..fb87662c7b 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -905,14 +905,14 @@ handle_call({new_user, User}, From, StateName,
handle_call({get_opts, OptTags}, From, _,
#state{socket = Socket,
transport_cb = Transport,
- socket_options = SockOpts}, _) ->
- OptsReply = get_socket_opts(Transport, Socket, OptTags, SockOpts, []),
+ socket_options = SockOpts}, Connection) ->
+ OptsReply = get_socket_opts(Connection, Transport, Socket, OptTags, SockOpts, []),
{keep_state_and_data, [{reply, From, OptsReply}]};
handle_call({set_opts, Opts0}, From, StateName,
#state{socket_options = Opts1,
socket = Socket,
- transport_cb = Transport} = State0, _) ->
- {Reply, Opts} = set_socket_opts(Transport, Socket, Opts0, Opts1, []),
+ transport_cb = Transport} = State0, Connection) ->
+ {Reply, Opts} = set_socket_opts(Connection, Transport, Socket, Opts0, Opts1, []),
State = State0#state{socket_options = Opts},
handle_active_option(Opts#socket_options.active, StateName, From, Reply, State);
@@ -1910,42 +1910,39 @@ call(FsmPid, Event) ->
{error, closed}
end.
-get_socket_opts(_,_,[], _, Acc) ->
+get_socket_opts(_, _,_,[], _, Acc) ->
{ok, Acc};
-get_socket_opts(Transport, Socket, [mode | Tags], SockOpts, Acc) ->
- get_socket_opts(Transport, Socket, Tags, SockOpts,
+get_socket_opts(Connection, Transport, Socket, [mode | Tags], SockOpts, Acc) ->
+ get_socket_opts(Connection, Transport, Socket, Tags, SockOpts,
[{mode, SockOpts#socket_options.mode} | Acc]);
-get_socket_opts(Transport, Socket, [packet | Tags], SockOpts, Acc) ->
+get_socket_opts(Connection, Transport, Socket, [packet | Tags], SockOpts, Acc) ->
case SockOpts#socket_options.packet of
{Type, headers} ->
- get_socket_opts(Transport, Socket, Tags, SockOpts, [{packet, Type} | Acc]);
+ get_socket_opts(Connection, Transport, Socket, Tags, SockOpts, [{packet, Type} | Acc]);
Type ->
- get_socket_opts(Transport, Socket, Tags, SockOpts, [{packet, Type} | Acc])
+ get_socket_opts(Connection, Transport, Socket, Tags, SockOpts, [{packet, Type} | Acc])
end;
-get_socket_opts(Transport, Socket, [header | Tags], SockOpts, Acc) ->
- get_socket_opts(Transport, Socket, Tags, SockOpts,
+get_socket_opts(Connection, Transport, Socket, [header | Tags], SockOpts, Acc) ->
+ get_socket_opts(Connection, Transport, Socket, Tags, SockOpts,
[{header, SockOpts#socket_options.header} | Acc]);
-get_socket_opts(Transport, Socket, [active | Tags], SockOpts, Acc) ->
- get_socket_opts(Transport, Socket, Tags, SockOpts,
+get_socket_opts(Connection, Transport, Socket, [active | Tags], SockOpts, Acc) ->
+ get_socket_opts(Connection, Transport, Socket, Tags, SockOpts,
[{active, SockOpts#socket_options.active} | Acc]);
-get_socket_opts(Transport, Socket, [Tag | Tags], SockOpts, Acc) ->
- try tls_socket:getopts(Transport, Socket, [Tag]) of
- {ok, [Opt]} ->
- get_socket_opts(Transport, Socket, Tags, SockOpts, [Opt | Acc]);
- {error, Error} ->
- {error, {options, {socket_options, Tag, Error}}}
- catch
- %% So that inet behavior does not crash our process
- _:Error -> {error, {options, {socket_options, Tag, Error}}}
+get_socket_opts(Connection, Transport, Socket, [Tag | Tags], SockOpts, Acc) ->
+ case Connection:getopts(Transport, Socket, [Tag]) of
+ {ok, [Opt]} ->
+ get_socket_opts(Connection, Transport, Socket, Tags, SockOpts, [Opt | Acc]);
+ {error, Reason} ->
+ {error, {options, {socket_options, Tag, Reason}}}
end;
-get_socket_opts(_, _,Opts, _,_) ->
+get_socket_opts(_,_, _,Opts, _,_) ->
{error, {options, {socket_options, Opts, function_clause}}}.
-set_socket_opts(_,_, [], SockOpts, []) ->
+set_socket_opts(_,_,_, [], SockOpts, []) ->
{ok, SockOpts};
-set_socket_opts(Transport, Socket, [], SockOpts, Other) ->
+set_socket_opts(ConnectionCb, Transport, Socket, [], SockOpts, Other) ->
%% Set non emulated options
- try tls_socket:setopts(Transport, Socket, Other) of
+ try ConnectionCb:setopts(Transport, Socket, Other) of
ok ->
{ok, SockOpts};
{error, InetError} ->
@@ -1956,13 +1953,13 @@ set_socket_opts(Transport, Socket, [], SockOpts, Other) ->
{{error, {options, {socket_options, Other, Error}}}, SockOpts}
end;
-set_socket_opts(Transport,Socket, [{mode, Mode}| Opts], SockOpts, Other)
+set_socket_opts(ConnectionCb, Transport,Socket, [{mode, Mode}| Opts], SockOpts, Other)
when Mode == list; Mode == binary ->
- set_socket_opts(Transport, Socket, Opts,
+ set_socket_opts(ConnectionCb, Transport, Socket, Opts,
SockOpts#socket_options{mode = Mode}, Other);
-set_socket_opts(_, _, [{mode, _} = Opt| _], SockOpts, _) ->
+set_socket_opts(_, _, _, [{mode, _} = Opt| _], SockOpts, _) ->
{{error, {options, {socket_options, Opt}}}, SockOpts};
-set_socket_opts(Transport,Socket, [{packet, Packet}| Opts], SockOpts, Other)
+set_socket_opts(ConnectionCb, Transport,Socket, [{packet, Packet}| Opts], SockOpts, Other)
when Packet == raw;
Packet == 0;
Packet == 1;
@@ -1978,26 +1975,26 @@ set_socket_opts(Transport,Socket, [{packet, Packet}| Opts], SockOpts, Other)
Packet == httph;
Packet == http_bin;
Packet == httph_bin ->
- set_socket_opts(Transport, Socket, Opts,
+ set_socket_opts(ConnectionCb, Transport, Socket, Opts,
SockOpts#socket_options{packet = Packet}, Other);
-set_socket_opts(_, _, [{packet, _} = Opt| _], SockOpts, _) ->
+set_socket_opts(_, _, _, [{packet, _} = Opt| _], SockOpts, _) ->
{{error, {options, {socket_options, Opt}}}, SockOpts};
-set_socket_opts(Transport, Socket, [{header, Header}| Opts], SockOpts, Other)
+set_socket_opts(ConnectionCb, Transport, Socket, [{header, Header}| Opts], SockOpts, Other)
when is_integer(Header) ->
- set_socket_opts(Transport, Socket, Opts,
+ set_socket_opts(ConnectionCb, Transport, Socket, Opts,
SockOpts#socket_options{header = Header}, Other);
-set_socket_opts(_, _, [{header, _} = Opt| _], SockOpts, _) ->
+set_socket_opts(_, _, _, [{header, _} = Opt| _], SockOpts, _) ->
{{error,{options, {socket_options, Opt}}}, SockOpts};
-set_socket_opts(Transport, Socket, [{active, Active}| Opts], SockOpts, Other)
+set_socket_opts(ConnectionCb, Transport, Socket, [{active, Active}| Opts], SockOpts, Other)
when Active == once;
Active == true;
Active == false ->
- set_socket_opts(Transport, Socket, Opts,
+ set_socket_opts(ConnectionCb, Transport, Socket, Opts,
SockOpts#socket_options{active = Active}, Other);
-set_socket_opts(_, _, [{active, _} = Opt| _], SockOpts, _) ->
+set_socket_opts(_,_, _, [{active, _} = Opt| _], SockOpts, _) ->
{{error, {options, {socket_options, Opt}} }, SockOpts};
-set_socket_opts(Transport, Socket, [Opt | Opts], SockOpts, Other) ->
- set_socket_opts(Transport, Socket, Opts, SockOpts, [Opt | Other]).
+set_socket_opts(ConnectionCb, Transport, Socket, [Opt | Opts], SockOpts, Other) ->
+ set_socket_opts(ConnectionCb, Transport, Socket, Opts, SockOpts, [Opt | Other]).
start_or_recv_cancel_timer(infinity, _RecvFrom) ->
undefined;
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index a289ee0a65..96c3ab86e9 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -60,7 +60,7 @@
%% Data handling
-export([passive_receive/2, next_record_if_active/1, handle_common_event/4, send/3,
- socket/5]).
+ socket/5, setopts/3, getopts/3]).
%% gen_statem state functions
-export([init/3, error/3, downgrade/3, %% Initiation and take down states
@@ -195,6 +195,10 @@ callback_mode() ->
socket(Pid, Transport, Socket, Connection, Tracker) ->
tls_socket:socket(Pid, Transport, Socket, Connection, Tracker).
+setopts(Transport, Socket, Other) ->
+ tls_socket:setopts(Transport, Socket, Other).
+getopts(Transport, Socket, Tag) ->
+ tls_socket:getopts(Transport, Socket, Tag).
%%--------------------------------------------------------------------
%% State functions
diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl
index 960ddf7808..ae2928b1c3 100644
--- a/lib/ssl/test/ssl_bench_SUITE.erl
+++ b/lib/ssl/test/ssl_bench_SUITE.erl
@@ -410,13 +410,19 @@ ssl_opts(connect_der) ->
[{verify, verify_peer} | ssl_opts("client_der")];
ssl_opts(Role) ->
CertData = cert_data(Role),
- [{active, false},
- {depth, 2},
- {reuseaddr, true},
- {mode,binary},
- {nodelay, true},
- {ciphers, [{dhe_rsa,aes_256_cbc,sha}]}
- |CertData].
+ Opts = [{active, false},
+ {depth, 2},
+ {reuseaddr, true},
+ {mode,binary},
+ {nodelay, true},
+ {ciphers, [{dhe_rsa,aes_256_cbc,sha}]}
+ |CertData],
+ case Role of
+ "client" ++ _ ->
+ [{server_name_indication, disable} | Opts];
+ "server" ++ _ ->
+ Opts
+ end.
cert_data(Der) when Der =:= "server_der"; Der =:= "client_der" ->
[Role,_] = string:tokens(Der, "_"),
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index 09bde3e397..012737c390 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -358,7 +358,7 @@ gen_event:stop -----> Module:terminate/2
<v>&nbsp;Name = atom()</v>
<v>&nbsp;GlobalName = ViaName = term()</v>
<v>Options = [Option]</v>
- <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v>
<v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
<v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v>
<v>&nbsp;&nbsp;SOpts = [term()]</v>
@@ -385,7 +385,7 @@ gen_event:stop -----> Module:terminate/2
<v>&nbsp;Name = atom()</v>
<v>&nbsp;GlobalName = ViaName = term()</v>
<v>Options = [Option]</v>
- <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v>
<v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
<v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v>
<v>&nbsp;&nbsp;SOpts = [term()]</v>
@@ -419,6 +419,12 @@ gen_event:stop -----> Module:terminate/2
<seealso marker="kernel:global"><c>global</c></seealso>.
Thus, <c>{via,global,GlobalName}</c> is a valid reference.</p>
</item>
+ <item>
+ <p>If option <c>{hibernate_after,HibernateAfterTimeout}</c> is present, the <c>gen_event</c>
+ process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and
+ if no message is received, the process goes into hibernation automatically
+ (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>).</p>
+ </item>
</list>
<p>If the event manager is successfully created, the function
returns <c>{ok,Pid}</c>, where <c>Pid</c> is the pid of
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index a8006bb870..7d137fc772 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -199,7 +199,7 @@ gen_server:abcast -----> Module:handle_cast/2
<type>
<v>Module = atom()</v>
<v>Options = [Option]</v>
- <v>&nbsp;Option = {debug,Dbgs}</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {hibernate_after,HibernateAfterTimeout}</v>
<v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
<v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics</v>
<v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
@@ -334,7 +334,7 @@ gen_server:abcast -----> Module:handle_cast/2
<v>Module = atom()</v>
<v>Args = term()</v>
<v>Options = [Option]</v>
- <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v>
<v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
<v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v>
<v>&nbsp;&nbsp;SOpts = [term()]</v>
@@ -364,7 +364,7 @@ gen_server:abcast -----> Module:handle_cast/2
<v>Module = atom()</v>
<v>Args = term()</v>
<v>Options = [Option]</v>
- <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v>
<v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
<v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v>
<v>&nbsp;&nbsp;SOpts = [term()]</v>
@@ -417,6 +417,12 @@ gen_server:abcast -----> Module:handle_cast/2
returns <c>{error,timeout}</c>.</p>
</item>
<item>
+ <p>If option <c>{hibernate_after,HibernateAfterTimeout}</c> is present, the <c>gen_server</c>
+ process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and
+ if no message is received, the process goes into hibernation automatically
+ (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>).</p>
+ </item>
+ <item>
<p>If option <c>{debug,Dbgs}</c> is present,
the corresponding <c>sys</c> function is called for each
item in <c>Dbgs</c>; see
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index 17a3a3c83c..1aac88c308 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -453,6 +453,21 @@ handle_event(_, _, State, Data) ->
</desc>
</datatype>
<datatype>
+ <name name="hibernate_after_opt"/>
+ <desc>
+ <p>
+ hibernate_after option that can be used when starting
+ a <c>gen_statem</c> server through,
+ <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>.
+ </p>
+ <p>If option<seealso marker="#type-hibernate_after_opt"><c>{hibernate_after,HibernateAfterTimeout}</c></seealso> is present, the <c>gen_statem</c>
+ process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and
+ if no message is received, the process goes into hibernation automatically
+ (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>).
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="start_opt"/>
<desc>
<p>
@@ -1551,6 +1566,13 @@ handle_event(_, _, State, Data) ->
</p>
</item>
<item>
+ <p>If option<seealso marker="#type-hibernate_after_opt"><c>{hibernate_after,HibernateAfterTimeout}</c></seealso> is present, the <c>gen_statem</c>
+ process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and
+ if no message is received, the process goes into hibernation automatically
+ (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>).
+ </p>
+ </item>
+ <item>
<p>
If option
<seealso marker="#type-debug_opt"><c>{debug,Dbgs}</c></seealso>
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 7c40058dd8..d53a31db0d 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -92,6 +92,14 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
:: dict:dict(ta(), line())
}).
+
+%% Are we outside or inside a catch or try/catch?
+-type catch_scope() :: 'none'
+ | 'after_old_catch'
+ | 'after_try'
+ | 'wrong_part_of_try'
+ | 'try_catch'.
+
%% Define the lint state record.
%% 'called' and 'exports' contain {Line, {Function, Arity}},
%% the other function collections contain {Function, Arity}.
@@ -135,7 +143,9 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
types = dict:new() %Type definitions
:: dict:dict(ta(), #typeinfo{}),
exp_types=gb_sets:empty() %Exported types
- :: gb_sets:set(ta())
+ :: gb_sets:set(ta()),
+ catch_scope = none %Inside/outside try or catch
+ :: catch_scope()
}).
-type lint_state() :: #lint{}.
@@ -223,7 +233,15 @@ format_error({redefine_old_bif_import,{F,A}}) ->
format_error({redefine_bif_import,{F,A}}) ->
io_lib:format("import directive overrides auto-imported BIF ~w/~w~n"
" - use \"-compile({no_auto_import,[~w/~w]}).\" to resolve name clash", [F,A,F,A]);
-
+format_error({get_stacktrace,wrong_part_of_try}) ->
+ "erlang:get_stacktrace/0 used in the wrong part of 'try' expression. "
+ "(Use it in the block between 'catch' and 'end'.)";
+format_error({get_stacktrace,after_old_catch}) ->
+ "erlang:get_stacktrace/0 used following an old-style 'catch' "
+ "may stop working in a future release. (Use it inside 'try'.)";
+format_error({get_stacktrace,after_try}) ->
+ "erlang:get_stacktrace/0 used following a 'try' expression "
+ "may stop working in a future release. (Use it inside 'try'.)";
format_error({deprecated, MFA, ReplacementMFA, Rel}) ->
io_lib:format("~s is deprecated and will be removed in ~s; use ~s",
[format_mfa(MFA), Rel, format_mfa(ReplacementMFA)]);
@@ -568,7 +586,10 @@ start(File, Opts) ->
false, Opts)},
{missing_spec_all,
bool_option(warn_missing_spec_all, nowarn_missing_spec_all,
- false, Opts)}
+ false, Opts)},
+ {get_stacktrace,
+ bool_option(warn_get_stacktrace, nowarn_get_stacktrace,
+ true, Opts)}
],
Enabled1 = [Category || {Category,true} <- Enabled0],
Enabled = ordsets:from_list(Enabled1),
@@ -1405,8 +1426,9 @@ call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func,file=File}=St)
%% function(Line, Name, Arity, Clauses, State) -> State.
function(Line, Name, Arity, Cs, St0) ->
- St1 = define_function(Line, Name, Arity, St0#lint{func={Name,Arity}}),
- clauses(Cs, St1).
+ St1 = St0#lint{func={Name,Arity},catch_scope=none},
+ St2 = define_function(Line, Name, Arity, St1),
+ clauses(Cs, St2).
-spec define_function(line(), atom(), arity(), lint_state()) -> lint_state().
@@ -2338,22 +2360,24 @@ expr({call,Line,F,As}, Vt, St0) ->
expr({'try',Line,Es,Scs,Ccs,As}, Vt, St0) ->
%% Currently, we don't allow any exports because later
%% passes cannot handle exports in combination with 'after'.
- {Evt0,St1} = exprs(Es, Vt, St0),
+ {Evt0,St1} = exprs(Es, Vt, St0#lint{catch_scope=wrong_part_of_try}),
TryLine = {'try',Line},
Uvt = vtunsafe(TryLine, Evt0, Vt),
Evt1 = vtupdate(Uvt, Evt0),
- {Sccs,St2} = icrt_clauses(Scs++Ccs, TryLine, vtupdate(Evt1, Vt), St1),
+ {Sccs,St2} = try_clauses(Scs, Ccs, TryLine,
+ vtupdate(Evt1, Vt), St1),
Rvt0 = Sccs,
Rvt1 = vtupdate(vtunsafe(TryLine, Rvt0, Vt), Rvt0),
Evt2 = vtmerge(Evt1, Rvt1),
{Avt0,St} = exprs(As, vtupdate(Evt2, Vt), St2),
Avt1 = vtupdate(vtunsafe(TryLine, Avt0, Vt), Avt0),
Avt = vtmerge(Evt2, Avt1),
- {Avt,St};
+ {Avt,St#lint{catch_scope=after_try}};
expr({'catch',Line,E}, Vt, St0) ->
%% No new variables added, flag new variables as unsafe.
{Evt,St} = expr(E, Vt, St0),
- {vtupdate(vtunsafe({'catch',Line}, Evt, Vt), Evt),St};
+ {vtupdate(vtunsafe({'catch',Line}, Evt, Vt), Evt),
+ St#lint{catch_scope=after_old_catch}};
expr({match,_Line,P,E}, Vt, St0) ->
{Evt,St1} = expr(E, Vt, St0),
{Pvt,Bvt,St2} = pattern(P, vtupdate(Evt, Vt), St1),
@@ -3173,6 +3197,17 @@ is_module_dialyzer_option(Option) ->
error_handling,race_conditions,no_missing_calls,
specdiffs,overspecs,underspecs,unknown]).
+%% try_catch_clauses(Scs, Ccs, In, ImportVarTable, State) ->
+%% {UpdVt,State}.
+
+try_clauses(Scs, Ccs, In, Vt, St0) ->
+ {Csvt0,St1} = icrt_clauses(Scs, Vt, St0),
+ St2 = St1#lint{catch_scope=try_catch},
+ {Csvt1,St3} = icrt_clauses(Ccs, Vt, St2),
+ Csvt = Csvt0 ++ Csvt1,
+ UpdVt = icrt_export(Csvt, Vt, In, St3),
+ {UpdVt,St3}.
+
%% icrt_clauses(Clauses, In, ImportVarTable, State) ->
%% {UpdVt,State}.
@@ -3657,7 +3692,8 @@ has_wildcard_field([]) -> false.
check_remote_function(Line, M, F, As, St0) ->
St1 = deprecated_function(Line, M, F, As, St0),
St2 = check_qlc_hrl(Line, M, F, As, St1),
- format_function(Line, M, F, As, St2).
+ St3 = check_get_stacktrace(Line, M, F, As, St2),
+ format_function(Line, M, F, As, St3).
%% check_qlc_hrl(Line, ModName, FuncName, [Arg], State) -> State
%% Add warning if qlc:q/1,2 has been called but qlc.hrl has not
@@ -3706,6 +3742,23 @@ deprecated_function(Line, M, F, As, St) ->
St
end.
+check_get_stacktrace(Line, erlang, get_stacktrace, [], St) ->
+ case St of
+ #lint{catch_scope=none} ->
+ St;
+ #lint{catch_scope=try_catch} ->
+ St;
+ #lint{catch_scope=Scope} ->
+ case is_warn_enabled(get_stacktrace, St) of
+ false ->
+ St;
+ true ->
+ add_warning(Line, {get_stacktrace,Scope}, St)
+ end
+ end;
+check_get_stacktrace(_, _, _, _, St) ->
+ St.
+
-dialyzer({no_match, deprecated_type/5}).
deprecated_type(L, M, N, As, St) ->
diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl
index 0b262de3ab..76f89841b9 100644
--- a/lib/stdlib/src/error_logger_file_h.erl
+++ b/lib/stdlib/src/error_logger_file_h.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -57,7 +57,7 @@ init(File, PrevHandler) ->
process_flag(trap_exit, true),
case file:open(File, [write]) of
{ok,Fd} ->
- Depth = get_depth(),
+ Depth = error_logger:get_format_depth(),
State = #st{fd=Fd,filename=File,prev_handler=PrevHandler,
depth=Depth},
{ok, State};
@@ -65,14 +65,6 @@ init(File, PrevHandler) ->
Error
end.
-get_depth() ->
- case application:get_env(kernel, error_logger_format_depth) of
- {ok, Depth} when is_integer(Depth) ->
- max(10, Depth);
- undefined ->
- unlimited
- end.
-
handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
{ok, State};
handle_event(Event, State) ->
diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl
index 2f2fd65252..8f0d7b0362 100644
--- a/lib/stdlib/src/error_logger_tty_h.erl
+++ b/lib/stdlib/src/error_logger_tty_h.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -44,7 +44,7 @@
%% This one is used when we takeover from the simple error_logger.
init({[], {error_logger, Buf}}) ->
User = set_group_leader(),
- Depth = get_depth(),
+ Depth = error_logger:get_format_depth(),
State = #st{user=User,prev_handler=error_logger,depth=Depth},
write_events(State, Buf),
{ok, State};
@@ -56,17 +56,9 @@ init({[], {error_logger_tty_h, PrevHandler}}) ->
%% This one is used when we are started directly.
init([]) ->
User = set_group_leader(),
- Depth = get_depth(),
+ Depth = error_logger:get_format_depth(),
{ok, #st{user=User,prev_handler=[],depth=Depth}}.
-get_depth() ->
- case application:get_env(kernel, error_logger_format_depth) of
- {ok, Depth} when is_integer(Depth) ->
- max(10, Depth);
- undefined ->
- unlimited
- end.
-
handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
{ok, State};
handle_event(Event, State) ->
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 6e8f780f7c..f2629a47c2 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -284,8 +284,9 @@ start(EscriptOptions) ->
io:format("escript: ~s\n", [Str]),
my_halt(127);
_:Reason ->
+ Stk = erlang:get_stacktrace(),
io:format("escript: Internal error: ~p\n", [Reason]),
- io:format("~p\n", [erlang:get_stacktrace()]),
+ io:format("~p\n", [Stk]),
my_halt(127)
end.
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 597830cf9a..257c829801 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -26,7 +26,7 @@
%%%
%%% The standard behaviour should export init_it/6.
%%%-----------------------------------------------------------------
--export([start/5, start/6, debug_options/2,
+-export([start/5, start/6, debug_options/2, hibernate_after/1,
name/1, unregister_name/1, get_proc_name/1, get_parent/0,
call/3, call/4, reply/2, stop/1, stop/3]).
@@ -408,6 +408,14 @@ spawn_opts(Options) ->
[]
end.
+hibernate_after(Options) ->
+ case lists:keyfind(hibernate_after, 1, Options) of
+ {_,HibernateAfterTimeout} ->
+ HibernateAfterTimeout;
+ false ->
+ infinity
+ end.
+
debug_options(Name, Opts) ->
case lists:keyfind(debug, 1, Opts) of
{_,Options} ->
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 4c80464680..da2b0da3ca 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -37,7 +37,7 @@
stop/1, stop/3,
notify/2, sync_notify/2,
add_handler/3, add_sup_handler/3, delete_handler/3, swap_handler/3,
- swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/4]).
+ swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/5]).
-export([init_it/6,
system_continue/3,
@@ -186,8 +186,9 @@ init_it(Starter, Parent, Name0, _, _, Options) ->
process_flag(trap_exit, true),
Name = gen:name(Name0),
Debug = gen:debug_options(Name, Options),
+ HibernateAfterTimeout = gen:hibernate_after(Options),
proc_lib:init_ack(Starter, {ok, self()}),
- loop(Parent, Name, [], Debug, false).
+ loop(Parent, Name, [], HibernateAfterTimeout, Debug, false).
-spec add_handler(emgr_ref(), handler(), term()) -> term().
add_handler(M, Handler, Args) -> rpc(M, {add_handler, Handler, Args}).
@@ -264,81 +265,83 @@ send(M, Cmd) ->
M ! Cmd,
ok.
-loop(Parent, ServerName, MSL, Debug, true) ->
- proc_lib:hibernate(?MODULE, wake_hib, [Parent, ServerName, MSL, Debug]);
-loop(Parent, ServerName, MSL, Debug, _) ->
- fetch_msg(Parent, ServerName, MSL, Debug, false).
+loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, true) ->
+ proc_lib:hibernate(?MODULE, wake_hib, [Parent, ServerName, MSL, HibernateAfterTimeout, Debug]);
+loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, _) ->
+ fetch_msg(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, false).
-wake_hib(Parent, ServerName, MSL, Debug) ->
- fetch_msg(Parent, ServerName, MSL, Debug, true).
+wake_hib(Parent, ServerName, MSL, HibernateAfterTimeout, Debug) ->
+ fetch_msg(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, true).
-fetch_msg(Parent, ServerName, MSL, Debug, Hib) ->
+fetch_msg(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, Hib) ->
receive
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
- [ServerName, MSL, Hib],Hib);
+ [ServerName, MSL, HibernateAfterTimeout, Hib],Hib);
{'EXIT', Parent, Reason} ->
terminate_server(Reason, Parent, MSL, ServerName);
Msg when Debug =:= [] ->
- handle_msg(Msg, Parent, ServerName, MSL, []);
+ handle_msg(Msg, Parent, ServerName, MSL, HibernateAfterTimeout, []);
Msg ->
Debug1 = sys:handle_debug(Debug, fun print_event/3,
ServerName, {in, Msg}),
- handle_msg(Msg, Parent, ServerName, MSL, Debug1)
+ handle_msg(Msg, Parent, ServerName, MSL, HibernateAfterTimeout, Debug1)
+ after HibernateAfterTimeout ->
+ loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, true)
end.
-handle_msg(Msg, Parent, ServerName, MSL, Debug) ->
+handle_msg(Msg, Parent, ServerName, MSL, HibernateAfterTimeout, Debug) ->
case Msg of
{notify, Event} ->
{Hib,MSL1} = server_notify(Event, handle_event, MSL, ServerName),
- loop(Parent, ServerName, MSL1, Debug, Hib);
+ loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib);
{_From, Tag, {sync_notify, Event}} ->
{Hib, MSL1} = server_notify(Event, handle_event, MSL, ServerName),
reply(Tag, ok),
- loop(Parent, ServerName, MSL1, Debug, Hib);
+ loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib);
{'EXIT', From, Reason} ->
MSL1 = handle_exit(From, Reason, MSL, ServerName),
- loop(Parent, ServerName, MSL1, Debug, false);
+ loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, false);
{_From, Tag, {call, Handler, Query}} ->
{Hib, Reply, MSL1} = server_call(Handler, Query, MSL, ServerName),
reply(Tag, Reply),
- loop(Parent, ServerName, MSL1, Debug, Hib);
+ loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib);
{_From, Tag, {add_handler, Handler, Args}} ->
{Hib, Reply, MSL1} = server_add_handler(Handler, Args, MSL),
reply(Tag, Reply),
- loop(Parent, ServerName, MSL1, Debug, Hib);
+ loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib);
{_From, Tag, {add_sup_handler, Handler, Args, SupP}} ->
{Hib, Reply, MSL1} = server_add_sup_handler(Handler, Args, MSL, SupP),
reply(Tag, Reply),
- loop(Parent, ServerName, MSL1, Debug, Hib);
+ loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib);
{_From, Tag, {delete_handler, Handler, Args}} ->
{Reply, MSL1} = server_delete_handler(Handler, Args, MSL,
ServerName),
reply(Tag, Reply),
- loop(Parent, ServerName, MSL1, Debug, false);
+ loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, false);
{_From, Tag, {swap_handler, Handler1, Args1, Handler2, Args2}} ->
{Hib, Reply, MSL1} = server_swap_handler(Handler1, Args1, Handler2,
Args2, MSL, ServerName),
reply(Tag, Reply),
- loop(Parent, ServerName, MSL1, Debug, Hib);
+ loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib);
{_From, Tag, {swap_sup_handler, Handler1, Args1, Handler2, Args2,
Sup}} ->
{Hib, Reply, MSL1} = server_swap_handler(Handler1, Args1, Handler2,
Args2, MSL, Sup, ServerName),
reply(Tag, Reply),
- loop(Parent, ServerName, MSL1, Debug, Hib);
+ loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib);
{_From, Tag, stop} ->
catch terminate_server(normal, Parent, MSL, ServerName),
reply(Tag, ok);
{_From, Tag, which_handlers} ->
reply(Tag, the_handlers(MSL)),
- loop(Parent, ServerName, MSL, Debug, false);
+ loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, false);
{_From, Tag, get_modules} ->
reply(Tag, get_modules(MSL)),
- loop(Parent, ServerName, MSL, Debug, false);
+ loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, false);
Other ->
{Hib, MSL1} = server_notify(Other, handle_info, MSL, ServerName),
- loop(Parent, ServerName, MSL1, Debug, Hib)
+ loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib)
end.
terminate_server(Reason, Parent, MSL, ServerName) ->
@@ -392,18 +395,18 @@ terminate_supervised(Pid, Reason, MSL, SName) ->
%%-----------------------------------------------------------------
%% Callback functions for system messages handling.
%%-----------------------------------------------------------------
-system_continue(Parent, Debug, [ServerName, MSL, Hib]) ->
- loop(Parent, ServerName, MSL, Debug, Hib).
+system_continue(Parent, Debug, [ServerName, MSL, HibernateAfterTimeout, Hib]) ->
+ loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, Hib).
-spec system_terminate(_, _, _, [_]) -> no_return().
-system_terminate(Reason, Parent, _Debug, [ServerName, MSL, _Hib]) ->
+system_terminate(Reason, Parent, _Debug, [ServerName, MSL, _HibernateAfterTimeout, _Hib]) ->
terminate_server(Reason, Parent, MSL, ServerName).
%%-----------------------------------------------------------------
%% Module here is sent in the system msg change_code. It specifies
%% which module should be changed.
%%-----------------------------------------------------------------
-system_code_change([ServerName, MSL, Hib], Module, OldVsn, Extra) ->
+system_code_change([ServerName, MSL, HibernateAfterTimeout, Hib], Module, OldVsn, Extra) ->
MSL1 = lists:zf(fun(H) when H#handler.module =:= Module ->
{ok, NewState} =
Module:code_change(OldVsn,
@@ -412,12 +415,12 @@ system_code_change([ServerName, MSL, Hib], Module, OldVsn, Extra) ->
(_) -> true
end,
MSL),
- {ok, [ServerName, MSL1, Hib]}.
+ {ok, [ServerName, MSL1, HibernateAfterTimeout, Hib]}.
-system_get_state([_ServerName, MSL, _Hib]) ->
+system_get_state([_ServerName, MSL, _HibernateAfterTimeout, _Hib]) ->
{ok, [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL]}.
-system_replace_state(StateFun, [ServerName, MSL, Hib]) ->
+system_replace_state(StateFun, [ServerName, MSL, HibernateAfterTimeout, Hib]) ->
{NMSL, NStates} =
lists:unzip([begin
Cur = {Mod,Id,State},
@@ -429,7 +432,7 @@ system_replace_state(StateFun, [ServerName, MSL, Hib]) ->
{HS, Cur}
end
end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]),
- {ok, NStates, [ServerName, NMSL, Hib]}.
+ {ok, NStates, [ServerName, NMSL, HibernateAfterTimeout, Hib]}.
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
@@ -798,7 +801,7 @@ get_modules(MSL) ->
%% Status information
%%-----------------------------------------------------------------
format_status(Opt, StatusData) ->
- [PDict, SysState, Parent, _Debug, [ServerName, MSL, _Hib]] = StatusData,
+ [PDict, SysState, Parent, _Debug, [ServerName, MSL, _HibernateAfterTimeout, _Hib]] = StatusData,
Header = gen:format_status_header("Status for event handler",
ServerName),
FmtMSL = [case erlang:function_exported(Mod, format_status, 2) of
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index f9d4286a7c..9ef0ca818c 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -113,7 +113,7 @@
sync_send_all_state_event/2, sync_send_all_state_event/3,
reply/2,
start_timer/2,send_event_after/2,cancel_timer/1,
- enter_loop/4, enter_loop/5, enter_loop/6, wake_hib/6]).
+ enter_loop/4, enter_loop/5, enter_loop/6, wake_hib/7]).
%% Internal exports
-export([init_it/6,
@@ -329,7 +329,8 @@ enter_loop(Mod, Options, StateName, StateData, ServerName, Timeout) ->
Name = gen:get_proc_name(ServerName),
Parent = gen:get_parent(),
Debug = gen:debug_options(Name, Options),
- loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug).
+ HibernateAfterTimeout = gen:hibernate_after(Options),
+ loop(Parent, Name, StateName, StateData, Mod, Timeout, HibernateAfterTimeout, Debug).
%%% ---------------------------------------------------
%%% Initiate the new process.
@@ -343,13 +344,14 @@ init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, Parent, Name0, Mod, Args, Options) ->
Name = gen:name(Name0),
Debug = gen:debug_options(Name, Options),
- case catch Mod:init(Args) of
+ HibernateAfterTimeout = gen:hibernate_after(Options),
+ case catch Mod:init(Args) of
{ok, StateName, StateData} ->
proc_lib:init_ack(Starter, {ok, self()}),
- loop(Parent, Name, StateName, StateData, Mod, infinity, Debug);
+ loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, Debug);
{ok, StateName, StateData, Timeout} ->
proc_lib:init_ack(Starter, {ok, self()}),
- loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug);
+ loop(Parent, Name, StateName, StateData, Mod, Timeout, HibernateAfterTimeout, Debug);
{stop, Reason} ->
gen:unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
@@ -371,68 +373,77 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
%%-----------------------------------------------------------------
%% The MAIN loop
%%-----------------------------------------------------------------
-loop(Parent, Name, StateName, StateData, Mod, hibernate, Debug) ->
+loop(Parent, Name, StateName, StateData, Mod, hibernate, HibernateAfterTimeout, Debug) ->
proc_lib:hibernate(?MODULE,wake_hib,
- [Parent, Name, StateName, StateData, Mod,
+ [Parent, Name, StateName, StateData, Mod, HibernateAfterTimeout,
Debug]);
-loop(Parent, Name, StateName, StateData, Mod, Time, Debug) ->
+
+loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, Debug) ->
+ receive
+ Msg ->
+ decode_msg(Msg,Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, Debug, false)
+ after HibernateAfterTimeout ->
+ loop(Parent, Name, StateName, StateData, Mod, hibernate, HibernateAfterTimeout, Debug)
+ end;
+
+loop(Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug) ->
Msg = receive
Input ->
Input
after Time ->
{'$gen_event', timeout}
end,
- decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, false).
+ decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug, false).
-wake_hib(Parent, Name, StateName, StateData, Mod, Debug) ->
+wake_hib(Parent, Name, StateName, StateData, Mod, HibernateAfterTimeout, Debug) ->
Msg = receive
Input ->
Input
end,
- decode_msg(Msg, Parent, Name, StateName, StateData, Mod, hibernate, Debug, true).
+ decode_msg(Msg, Parent, Name, StateName, StateData, Mod, hibernate, HibernateAfterTimeout, Debug, true).
-decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Hib) ->
+decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug, Hib) ->
case Msg of
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
- [Name, StateName, StateData, Mod, Time], Hib);
+ [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout], Hib);
{'EXIT', Parent, Reason} ->
terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug);
_Msg when Debug =:= [] ->
- handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time);
+ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout);
_Msg ->
Debug1 = sys:handle_debug(Debug, fun print_event/3,
{Name, StateName}, {in, Msg}),
handle_msg(Msg, Parent, Name, StateName, StateData,
- Mod, Time, Debug1)
+ Mod, Time, HibernateAfterTimeout, Debug1)
end.
%%-----------------------------------------------------------------
%% Callback functions for system messages handling.
%%-----------------------------------------------------------------
-system_continue(Parent, Debug, [Name, StateName, StateData, Mod, Time]) ->
- loop(Parent, Name, StateName, StateData, Mod, Time, Debug).
+system_continue(Parent, Debug, [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout]) ->
+ loop(Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug).
-spec system_terminate(term(), _, _, [term(),...]) -> no_return().
system_terminate(Reason, _Parent, Debug,
- [Name, StateName, StateData, Mod, _Time]) ->
+ [Name, StateName, StateData, Mod, _Time, _HibernateAfterTimeout]) ->
terminate(Reason, Name, [], Mod, StateName, StateData, Debug).
-system_code_change([Name, StateName, StateData, Mod, Time],
+system_code_change([Name, StateName, StateData, Mod, Time, HibernateAfterTimeout],
_Module, OldVsn, Extra) ->
case catch Mod:code_change(OldVsn, StateName, StateData, Extra) of
{ok, NewStateName, NewStateData} ->
- {ok, [Name, NewStateName, NewStateData, Mod, Time]};
+ {ok, [Name, NewStateName, NewStateData, Mod, Time, HibernateAfterTimeout]};
Else -> Else
end.
-system_get_state([_Name, StateName, StateData, _Mod, _Time]) ->
+system_get_state([_Name, StateName, StateData, _Mod, _Time, _HibernateAfterTimeout]) ->
{ok, {StateName, StateData}}.
-system_replace_state(StateFun, [Name, StateName, StateData, Mod, Time]) ->
+system_replace_state(StateFun, [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout]) ->
Result = {NStateName, NStateData} = StateFun({StateName, StateData}),
- {ok, Result, [Name, NStateName, NStateData, Mod, Time]}.
+ {ok, Result, [Name, NStateName, NStateData, Mod, Time, HibernateAfterTimeout]}.
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
@@ -467,19 +478,19 @@ print_event(Dev, return, {Name, StateName}) ->
io:format(Dev, "*DBG* ~p switched to state ~w~n",
[Name, StateName]).
-handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time) -> %No debug here
+handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTimeout) -> %No debug here
From = from(Msg),
case catch dispatch(Msg, Mod, StateName, StateData) of
{next_state, NStateName, NStateData} ->
- loop(Parent, Name, NStateName, NStateData, Mod, infinity, []);
+ loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, []);
{next_state, NStateName, NStateData, Time1} ->
- loop(Parent, Name, NStateName, NStateData, Mod, Time1, []);
+ loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, []);
{reply, Reply, NStateName, NStateData} when From =/= undefined ->
reply(From, Reply),
- loop(Parent, Name, NStateName, NStateData, Mod, infinity, []);
+ loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, []);
{reply, Reply, NStateName, NStateData, Time1} when From =/= undefined ->
reply(From, Reply),
- loop(Parent, Name, NStateName, NStateData, Mod, Time1, []);
+ loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, []);
{stop, Reason, NStateData} ->
terminate(Reason, Name, Msg, Mod, StateName, NStateData, []);
{stop, Reason, Reply, NStateData} when From =/= undefined ->
@@ -490,7 +501,7 @@ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time) -> %No debug her
{'EXIT', {undef, [{Mod, handle_info, [_,_,_], _}|_]}} ->
error_logger:warning_msg("** Undefined handle_info in ~p~n"
"** Unhandled message: ~p~n", [Mod, Msg]),
- loop(Parent, Name, StateName, StateData, Mod, infinity, []);
+ loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, []);
{'EXIT', What} ->
terminate(What, Name, Msg, Mod, StateName, StateData, []);
Reply ->
@@ -498,23 +509,23 @@ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time) -> %No debug her
Name, Msg, Mod, StateName, StateData, [])
end.
-handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, Debug) ->
+handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTimeout, Debug) ->
From = from(Msg),
case catch dispatch(Msg, Mod, StateName, StateData) of
{next_state, NStateName, NStateData} ->
Debug1 = sys:handle_debug(Debug, fun print_event/3,
{Name, NStateName}, return),
- loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1);
+ loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, Debug1);
{next_state, NStateName, NStateData, Time1} ->
Debug1 = sys:handle_debug(Debug, fun print_event/3,
{Name, NStateName}, return),
- loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1);
+ loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, Debug1);
{reply, Reply, NStateName, NStateData} when From =/= undefined ->
Debug1 = reply(Name, From, Reply, Debug, NStateName),
- loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1);
+ loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, Debug1);
{reply, Reply, NStateName, NStateData, Time1} when From =/= undefined ->
Debug1 = reply(Name, From, Reply, Debug, NStateName),
- loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1);
+ loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, Debug1);
{stop, Reason, NStateData} ->
terminate(Reason, Name, Msg, Mod, StateName, NStateData, Debug);
{stop, Reason, Reply, NStateData} when From =/= undefined ->
@@ -645,7 +656,7 @@ get_msg(Msg) -> Msg.
%% Status information
%%-----------------------------------------------------------------
format_status(Opt, StatusData) ->
- [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time]] =
+ [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time, _HibernateAfterTimeout]] =
StatusData,
Header = gen:format_status_header("Status for state machine",
Name),
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index e628fec00f..a3d53efd0d 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -94,7 +94,7 @@
cast/2, reply/2,
abcast/2, abcast/3,
multi_call/2, multi_call/3, multi_call/4,
- enter_loop/3, enter_loop/4, enter_loop/5, wake_hib/5]).
+ enter_loop/3, enter_loop/4, enter_loop/5, wake_hib/6]).
%% System exports
-export([system_continue/3,
@@ -107,7 +107,9 @@
%% Internal exports
-export([init_it/6]).
--import(error_logger, [format/2]).
+-define(
+ STACKTRACE(),
+ try throw(ok) catch _ -> erlang:get_stacktrace() end).
%%%=========================================================================
%%% API
@@ -307,7 +309,8 @@ enter_loop(Mod, Options, State, ServerName, Timeout) ->
Name = gen:get_proc_name(ServerName),
Parent = gen:get_parent(),
Debug = gen:debug_options(Name, Options),
- loop(Parent, Name, State, Mod, Timeout, Debug).
+ HibernateAfterTimeout = gen:hibernate_after(Options),
+ loop(Parent, Name, State, Mod, Timeout, HibernateAfterTimeout, Debug).
%%%========================================================================
%%% Gen-callback functions
@@ -325,14 +328,16 @@ init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, Parent, Name0, Mod, Args, Options) ->
Name = gen:name(Name0),
Debug = gen:debug_options(Name, Options),
- case catch Mod:init(Args) of
- {ok, State} ->
+ HibernateAfterTimeout = gen:hibernate_after(Options),
+
+ case init_it(Mod, Args) of
+ {ok, {ok, State}} ->
proc_lib:init_ack(Starter, {ok, self()}),
- loop(Parent, Name, State, Mod, infinity, Debug);
- {ok, State, Timeout} ->
+ loop(Parent, Name, State, Mod, infinity, HibernateAfterTimeout, Debug);
+ {ok, {ok, State, Timeout}} ->
proc_lib:init_ack(Starter, {ok, self()}),
- loop(Parent, Name, State, Mod, Timeout, Debug);
- {stop, Reason} ->
+ loop(Parent, Name, State, Mod, Timeout, HibernateAfterTimeout, Debug);
+ {ok, {stop, Reason}} ->
%% For consistency, we must make sure that the
%% registered name (if any) is unregistered before
%% the parent process is notified about the failure.
@@ -342,18 +347,25 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
gen:unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
- ignore ->
+ {ok, ignore} ->
gen:unregister_name(Name0),
proc_lib:init_ack(Starter, ignore),
exit(normal);
- {'EXIT', Reason} ->
- gen:unregister_name(Name0),
- proc_lib:init_ack(Starter, {error, Reason}),
- exit(Reason);
- Else ->
+ {ok, Else} ->
Error = {bad_return_value, Else},
proc_lib:init_ack(Starter, {error, Error}),
- exit(Error)
+ exit(Error);
+ {'EXIT', Class, Reason, Stacktrace} ->
+ gen:unregister_name(Name0),
+ proc_lib:init_ack(Starter, {error, terminate_reason(Class, Reason, Stacktrace)}),
+ erlang:raise(Class, Reason, Stacktrace)
+ end.
+init_it(Mod, Args) ->
+ try
+ {ok, Mod:init(Args)}
+ catch
+ throw:R -> {ok, R};
+ Class:R -> {'EXIT', Class, R, erlang:get_stacktrace()}
end.
%%%========================================================================
@@ -362,37 +374,46 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
%%% ---------------------------------------------------
%%% The MAIN loop.
%%% ---------------------------------------------------
-loop(Parent, Name, State, Mod, hibernate, Debug) ->
- proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, Debug]);
-loop(Parent, Name, State, Mod, Time, Debug) ->
+loop(Parent, Name, State, Mod, hibernate, HibernateAfterTimeout, Debug) ->
+ proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, HibernateAfterTimeout, Debug]);
+
+loop(Parent, Name, State, Mod, infinity, HibernateAfterTimeout, Debug) ->
+ receive
+ Msg ->
+ decode_msg(Msg, Parent, Name, State, Mod, infinity, HibernateAfterTimeout, Debug, false)
+ after HibernateAfterTimeout ->
+ loop(Parent, Name, State, Mod, hibernate, HibernateAfterTimeout, Debug)
+ end;
+
+loop(Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug) ->
Msg = receive
Input ->
Input
after Time ->
timeout
end,
- decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, false).
+ decode_msg(Msg, Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug, false).
-wake_hib(Parent, Name, State, Mod, Debug) ->
+wake_hib(Parent, Name, State, Mod, HibernateAfterTimeout, Debug) ->
Msg = receive
Input ->
Input
end,
- decode_msg(Msg, Parent, Name, State, Mod, hibernate, Debug, true).
+ decode_msg(Msg, Parent, Name, State, Mod, hibernate, HibernateAfterTimeout, Debug, true).
-decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
+decode_msg(Msg, Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug, Hib) ->
case Msg of
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
- [Name, State, Mod, Time], Hib);
+ [Name, State, Mod, Time, HibernateAfterTimeout], Hib);
{'EXIT', Parent, Reason} ->
- terminate(Reason, Name, undefined, Msg, Mod, State, Debug);
+ terminate(Reason, ?STACKTRACE(), Name, undefined, Msg, Mod, State, Debug);
_Msg when Debug =:= [] ->
- handle_msg(Msg, Parent, Name, State, Mod);
+ handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout);
_Msg ->
Debug1 = sys:handle_debug(Debug, fun print_event/3,
Name, {in, Msg}),
- handle_msg(Msg, Parent, Name, State, Mod, Debug1)
+ handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout, Debug1)
end.
%%% ---------------------------------------------------
@@ -578,17 +599,11 @@ start_monitor(Node, Name) when is_atom(Node), is_atom(Name) ->
%% ---------------------------------------------------
%% Helper functions for try-catch of callbacks.
%% Returns the return value of the callback, or
-%% {'EXIT', ExitReason, ReportReason} (if an exception occurs)
-%%
-%% ExitReason is the reason that shall be used when the process
-%% terminates.
-%%
-%% ReportReason is the reason that shall be printed in the error
-%% report.
+%% {'EXIT', Class, Reason, Stack} (if an exception occurs)
%%
-%% These functions are introduced in order to add the stack trace in
-%% the error report produced when a callback is terminated with
-%% erlang:exit/1 (OTP-12263).
+%% The Class, Reason and Stack are given to erlang:raise/3
+%% to make sure proc_lib receives the proper reasons and
+%% stacktraces.
%% ---------------------------------------------------
try_dispatch({'$gen_cast', Msg}, Mod, State) ->
@@ -610,15 +625,10 @@ try_dispatch(Mod, Func, Msg, State) ->
[Mod, Msg]),
{ok, {noreply, State}};
true ->
- Stacktrace = erlang:get_stacktrace(),
- {'EXIT', {R, Stacktrace}, {R, Stacktrace}}
+ {'EXIT', error, R, erlang:get_stacktrace()}
end;
- error:R ->
- Stacktrace = erlang:get_stacktrace(),
- {'EXIT', {R, Stacktrace}, {R, Stacktrace}};
- exit:R ->
- Stacktrace = erlang:get_stacktrace(),
- {'EXIT', R, {R, Stacktrace}}
+ Class:R ->
+ {'EXIT', Class, R, erlang:get_stacktrace()}
end.
try_handle_call(Mod, Msg, From, State) ->
@@ -627,12 +637,8 @@ try_handle_call(Mod, Msg, From, State) ->
catch
throw:R ->
{ok, R};
- error:R ->
- Stacktrace = erlang:get_stacktrace(),
- {'EXIT', {R, Stacktrace}, {R, Stacktrace}};
- exit:R ->
- Stacktrace = erlang:get_stacktrace(),
- {'EXIT', R, {R, Stacktrace}}
+ Class:R ->
+ {'EXIT', Class, R, erlang:get_stacktrace()}
end.
try_terminate(Mod, Reason, State) ->
@@ -643,12 +649,8 @@ try_terminate(Mod, Reason, State) ->
catch
throw:R ->
{ok, R};
- error:R ->
- Stacktrace = erlang:get_stacktrace(),
- {'EXIT', {R, Stacktrace}, {R, Stacktrace}};
- exit:R ->
- Stacktrace = erlang:get_stacktrace(),
- {'EXIT', R, {R, Stacktrace}}
+ Class:R ->
+ {'EXIT', Class, R, erlang:get_stacktrace()}
end;
false ->
{ok, ok}
@@ -659,89 +661,91 @@ try_terminate(Mod, Reason, State) ->
%%% Message handling functions
%%% ---------------------------------------------------
-handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod) ->
+handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, HibernateAfterTimeout) ->
Result = try_handle_call(Mod, Msg, From, State),
case Result of
{ok, {reply, Reply, NState}} ->
reply(From, Reply),
- loop(Parent, Name, NState, Mod, infinity, []);
+ loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, []);
{ok, {reply, Reply, NState, Time1}} ->
reply(From, Reply),
- loop(Parent, Name, NState, Mod, Time1, []);
+ loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, []);
{ok, {noreply, NState}} ->
- loop(Parent, Name, NState, Mod, infinity, []);
+ loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, []);
{ok, {noreply, NState, Time1}} ->
- loop(Parent, Name, NState, Mod, Time1, []);
+ loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, []);
{ok, {stop, Reason, Reply, NState}} ->
- {'EXIT', R} =
- (catch terminate(Reason, Name, From, Msg, Mod, NState, [])),
- reply(From, Reply),
- exit(R);
- Other -> handle_common_reply(Other, Parent, Name, From, Msg, Mod, State)
+ try
+ terminate(Reason, ?STACKTRACE(), Name, From, Msg, Mod, NState, [])
+ after
+ reply(From, Reply)
+ end;
+ Other -> handle_common_reply(Other, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State)
end;
-handle_msg(Msg, Parent, Name, State, Mod) ->
+handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout) ->
Reply = try_dispatch(Msg, Mod, State),
- handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, State).
+ handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, HibernateAfterTimeout, State).
-handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) ->
+handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, HibernateAfterTimeout, Debug) ->
Result = try_handle_call(Mod, Msg, From, State),
case Result of
{ok, {reply, Reply, NState}} ->
Debug1 = reply(Name, From, Reply, NState, Debug),
- loop(Parent, Name, NState, Mod, infinity, Debug1);
+ loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, Debug1);
{ok, {reply, Reply, NState, Time1}} ->
Debug1 = reply(Name, From, Reply, NState, Debug),
- loop(Parent, Name, NState, Mod, Time1, Debug1);
+ loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, Debug1);
{ok, {noreply, NState}} ->
Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,
{noreply, NState}),
- loop(Parent, Name, NState, Mod, infinity, Debug1);
+ loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, Debug1);
{ok, {noreply, NState, Time1}} ->
Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,
{noreply, NState}),
- loop(Parent, Name, NState, Mod, Time1, Debug1);
+ loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, Debug1);
{ok, {stop, Reason, Reply, NState}} ->
- {'EXIT', R} =
- (catch terminate(Reason, Name, From, Msg, Mod, NState, Debug)),
- _ = reply(Name, From, Reply, NState, Debug),
- exit(R);
+ try
+ terminate(Reason, ?STACKTRACE(), Name, From, Msg, Mod, NState, Debug)
+ after
+ _ = reply(Name, From, Reply, NState, Debug)
+ end;
Other ->
- handle_common_reply(Other, Parent, Name, From, Msg, Mod, State, Debug)
+ handle_common_reply(Other, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State, Debug)
end;
-handle_msg(Msg, Parent, Name, State, Mod, Debug) ->
+handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout, Debug) ->
Reply = try_dispatch(Msg, Mod, State),
- handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, State, Debug).
+ handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, HibernateAfterTimeout, State, Debug).
-handle_common_reply(Reply, Parent, Name, From, Msg, Mod, State) ->
+handle_common_reply(Reply, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State) ->
case Reply of
{ok, {noreply, NState}} ->
- loop(Parent, Name, NState, Mod, infinity, []);
+ loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, []);
{ok, {noreply, NState, Time1}} ->
- loop(Parent, Name, NState, Mod, Time1, []);
+ loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, []);
{ok, {stop, Reason, NState}} ->
- terminate(Reason, Name, From, Msg, Mod, NState, []);
- {'EXIT', ExitReason, ReportReason} ->
- terminate(ExitReason, ReportReason, Name, From, Msg, Mod, State, []);
+ terminate(Reason, ?STACKTRACE(), Name, From, Msg, Mod, NState, []);
+ {'EXIT', Class, Reason, Stacktrace} ->
+ terminate(Class, Reason, Stacktrace, Name, From, Msg, Mod, State, []);
{ok, BadReply} ->
- terminate({bad_return_value, BadReply}, Name, From, Msg, Mod, State, [])
+ terminate({bad_return_value, BadReply}, ?STACKTRACE(), Name, From, Msg, Mod, State, [])
end.
-handle_common_reply(Reply, Parent, Name, From, Msg, Mod, State, Debug) ->
+handle_common_reply(Reply, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State, Debug) ->
case Reply of
{ok, {noreply, NState}} ->
Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,
{noreply, NState}),
- loop(Parent, Name, NState, Mod, infinity, Debug1);
+ loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, Debug1);
{ok, {noreply, NState, Time1}} ->
Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,
{noreply, NState}),
- loop(Parent, Name, NState, Mod, Time1, Debug1);
+ loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, Debug1);
{ok, {stop, Reason, NState}} ->
- terminate(Reason, Name, From, Msg, Mod, NState, Debug);
- {'EXIT', ExitReason, ReportReason} ->
- terminate(ExitReason, ReportReason, Name, From, Msg, Mod, State, Debug);
+ terminate(Reason, ?STACKTRACE(), Name, From, Msg, Mod, NState, Debug);
+ {'EXIT', Class, Reason, Stacktrace} ->
+ terminate(Class, Reason, Stacktrace, Name, From, Msg, Mod, State, Debug);
{ok, BadReply} ->
- terminate({bad_return_value, BadReply}, Name, From, Msg, Mod, State, Debug)
+ terminate({bad_return_value, BadReply}, ?STACKTRACE(), Name, From, Msg, Mod, State, Debug)
end.
reply(Name, {To, Tag}, Reply, State, Debug) ->
@@ -753,26 +757,26 @@ reply(Name, {To, Tag}, Reply, State, Debug) ->
%%-----------------------------------------------------------------
%% Callback functions for system messages handling.
%%-----------------------------------------------------------------
-system_continue(Parent, Debug, [Name, State, Mod, Time]) ->
- loop(Parent, Name, State, Mod, Time, Debug).
+system_continue(Parent, Debug, [Name, State, Mod, Time, HibernateAfterTimeout]) ->
+ loop(Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug).
-spec system_terminate(_, _, _, [_]) -> no_return().
-system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time]) ->
- terminate(Reason, Name, undefined, [], Mod, State, Debug).
+system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time, _HibernateAfterTimeout]) ->
+ terminate(Reason, ?STACKTRACE(), Name, undefined, [], Mod, State, Debug).
-system_code_change([Name, State, Mod, Time], _Module, OldVsn, Extra) ->
+system_code_change([Name, State, Mod, Time, HibernateAfterTimeout], _Module, OldVsn, Extra) ->
case catch Mod:code_change(OldVsn, State, Extra) of
- {ok, NewState} -> {ok, [Name, NewState, Mod, Time]};
+ {ok, NewState} -> {ok, [Name, NewState, Mod, Time, HibernateAfterTimeout]};
Else -> Else
end.
-system_get_state([_Name, State, _Mod, _Time]) ->
+system_get_state([_Name, State, _Mod, _Time, _HibernateAfterTimeout]) ->
{ok, State}.
-system_replace_state(StateFun, [Name, State, Mod, Time]) ->
+system_replace_state(StateFun, [Name, State, Mod, Time, HibernateAfterTimeout]) ->
NState = StateFun(State),
- {ok, NState, [Name, NState, Mod, Time]}.
+ {ok, NState, [Name, NState, Mod, Time, HibernateAfterTimeout]}.
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
@@ -800,35 +804,58 @@ print_event(Dev, Event, Name) ->
%%% ---------------------------------------------------
%%% Terminate the server.
+%%%
+%%% terminate/8 is triggered by {stop, Reason} or bad
+%%% return values. The stacktrace is generated via the
+%%% ?STACKTRACE() macro and the ReportReason must not
+%%% be wrapped in tuples.
+%%%
+%%% terminate/9 is triggered in case of error/exit in
+%%% the user callback. In this case the report reason
+%%% always includes the user stacktrace.
+%%%
+%%% The reason received in the terminate/2 callbacks
+%%% always includes the stacktrace for errors and never
+%%% for exits.
%%% ---------------------------------------------------
--spec terminate(_, _, _, _, _, _, _) -> no_return().
-terminate(Reason, Name, From, Msg, Mod, State, Debug) ->
- terminate(Reason, Reason, Name, From, Msg, Mod, State, Debug).
-
-spec terminate(_, _, _, _, _, _, _, _) -> no_return().
-terminate(ExitReason, ReportReason, Name, From, Msg, Mod, State, Debug) ->
- Reply = try_terminate(Mod, ExitReason, State),
+terminate(Reason, Stacktrace, Name, From, Msg, Mod, State, Debug) ->
+ terminate(exit, Reason, Stacktrace, Reason, Name, From, Msg, Mod, State, Debug).
+
+-spec terminate(_, _, _, _, _, _, _, _, _) -> no_return().
+terminate(Class, Reason, Stacktrace, Name, From, Msg, Mod, State, Debug) ->
+ ReportReason = {Reason, Stacktrace},
+ terminate(Class, Reason, Stacktrace, ReportReason, Name, From, Msg, Mod, State, Debug).
+
+-spec terminate(_, _, _, _, _, _, _, _, _, _) -> no_return().
+terminate(Class, Reason, Stacktrace, ReportReason, Name, From, Msg, Mod, State, Debug) ->
+ Reply = try_terminate(Mod, terminate_reason(Class, Reason, Stacktrace), State),
case Reply of
- {'EXIT', ExitReason1, ReportReason1} ->
+ {'EXIT', C, R, S} ->
FmtState = format_status(terminate, Mod, get(), State),
- error_info(ReportReason1, Name, From, Msg, FmtState, Debug),
- exit(ExitReason1);
+ error_info({R, S}, Name, From, Msg, FmtState, Debug),
+ erlang:raise(C, R, S);
_ ->
- case ExitReason of
- normal ->
- exit(normal);
- shutdown ->
- exit(shutdown);
- {shutdown,_}=Shutdown ->
- exit(Shutdown);
+ case {Class, Reason} of
+ {exit, normal} -> ok;
+ {exit, shutdown} -> ok;
+ {exit, {shutdown,_}} -> ok;
_ ->
FmtState = format_status(terminate, Mod, get(), State),
- error_info(ReportReason, Name, From, Msg, FmtState, Debug),
- exit(ExitReason)
+ error_info(ReportReason, Name, From, Msg, FmtState, Debug)
end
+ end,
+ case Stacktrace of
+ [] ->
+ erlang:Class(Reason);
+ _ ->
+ erlang:raise(Class, Reason, Stacktrace)
end.
+terminate_reason(error, Reason, Stacktrace) -> {Reason, Stacktrace};
+terminate_reason(exit, Reason, _Stacktrace) -> Reason.
+
error_info(_Reason, application_controller, _From, _Msg, _State, _Debug) ->
%% OTP-5811 Don't send an error report if it's the system process
%% application_controller which is terminating - let init take care
@@ -850,17 +877,17 @@ error_info(Reason, Name, From, Msg, State, Debug) ->
end
end;
_ ->
- Reason
- end,
+ error_logger:limit_term(Reason)
+ end,
{ClientFmt, ClientArgs} = client_stacktrace(From),
- format("** Generic server ~p terminating \n"
- "** Last message in was ~p~n"
- "** When Server state == ~p~n"
- "** Reason for termination == ~n** ~p~n" ++ ClientFmt,
- [Name, Msg, State, Reason1] ++ ClientArgs),
+ LimitedState = error_logger:limit_term(State),
+ error_logger:format("** Generic server ~p terminating \n"
+ "** Last message in was ~p~n"
+ "** When Server state == ~p~n"
+ "** Reason for termination == ~n** ~p~n" ++ ClientFmt,
+ [Name, Msg, LimitedState, Reason1] ++ ClientArgs),
sys:print_log(Debug),
ok.
-
client_stacktrace(undefined) ->
{"", []};
client_stacktrace({From, _Tag}) ->
@@ -885,7 +912,7 @@ client_stacktrace(From) when is_pid(From) ->
%% Status information
%%-----------------------------------------------------------------
format_status(Opt, StatusData) ->
- [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData,
+ [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time, _HibernateAfterTimeout]] = StatusData,
Header = gen:format_status_header("Status for generic server", Name),
Log = sys:get_debug(log, Debug, []),
Specfic = case format_status(Opt, Mod, PDict, State) of
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 6f566b8beb..b5e9da1e66 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -369,9 +369,12 @@ event_type(Type) ->
Dbgs ::
['trace' | 'log' | 'statistics' | 'debug'
| {'logfile', string()}]}.
+-type hibernate_after_opt() ::
+ {'hibernate_after', HibernateAfterTimeout :: timeout()}.
-type start_opt() ::
debug_opt()
| {'timeout', Time :: timeout()}
+ | hibernate_after_opt()
| {'spawn_opt', [proc_lib:spawn_option()]}.
-type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}.
@@ -544,14 +547,14 @@ reply({To,Tag}, Reply) when is_pid(To) ->
%% started by proc_lib into a state machine using
%% the same arguments as you would have returned from init/1
-spec enter_loop(
- Module :: module(), Opts :: [debug_opt()],
+ Module :: module(), Opts :: [debug_opt() | hibernate_after_opt()],
State :: state(), Data :: data()) ->
no_return().
enter_loop(Module, Opts, State, Data) ->
enter_loop(Module, Opts, State, Data, self()).
%%
-spec enter_loop(
- Module :: module(), Opts :: [debug_opt()],
+ Module :: module(), Opts :: [debug_opt() | hibernate_after_opt()],
State :: state(), Data :: data(),
Server_or_Actions ::
server_name() | pid() | [action()]) ->
@@ -565,7 +568,7 @@ enter_loop(Module, Opts, State, Data, Server_or_Actions) ->
end.
%%
-spec enter_loop(
- Module :: module(), Opts :: [debug_opt()],
+ Module :: module(), Opts :: [debug_opt() | hibernate_after_opt()],
State :: state(), Data :: data(),
Server :: server_name() | pid(),
Actions :: [action()] | action()) ->
@@ -605,7 +608,8 @@ enter(Module, Opts, State, Data, Server, Actions, Parent) ->
%% The values should already have been type checked
Name = gen:get_proc_name(Server),
Debug = gen:debug_options(Name, Opts),
- Events = [],
+ HibernateAfterTimeout = gen:hibernate_after(Opts),
+ Events = [],
P = [],
Event = {internal,init_state},
%% We enforce {postpone,false} to ensure that
@@ -648,6 +652,7 @@ enter(Module, Opts, State, Data, Server, Actions, Parent) ->
timer_refs => TimerRefs,
timer_types => TimerTypes,
hibernate => Hibernate,
+ hibernate_after => HibernateAfterTimeout,
cancel_timers => CancelTimers
},
NewDebug = sys_debug(Debug, S, State, {enter,Event,State}),
@@ -854,7 +859,7 @@ loop_hibernate(Parent, Debug, S) ->
{wakeup_from_hibernate,3}}).
%% Entry point for wakeup_from_hibernate/3
-loop_receive(Parent, Debug, S) ->
+loop_receive(Parent, Debug, #{hibernate_after := HibernateAfterTimeout} = S) ->
receive
Msg ->
case Msg of
@@ -956,6 +961,9 @@ loop_receive(Parent, Debug, S) ->
loop_receive_result(
Parent, Debug, S, Hibernate, Event)
end
+ after
+ HibernateAfterTimeout ->
+ loop_hibernate(Parent, Debug, S)
end.
loop_receive_result(
@@ -1714,6 +1722,8 @@ error_info(
end;
_ -> {Reason,Stacktrace}
end,
+ [LimitedP, LimitedFmtData, LimitedFixedReason] =
+ [error_logger:limit_term(D) || D <- [P, FmtData, FixedReason]],
CBMode =
case StateEnter of
true ->
@@ -1747,8 +1757,8 @@ error_info(
[] -> [];
[Event|_] -> [Event]
end] ++
- [FmtData,
- Class,FixedReason,
+ [LimitedFmtData,
+ Class,LimitedFixedReason,
CBMode] ++
case Q of
[_|[_|_] = Events] -> [Events];
@@ -1756,7 +1766,7 @@ error_info(
end ++
case P of
[] -> [];
- _ -> [P]
+ _ -> [LimitedP]
end ++
case FixedStacktrace of
[] -> [];
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 5ed2f4d888..9d447418f8 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -84,6 +84,8 @@
-export([write_unicode_string/1, write_unicode_char/1,
deep_unicode_char_list/1]).
+-export([limit_term/2]).
+
-export_type([chars/0, latin1_string/0, continuation/0,
fread_error/0, fread_item/0, format_spec/0]).
@@ -911,3 +913,116 @@ binrev(L) ->
binrev(L, T) ->
list_to_binary(lists:reverse(L, T)).
+
+-spec limit_term(term(), non_neg_integer()) -> term().
+
+%% The intention is to mimic the depth limitation of io_lib:write()
+%% and io_lib_pretty:print(). The leaves ('...') should never be
+%% seen when printed with the same depth. Bitstrings are never
+%% truncated, which is OK as long as they are not sent to other nodes.
+limit_term(Term, Depth) ->
+ try test_limit(Term, Depth) of
+ ok -> Term
+ catch
+ throw:limit ->
+ limit(Term, Depth)
+ end.
+
+limit(_, 0) -> '...';
+limit([H|T]=L, D) ->
+ if
+ D =:= 1 -> '...';
+ true ->
+ case printable_list(L) of
+ true -> L;
+ false ->
+ [limit(H, D-1)|limit_tail(T, D-1)]
+ end
+ end;
+limit(Term, D) when is_map(Term) ->
+ limit_map(Term, D);
+limit({}=T, _D) -> T;
+limit(T, D) when is_tuple(T) ->
+ if
+ D =:= 1 -> '...';
+ true ->
+ list_to_tuple([limit(element(1, T), D-1)|
+ limit_tail(tl(tuple_to_list(T)), D-1)])
+ end;
+limit(<<_/bitstring>>=Term, D) -> limit_bitstring(Term, D);
+limit(Term, _D) -> Term.
+
+limit_tail([], _D) -> [];
+limit_tail(_, 1) -> ['...'];
+limit_tail([H|T], D) ->
+ [limit(H, D-1)|limit_tail(T, D-1)];
+limit_tail(Other, D) ->
+ limit(Other, D-1).
+
+%% Cannot limit maps properly since there is no guarantee that
+%% maps:from_list() creates a map with the same internal ordering of
+%% the selected associations as in Map.
+limit_map(Map, D) ->
+ maps:from_list(erts_internal:maps_to_list(Map, D)).
+%% maps:from_list(limit_map_body(erts_internal:maps_to_list(Map, D), D)).
+
+%% limit_map_body(_, 0) -> [{'...', '...'}];
+%% limit_map_body([], _) -> [];
+%% limit_map_body([{K,V}], D) -> [limit_map_assoc(K, V, D)];
+%% limit_map_body([{K,V}|KVs], D) ->
+%% [limit_map_assoc(K, V, D) | limit_map_body(KVs, D-1)].
+
+%% limit_map_assoc(K, V, D) ->
+%% {limit(K, D-1), limit(V, D-1)}.
+
+limit_bitstring(B, _D) -> B. %% Keeps all printable binaries.
+
+test_limit(_, 0) -> throw(limit);
+test_limit([H|T]=L, D) when is_integer(D) ->
+ if
+ D =:= 1 -> throw(limit);
+ true ->
+ case printable_list(L) of
+ true -> ok;
+ false ->
+ test_limit(H, D-1),
+ test_limit_tail(T, D-1)
+ end
+ end;
+test_limit(Term, D) when is_map(Term) ->
+ test_limit_map(Term, D);
+test_limit({}, _D) -> ok;
+test_limit(T, D) when is_tuple(T) ->
+ test_limit_tuple(T, 1, tuple_size(T), D);
+test_limit(<<_/bitstring>>=Term, D) -> test_limit_bitstring(Term, D);
+test_limit(_Term, _D) -> ok.
+
+test_limit_tail([], _D) -> ok;
+test_limit_tail(_, 1) -> throw(limit);
+test_limit_tail([H|T], D) ->
+ test_limit(H, D-1),
+ test_limit_tail(T, D-1);
+test_limit_tail(Other, D) ->
+ test_limit(Other, D-1).
+
+test_limit_tuple(_T, I, Sz, _D) when I > Sz -> ok;
+test_limit_tuple(_, _, _, 1) -> throw(limit);
+test_limit_tuple(T, I, Sz, D) ->
+ test_limit(element(I, T), D-1),
+ test_limit_tuple(T, I+1, Sz, D-1).
+
+test_limit_map(_Map, _D) -> ok.
+%% test_limit_map_body(erts_internal:maps_to_list(Map, D), D).
+
+%% test_limit_map_body(_, 0) -> throw(limit);
+%% test_limit_map_body([], _) -> ok;
+%% test_limit_map_body([{K,V}], D) -> test_limit_map_assoc(K, V, D);
+%% test_limit_map_body([{K,V}|KVs], D) ->
+%% test_limit_map_assoc(K, V, D),
+%% test_limit_map_body(KVs, D-1).
+
+%% test_limit_map_assoc(K, V, D) ->
+%% test_limit(K, D-1),
+%% test_limit(V, D-1).
+
+test_limit_bitstring(_, _) -> ok.
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 363705b0f4..3fa54cd0d5 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -264,12 +264,12 @@ exit_p(Class, Reason, Stacktrace) ->
case get('$initial_call') of
{M,F,A} when is_atom(M), is_atom(F), is_integer(A) ->
MFA = {M,F,make_dummy_args(A, [])},
- crash_report(Class, Reason, MFA),
+ crash_report(Class, Reason, MFA, Stacktrace),
erlang:raise(exit, exit_reason(Class, Reason, Stacktrace), Stacktrace);
_ ->
%% The process dictionary has been cleared or
%% possibly modified.
- crash_report(Class, Reason, []),
+ crash_report(Class, Reason, [], Stacktrace),
erlang:raise(exit, exit_reason(Class, Reason, Stacktrace), Stacktrace)
end.
@@ -499,26 +499,28 @@ trans_init(M, F, A) when is_atom(M), is_atom(F) ->
%% Generate a crash report.
%% -----------------------------------------------------
-crash_report(exit, normal, _) -> ok;
-crash_report(exit, shutdown, _) -> ok;
-crash_report(exit, {shutdown,_}, _) -> ok;
-crash_report(Class, Reason, StartF) ->
- OwnReport = my_info(Class, Reason, StartF),
+crash_report(exit, normal, _, _) -> ok;
+crash_report(exit, shutdown, _, _) -> ok;
+crash_report(exit, {shutdown,_}, _, _) -> ok;
+crash_report(Class, Reason, StartF, Stacktrace) ->
+ OwnReport = my_info(Class, Reason, StartF, Stacktrace),
LinkReport = linked_info(self()),
Rep = [OwnReport,LinkReport],
error_logger:error_report(crash_report, Rep).
-my_info(Class, Reason, []) ->
- my_info_1(Class, Reason);
-my_info(Class, Reason, StartF) ->
- [{initial_call, StartF}|my_info_1(Class, Reason)].
+my_info(Class, Reason, [], Stacktrace) ->
+ my_info_1(Class, Reason, Stacktrace);
+my_info(Class, Reason, StartF, Stacktrace) ->
+ [{initial_call, StartF}|
+ my_info_1(Class, Reason, Stacktrace)].
-my_info_1(Class, Reason) ->
+my_info_1(Class, Reason, Stacktrace) ->
[{pid, self()},
get_process_info(self(), registered_name),
- {error_info, {Class,Reason,erlang:get_stacktrace()}},
+ {error_info, {Class,Reason,Stacktrace}},
get_ancestors(self()),
- get_process_info(self(), messages),
+ get_process_info(self(), message_queue_len),
+ get_messages(self()),
get_process_info(self(), links),
get_cleaned_dictionary(self()),
get_process_info(self(), trap_exit),
@@ -538,12 +540,49 @@ get_ancestors(Pid) ->
{ancestors,[]}
end.
+%% The messages and the dictionary are possibly limited too much if
+%% some error handles output the messages or the dictionary using ~P
+%% or ~W with depth greater than the depth used here (the depth of
+%% control characters P and W takes precedence over the depth set by
+%% application variable error_logger_format_depth). However, it is
+%% assumed that all report handlers call proc_lib:format().
+get_messages(Pid) ->
+ Messages = get_process_messages(Pid),
+ {messages, error_logger:limit_term(Messages)}.
+
+get_process_messages(Pid) ->
+ Depth = error_logger:get_format_depth(),
+ case Pid =/= self() orelse Depth =:= unlimited of
+ true ->
+ {messages, Messages} = get_process_info(Pid, messages),
+ Messages;
+ false ->
+ %% If there are more messages than Depth, garbage
+ %% collection can sometimes be avoided by collecting just
+ %% enough messages for the crash report. It is assumed the
+ %% process is about to die anyway.
+ receive_messages(Depth)
+ end.
+
+receive_messages(0) -> [];
+receive_messages(N) ->
+ receive
+ M ->
+ [M|receive_messages(N - 1)]
+ after 0 ->
+ []
+ end.
+
get_cleaned_dictionary(Pid) ->
case get_process_info(Pid,dictionary) of
- {dictionary,Dict} -> {dictionary,clean_dict(Dict)};
+ {dictionary,Dict} -> {dictionary,cleaned_dict(Dict)};
_ -> {dictionary,[]}
end.
+cleaned_dict(Dict) ->
+ CleanDict = clean_dict(Dict),
+ error_logger:limit_term(CleanDict).
+
clean_dict([{'$ancestors',_}|Dict]) ->
clean_dict(Dict);
clean_dict([{'$initial_call',_}|Dict]) ->
@@ -581,20 +620,24 @@ make_neighbour_reports1([P|Ps]) ->
make_neighbour_reports1([]) ->
[].
+%% Do not include messages or process dictionary, even if
+%% error_logger_format_depth is unlimited.
make_neighbour_report(Pid) ->
[{pid, Pid},
get_process_info(Pid, registered_name),
get_initial_call(Pid),
get_process_info(Pid, current_function),
get_ancestors(Pid),
- get_process_info(Pid, messages),
+ get_process_info(Pid, message_queue_len),
+ %% get_messages(Pid),
get_process_info(Pid, links),
- get_cleaned_dictionary(Pid),
+ %% get_cleaned_dictionary(Pid),
get_process_info(Pid, trap_exit),
get_process_info(Pid, status),
get_process_info(Pid, heap_size),
get_process_info(Pid, stack_size),
- get_process_info(Pid, reductions)
+ get_process_info(Pid, reductions),
+ get_process_info(Pid, current_stacktrace)
].
get_initial_call(Pid) ->
@@ -721,24 +764,37 @@ format(CrashReport, Encoding) ->
format([OwnReport,LinkReport], Encoding, Depth) ->
Extra = {Encoding,Depth},
- OwnFormat = format_report(OwnReport, Extra),
- LinkFormat = format_report(LinkReport, Extra),
+ MyIndent = " ",
+ OwnFormat = format_report(OwnReport, MyIndent, Extra),
+ LinkFormat = format_link_report(LinkReport, MyIndent, Extra),
Str = io_lib:format(" crasher:~n~ts neighbours:~n~ts",
[OwnFormat, LinkFormat]),
lists:flatten(Str).
-format_report(Rep, Extra) when is_list(Rep) ->
- format_rep(Rep, Extra);
-format_report(Rep, {Enc,_}) ->
- io_lib:format("~"++modifier(Enc)++"p~n", [Rep]).
-
-format_rep([{initial_call,InitialCall}|Rep], {_Enc,Depth}=Extra) ->
- [format_mfa(InitialCall, Depth)|format_rep(Rep, Extra)];
-format_rep([{error_info,{Class,Reason,StackTrace}}|Rep], Extra) ->
- [format_exception(Class, Reason, StackTrace, Extra)|format_rep(Rep, Extra)];
-format_rep([{Tag,Data}|Rep], Extra) ->
- [format_tag(Tag, Data, Extra)|format_rep(Rep, Extra)];
-format_rep(_, _Extra) ->
+format_link_report([Link|Reps], Indent, Extra) ->
+ Rep = case Link of
+ {neighbour,Rep0} -> Rep0;
+ _ -> Link
+ end,
+ LinkIndent = [" ",Indent],
+ [Indent,"neighbour:\n",format_report(Rep, LinkIndent, Extra)|
+ format_link_report(Reps, Indent, Extra)];
+format_link_report([], _, _) ->
+ [].
+
+format_report(Rep, Indent, Extra) when is_list(Rep) ->
+ format_rep(Rep, Indent, Extra);
+format_report(Rep, Indent, {Enc,Depth}) ->
+ io_lib:format("~s~"++modifier(Enc)++"P~n", [Indent, Rep, Depth]).
+
+format_rep([{initial_call,InitialCall}|Rep], Indent, Extra) ->
+ [format_mfa(Indent, InitialCall, Extra)|format_rep(Rep, Indent, Extra)];
+format_rep([{error_info,{Class,Reason,StackTrace}}|Rep], Indent, Extra) ->
+ [format_exception(Class, Reason, StackTrace, Extra)|
+ format_rep(Rep, Indent, Extra)];
+format_rep([{Tag,Data}|Rep], Indent, Extra) ->
+ [format_tag(Indent, Tag, Data, Extra)|format_rep(Rep, Indent, Extra)];
+format_rep(_, _, _Extra) ->
[].
format_exception(Class, Reason, StackTrace, {Enc,_}=Extra) ->
@@ -749,14 +805,14 @@ format_exception(Class, Reason, StackTrace, {Enc,_}=Extra) ->
[EI, lib:format_exception(1+length(EI), Class, Reason,
StackTrace, StackFun, PF, Enc), "\n"].
-format_mfa({M,F,Args}=StartF, Depth) ->
+format_mfa(Indent, {M,F,Args}=StartF, Extra) ->
try
A = length(Args),
- [" initial call: ",atom_to_list(M),$:,atom_to_list(F),$/,
+ [Indent,"initial call: ",atom_to_list(M),$:,atom_to_list(F),$/,
integer_to_list(A),"\n"]
catch
error:_ ->
- format_tag(initial_call, StartF, Depth)
+ format_tag(Indent, initial_call, StartF, Extra)
end.
pp_fun({Enc,Depth}) ->
@@ -769,12 +825,12 @@ pp_fun({Enc,Depth}) ->
io_lib:format("~." ++ integer_to_list(I) ++ P, [Term|Tl])
end.
-format_tag(Tag, Data, {_Enc,Depth}) ->
+format_tag(Indent, Tag, Data, {_Enc,Depth}) ->
case Depth of
unlimited ->
- io_lib:format(" ~p: ~80.18p~n", [Tag, Data]);
+ io_lib:format("~s~p: ~80.18p~n", [Indent, Tag, Data]);
_ ->
- io_lib:format(" ~p: ~80.18P~n", [Tag, Data, Depth])
+ io_lib:format("~s~p: ~80.18P~n", [Indent, Tag, Data, Depth])
end.
modifier(latin1) -> "";
diff --git a/lib/stdlib/test/dummy_h.erl b/lib/stdlib/test/dummy_h.erl
index 45ccfee240..7a9eb11f98 100644
--- a/lib/stdlib/test/dummy_h.erl
+++ b/lib/stdlib/test/dummy_h.erl
@@ -43,6 +43,9 @@ handle_event(hibernate, _State) ->
{ok,[],hibernate};
handle_event(wakeup, _State) ->
{ok,[]};
+handle_event({From, handle_event}, _State) ->
+ From ! handled_event,
+ {ok,[]};
handle_event(Event, Parent) ->
Parent ! {dummy_h, Event},
{ok, Parent}.
@@ -75,6 +78,9 @@ handle_info(wake, _State) ->
{ok, []};
handle_info(gnurf, _State) ->
{ok, []};
+handle_info({From, handle_info}, _State) ->
+ From ! handled_info,
+ {ok, []};
handle_info(Info, Parent) ->
Parent ! {dummy_h, Info},
{ok, Parent}.
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 03cad2c093..02524679fa 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -65,7 +65,8 @@
maps/1,maps_type/1,maps_parallel_match/1,
otp_11851/1,otp_11879/1,otp_13230/1,
record_errors/1, otp_11879_cont/1,
- non_latin1_module/1, otp_14323/1]).
+ non_latin1_module/1, otp_14323/1,
+ get_stacktrace/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -85,7 +86,8 @@ all() ->
too_many_arguments, basic_errors, bin_syntax_errors, predef,
maps, maps_type, maps_parallel_match,
otp_11851, otp_11879, otp_13230,
- record_errors, otp_11879_cont, non_latin1_module, otp_14323].
+ record_errors, otp_11879_cont, non_latin1_module, otp_14323,
+ get_stacktrace].
groups() ->
[{unused_vars_warn, [],
@@ -3980,6 +3982,63 @@ otp_14323(Config) ->
[] = run(Config, Ts),
ok.
+get_stacktrace(Config) ->
+ Ts = [{old_catch,
+ <<"t1() ->
+ catch error(foo),
+ erlang:get_stacktrace().
+ ">>,
+ [],
+ {warnings,[{3,erl_lint,{get_stacktrace,after_old_catch}}]}},
+ {nowarn_get_stacktrace,
+ <<"t1() ->
+ catch error(foo),
+ erlang:get_stacktrace().
+ ">>,
+ [nowarn_get_stacktrace],
+ []},
+ {try_catch,
+ <<"t1(X) ->
+ try abs(X) of
+ _ ->
+ erlang:get_stacktrace()
+ catch
+ _:_ -> ok
+ end.
+
+ t2() ->
+ try error(foo)
+ catch _:_ -> ok
+ end,
+ erlang:get_stacktrace().
+
+ t3() ->
+ try error(foo)
+ catch _:_ ->
+ try error(bar)
+ catch _:_ ->
+ ok
+ end,
+ erlang:get_stacktrace()
+ end.
+
+ no_warning(X) ->
+ try
+ abs(X)
+ catch
+ _:_ ->
+ erlang:get_stacktrace()
+ end.
+ ">>,
+ [],
+ {warnings,[{4,erl_lint,{get_stacktrace,wrong_part_of_try}},
+ {13,erl_lint,{get_stacktrace,after_try}},
+ {22,erl_lint,{get_stacktrace,after_try}}]}}],
+
+ run(Config, Ts),
+ ok.
+
+
run(Config, Tests) ->
F = fun({N,P,Ws,E}, BadL) ->
case catch run_test(Config, P, Ws) of
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 749920f843..880b10117c 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -26,7 +26,7 @@
end_per_testcase/2]).
-export([start/1, add_handler/1, add_sup_handler/1,
delete_handler/1, swap_handler/1, swap_sup_handler/1,
- notify/1, sync_notify/1, call/1, info/1, hibernate/1,
+ notify/1, sync_notify/1, call/1, info/1, hibernate/1, auto_hibernate/1,
call_format_status/1, call_format_status_anon/1,
error_format_status/1, get_state/1, replace_state/1,
start_opt/1,
@@ -37,7 +37,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [start, {group, test_all}, hibernate,
+ [start, {group, test_all}, hibernate, auto_hibernate,
call_format_status, call_format_status_anon, error_format_status,
get_state, replace_state,
start_opt, {group, undef_callbacks}, undef_in_terminate].
@@ -306,6 +306,48 @@ hibernate(Config) when is_list(Config) ->
ok.
+auto_hibernate(Config) when is_list(Config) ->
+ HibernateAfterTimeout = 100,
+ State = {auto_hibernate_state},
+ {ok,Pid} = gen_event:start({local, auto_hibernate_handler}, [{hibernate_after, HibernateAfterTimeout}]),
+ %% After init test
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ ok = gen_event:add_handler(auto_hibernate_handler, dummy_h, [State]),
+ %% Get state test
+ [{dummy_h,false,State}] = sys:get_state(Pid),
+ is_in_erlang_hibernate(Pid),
+ %% Call test
+ {ok, hejhopp} = gen_event:call(auto_hibernate_handler, dummy_h, hejsan),
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% Event test
+ ok = gen_event:notify(auto_hibernate_handler, {self(), handle_event}),
+ receive
+ handled_event ->
+ ok
+ after 1000 ->
+ ct:fail(event)
+ end,
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% Info test
+ Pid ! {self(), handle_info},
+ receive
+ handled_info ->
+ ok
+ after 1000 ->
+ ct:fail(info)
+ end,
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ ok = gen_event:stop(auto_hibernate_handler),
+ ok.
+
is_in_erlang_hibernate(Pid) ->
receive after 1 -> ok end,
is_in_erlang_hibernate_1(200, Pid).
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index 7f98526d35..86cf58566b 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -44,7 +44,7 @@
-export([undef_in_handle_info/1, undef_in_terminate/1]).
--export([hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]).
+-export([hibernate/1,auto_hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]).
-export([enter_loop/1]).
@@ -68,7 +68,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[{group, start}, {group, abnormal}, shutdown,
- {group, sys}, hibernate, enter_loop, {group, undef_callbacks},
+ {group, sys}, hibernate, auto_hibernate, enter_loop, {group, undef_callbacks},
undef_in_handle_info, undef_in_terminate].
groups() ->
@@ -700,6 +700,43 @@ hibernate(Config) when is_list(Config) ->
process_flag(trap_exit, OldFl),
ok.
+%% Auto hibernation
+auto_hibernate(Config) when is_list(Config) ->
+ OldFl = process_flag(trap_exit, true),
+ HibernateAfterTimeout = 100,
+ State = {auto_hibernate_state},
+ {ok, Pid} = gen_fsm:start_link({local, my_test_name_auto_hibernate}, ?MODULE, {state_data, State}, [{hibernate_after, HibernateAfterTimeout}]),
+ %% After init test
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% Get state test
+ {_, State} = sys:get_state(my_test_name_auto_hibernate),
+ is_in_erlang_hibernate(Pid),
+ %% Sync send event test
+ 'alive!' = gen_fsm:sync_send_event(Pid,'alive?'),
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% Send event test
+ ok = gen_fsm:send_all_state_event(Pid,{'alive?', self()}),
+ wfor(yes),
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% Info test
+ Pid ! {self(), handle_info},
+ wfor({Pid, handled_info}),
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ stop_it(Pid),
+ receive
+ {'EXIT',Pid,normal} -> ok
+ end,
+ process_flag(trap_exit, OldFl),
+ ok.
+
is_in_erlang_hibernate(Pid) ->
receive after 1 -> ok end,
is_in_erlang_hibernate_1(200, Pid).
@@ -1151,6 +1188,8 @@ idle(badreturn, _From, _Data) ->
idle({timeout,Time}, From, _Data) ->
gen_fsm:send_event_after(Time, {timeout,Time}),
{next_state, timeout, From};
+idle('alive?', _From, Data) ->
+ {reply, 'alive!', idle, Data};
idle(_, _From, Data) ->
{reply, 'eh?', idle, Data}.
@@ -1226,6 +1265,9 @@ handle_info(hibernate_later, _SName, _State) ->
handle_info({call_undef_fun, {Mod, Fun}}, State, Data) ->
Mod:Fun(),
{next_state, State, Data};
+handle_info({From, handle_info}, SName, State) ->
+ From ! {self(), handled_info},
+ {next_state, SName, State};
handle_info(Info, _State, Data) ->
{stop, {unexpected,Info}, Data}.
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index 6f72034b09..2e9dc4d4fb 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -32,7 +32,7 @@
call_remote_n1/1, call_remote_n2/1, call_remote_n3/1, spec_init/1,
spec_init_local_registered_parent/1,
spec_init_global_registered_parent/1,
- otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1,
+ otp_5854/1, hibernate/1, auto_hibernate/1, otp_7669/1, call_format_status/1,
error_format_status/1, terminate_crash_format/1,
get_state/1, replace_state/1, call_with_huge_message_queue/1,
undef_handle_call/1, undef_handle_cast/1, undef_handle_info/1,
@@ -65,7 +65,7 @@ all() ->
call_remote3, call_remote_n1, call_remote_n2,
call_remote_n3, spec_init,
spec_init_local_registered_parent,
- spec_init_global_registered_parent, otp_5854, hibernate,
+ spec_init_global_registered_parent, otp_5854, hibernate, auto_hibernate,
otp_7669,
call_format_status, error_format_status, terminate_crash_format,
get_state, replace_state,
@@ -404,7 +404,7 @@ crash(Config) when is_list(Config) ->
end,
receive
{error_report,_,{Pid4,crash_report,[List4|_]}} ->
- {exit,crashed,_} = proplists:get_value(error_info, List4),
+ {exit,crashed,[{?MODULE, handle_call, 3, _}|_]} = proplists:get_value(error_info, List4),
Pid4 = proplists:get_value(pid, List4);
Other4 ->
io:format("Unexpected: ~p", [Other4]),
@@ -730,6 +730,58 @@ hibernate(Config) when is_list(Config) ->
process_flag(trap_exit, OldFl),
ok.
+auto_hibernate(Config) when is_list(Config) ->
+ OldFl = process_flag(trap_exit, true),
+ HibernateAfterTimeout = 100,
+ State = {auto_hibernate_state},
+ {ok, Pid} =
+ gen_server:start_link({local, my_test_name_auto_hibernate},
+ gen_server_SUITE, {state,State}, [{hibernate_after, HibernateAfterTimeout}]),
+ %% After init test
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% Get state test
+ State = sys:get_state(my_test_name_auto_hibernate),
+ is_in_erlang_hibernate(Pid),
+ %% Call test
+ ok = gen_server:call(my_test_name_auto_hibernate, started_p),
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% Cast test
+ ok = gen_server:cast(my_test_name_auto_hibernate, {self(),handle_cast}),
+ receive
+ {Pid, handled_cast} ->
+ ok
+ after 1000 ->
+ ct:fail(cast)
+ end,
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% Info test
+ Pid ! {self(),handle_info},
+ receive
+ {Pid, handled_info} ->
+ ok
+ after 1000 ->
+ ct:fail(info)
+ end,
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+
+ ok = gen_server:call(my_test_name_auto_hibernate, stop),
+ receive
+ {'EXIT', Pid, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(gen_server_did_not_die)
+ end,
+ process_flag(trap_exit, OldFl),
+ ok.
+
is_in_erlang_hibernate(Pid) ->
receive after 1 -> ok end,
is_in_erlang_hibernate_1(200, Pid).
@@ -747,6 +799,23 @@ is_in_erlang_hibernate_1(N, Pid) ->
is_in_erlang_hibernate_1(N-1, Pid)
end.
+is_not_in_erlang_hibernate(Pid) ->
+ receive after 1 -> ok end,
+ is_not_in_erlang_hibernate_1(200, Pid).
+
+is_not_in_erlang_hibernate_1(0, Pid) ->
+ io:format("~p\n", [erlang:process_info(Pid, current_function)]),
+ ct:fail(not_in_erlang_hibernate_3);
+is_not_in_erlang_hibernate_1(N, Pid) ->
+ {current_function,MFA} = erlang:process_info(Pid, current_function),
+ case MFA of
+ {erlang,hibernate,3} ->
+ receive after 10 -> ok end,
+ is_not_in_erlang_hibernate_1(N-1, Pid);
+ _ ->
+ ok
+ end.
+
%% --------------------------------------
%% Test gen_server:abcast and handle_cast.
%% Test all different return values from
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 05934b3953..5b9daecfd3 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -40,7 +40,7 @@ all() ->
shutdown, stop_and_reply, state_enter, event_order,
state_timeout, event_types, generic_timers, code_change,
{group, sys},
- hibernate, enter_loop, {group, undef_callbacks},
+ hibernate, auto_hibernate, enter_loop, {group, undef_callbacks},
undef_in_terminate].
groups() ->
@@ -1284,6 +1284,84 @@ hibernate(Config) ->
end,
ok = verify_empty_msgq().
+%% Auto-hibernation timeout
+auto_hibernate(Config) ->
+ OldFl = process_flag(trap_exit, true),
+ HibernateAfterTimeout = 100,
+
+ {ok,Pid} =
+ gen_statem:start_link(
+ ?MODULE, start_arg(Config, []), [{hibernate_after, HibernateAfterTimeout}]),
+ %% After init test
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% After info test
+ Pid ! {hping, self()},
+ receive
+ {Pid, hpong} ->
+ ok
+ after 1000 ->
+ ct:fail(info)
+ end,
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% After cast test
+ ok = gen_statem:cast(Pid, {hping, self()}),
+ receive
+ {Pid, hpong} ->
+ ok
+ after 1000 ->
+ ct:fail(cast)
+ end,
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% After call test
+ hpong = gen_statem:call(Pid, hping),
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% Timer test 1
+ TimerTimeout1 = 50,
+ ok = gen_statem:call(Pid, {arm_htimer, self(), TimerTimeout1}),
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(TimerTimeout1),
+ is_not_in_erlang_hibernate(Pid),
+ receive
+ {Pid, htimer_armed} ->
+ ok
+ after 1000 ->
+ ct:fail(timer1)
+ end,
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ %% Timer test 2
+ TimerTimeout2 = 150,
+ ok = gen_statem:call(Pid, {arm_htimer, self(), TimerTimeout2}),
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ receive
+ {Pid, htimer_armed} ->
+ ok
+ after 1000 ->
+ ct:fail(timer2)
+ end,
+ is_not_in_erlang_hibernate(Pid),
+ timer:sleep(HibernateAfterTimeout),
+ is_in_erlang_hibernate(Pid),
+ stop_it(Pid),
+ process_flag(trap_exit, OldFl),
+ receive
+ {'EXIT',Pid,normal} -> ok
+ after 5000 ->
+ ct:fail(gen_statem_did_not_die)
+ end,
+ ok = verify_empty_msgq().
+
is_in_erlang_hibernate(Pid) ->
receive after 1 -> ok end,
is_in_erlang_hibernate_1(200, Pid).
@@ -1704,6 +1782,19 @@ terminate(_Reason, _State, _Data) ->
%% State functions
+idle(info, {hping,Pid}, _Data) ->
+ Pid ! {self(), hpong},
+ keep_state_and_data;
+idle(cast, {hping,Pid}, Data) ->
+ Pid ! {self(), hpong},
+ {keep_state, Data};
+idle({call, From}, hping, _Data) ->
+ {keep_state_and_data, [{reply, From, hpong}]};
+idle({call, From}, {arm_htimer, Pid, Timeout}, _Data) ->
+ {keep_state_and_data, [{reply, From, ok}, {timeout, Timeout, {arm_htimer, Pid}}]};
+idle(timeout, {arm_htimer, Pid}, _Data) ->
+ Pid ! {self(), htimer_armed},
+ keep_state_and_data;
idle(cast, {connect,Pid}, Data) ->
Pid ! accept,
{next_state,wfor_conf,Data,infinity}; % NoOp timeout just to test API
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index ef3f0be5d7..e2c73371cd 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -31,7 +31,7 @@
otp_10836/1, io_lib_width_too_small/1,
io_with_huge_message_queue/1, format_string/1,
maps/1, coverage/1, otp_14178_unicode_atoms/1, otp_14175/1,
- otp_14285/1]).
+ otp_14285/1, limit_term/1]).
-export([pretty/2]).
@@ -63,7 +63,7 @@ all() ->
io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836,
io_lib_width_too_small, io_with_huge_message_queue,
format_string, maps, coverage, otp_14178_unicode_atoms, otp_14175,
- otp_14285].
+ otp_14285, limit_term].
%% Error cases for output.
error_1(Config) when is_list(Config) ->
@@ -2373,3 +2373,58 @@ otp_14285(_Config) ->
latin1_fmt(Fmt, Args) ->
L = fmt(Fmt, Args),
true = lists:all(fun is_latin1/1, L).
+
+limit_term(_Config) ->
+ {_, 2} = limt([a,b,c], 2),
+ {_, 2} = limt([a,b,c], 3),
+ {_, 2} = limt([a,b|c], 2),
+ {_, 2} = limt([a,b|c], 3),
+ {_, 2} = limt({a,b,c,[d,e]}, 2),
+ {_, 2} = limt({a,b,c,[d,e]}, 3),
+ {_, 2} = limt({a,b,c,[d,e]}, 4),
+ {_, 1} = limt(<<"foo">>, 18),
+ ok = blimt(<<"123456789012345678901234567890">>),
+ {_, 1} = limt(<<7:3>>, 2),
+ {_, 1} = limt(<<7:21>>, 2),
+ {_, 1} = limt([], 2),
+ {_, 1} = limt({}, 2),
+ {_, 1} = limt(#{}, 2),
+ {_, 1} = limt(#{[] => {}}, 2),
+ {_, 1} = limt(#{[] => {}}, 3),
+ T = #{[] => {},[a] => [b]},
+ {_, 1} = limt(T, 2),
+ {_, 1} = limt(T, 3),
+ {_, 1} = limt(T, 4),
+ ok.
+
+blimt(Binary) ->
+ blimt(Binary, byte_size(Binary)).
+
+blimt(_B, 1) -> ok;
+blimt(B, D) ->
+ {_, 1} = limt(B, D),
+ blimt(B, D - 1).
+
+limt(Term, Depth) when is_integer(Depth) ->
+ T1 = io_lib:limit_term(Term, Depth),
+ S = form(Term, Depth),
+ S1 = form(T1, Depth),
+ OK1 = S1 =:= S,
+
+ T2 = io_lib:limit_term(Term, Depth+1),
+ S2 = form(T2, Depth),
+ OK2 = S2 =:= S,
+
+ T3 = io_lib:limit_term(Term, Depth-1),
+ S3 = form(T3, Depth),
+ OK3 = S3 =/= S,
+
+ R = case {OK1, OK2, OK3} of
+ {true, true, true} -> 2;
+ {true, true, false} -> 1;
+ _ -> 0
+ end,
+ {{S, S1, S2}, R}.
+
+form(Term, Depth) ->
+ lists:flatten(io_lib:format("~W", [Term, Depth])).
diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml
index 9c8ce148e9..6e66a957ab 100644
--- a/lib/tools/doc/src/lcnt.xml
+++ b/lib/tools/doc/src/lcnt.xml
@@ -38,7 +38,7 @@
<modulesummary>A runtime system Lock Profiling tool.</modulesummary>
<description>
<p>The <c>lcnt</c> module is used to profile the internal ethread locks in the
- Erlang Runtime System. With <c>lcnt</c> enabled, Internal counters in the
+ Erlang Runtime System. With <c>lcnt</c> enabled, internal counters in the
runtime system are updated each time a lock is taken. The counters stores
information about the number of acquisition tries and the number of collisions
that has occurred during the acquisition tries. The counters also record the
@@ -47,17 +47,17 @@
<p>
The data produced by the lock counters will give an estimate on how well
the runtime system will behave from a parallelizable view point for the
- scenarios tested. This tool was mainly developed to help erlang runtime
+ scenarios tested. This tool was mainly developed to help Erlang runtime
developers iron out potential and generic bottlenecks.
</p>
<p>Locks in the emulator are named after what type of resource they protect and where
in the emulator they are initialized, those are lock 'classes'. Most of those
locks are also instantiated several times, and given unique identifiers, to increase
locking granularity. Typically an instantiated lock protects a disjunct set of
- the resource, i.e ets-tables, processes or ports. In other cases it protects a
- specific range of a resource, e.g. <c>pix_lock</c> which protects index to process
+ the resource, for example ets tables, processes or ports. In other cases it protects a
+ specific range of a resource, for example <c>pix_lock</c> which protects index to process
mappings, and is given a unique number within the class. A unique lock in <c>lcnt</c>
- is referenced by a name (class) and an identifier, <c>{Name, Id}</c>.
+ is referenced by a name (class) and an identifier: <c>{Name, Id}</c>.
</p>
<p>Some locks in the system are static and protects global resources, for example
<c>bif_timers</c> and the <c>run_queue</c> locks. Other locks are dynamic and not
diff --git a/lib/tools/doc/src/lcnt_chapter.xml b/lib/tools/doc/src/lcnt_chapter.xml
index 6cfdb5cf1b..1981d66117 100644
--- a/lib/tools/doc/src/lcnt_chapter.xml
+++ b/lib/tools/doc/src/lcnt_chapter.xml
@@ -42,42 +42,32 @@
completed its access to the resource and unlocked it. The <c>lcnt</c> tool measures these lock conflicts.
</p>
<p>
- Locks has an inherent cost in execution time and memory space. It takes time initialize, destroy, aquiring or releasing locks. To decrease lock contention it
+ Locks have an inherent cost in execution time and memory space. It takes time initialize, destroy, aquiring or releasing locks. To decrease lock contention it
some times necessary to use finer grained locking strategies. This will usually also increase the locking overhead and hence there is a tradeoff
between lock contention and overhead. In general, lock contention increases with the number of threads running concurrently. The <c>lcnt</c> tool does not measure locking overhead.
</p>
<section>
<title> Enabling lock-counting </title>
- <p>For investigation of locks in the emulator we use an internal tool called <c>lcnt</c> (short for lock-count). The VM needs to be compiled with this option enabled. To enable this, use:</p>
+ <p>For investigation of locks in the emulator we use an internal tool called <c>lcnt</c> (short for lock-count). The VM needs to be compiled with this option enabled.
+ To compile a lock-counting VM along with a normal VM, use:</p>
<pre>
cd $ERL_TOP
-./configure --enable-lock-counter
- </pre>
-
- <p>
- Another way to enable this alongside a normal VM is to compile it at emulator directory level, much like a debug build. To compile it this way do the following,
- </p>
- <pre>
-cd $ERL_TOP/erts/emulator
-make lcnt FLAVOR=smp
- </pre>
- <p> and then starting Erlang with,</p>
+./configure --enable-lock-counter</pre>
+ <p>Start the lock-counting VM like this:</p>
<pre>
-$ERL_TOP/bin/cerl -lcnt
- </pre>
- <p>To verify that you lock-counting enabled check that <c>[lock-counting]</c> appears in the status text when the VM is started.</p>
+$ERL_TOP/bin/erl -emu_type lcnt</pre>
+ <p>To verify that lock counting is enabled check that <c>[lock-counting]</c> appears in the status text when the VM is started.</p>
<pre>
-Erlang R13B03 (erts-5.7.4) [source] [64-bit] [smp:8:8] [rq:8] [async-threads:0] [hipe]
- [kernel-poll:false] [lock-counting]
- </pre>
+Erlang/OTP 20 [erts-9.0] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe]
+ [kernel-poll:false] [lock-counting]</pre>
</section>
<section>
<title>Getting started</title>
<p>Once you have a lock counting enabled VM the module <c>lcnt</c> can be used. The module is intended to be used from the current running nodes shell. To access remote nodes use <c>lcnt:clear(Node)</c> and <c>lcnt:collect(Node)</c>. </p>
<p>All locks are continuously monitored and its statistics updated. Use <c>lcnt:clear/0</c> to initially clear all counters before running any specific tests. This command will also reset the duration timer internally.</p>
- <p>To retrieve lock statistics information use, <c>lcnt:collect/0,1</c>. The collect operation will start a <c>lcnt</c> server if it not already started. All collected data will be built into an erlang term and uploaded to the server and a duration time will also be uploaded. This duration is the time between <c>lcnt:clear/0,1</c> and <c>lcnt:collect/0,1</c>.</p>
+ <p>To retrieve lock statistics information, use <c>lcnt:collect/0,1</c>. The collect operation will start a <c>lcnt</c> server if it not already started. All collected data will be built into an Erlang term and uploaded to the server and a duration time will also be uploaded. This duration is the time between <c>lcnt:clear/0,1</c> and <c>lcnt:collect/0,1</c>.</p>
<p>Once the data is collected to the server it can be filtered, sorted and printed in many different ways.</p>
<p>See the <seealso marker="lcnt">reference manual</seealso> for a description of each function.</p>
diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index d39a5deeab..2e48b11740 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -29,7 +29,8 @@
-export([t_load/1,
t_conflicts/1,
t_locations/1,
- t_swap_keys/1]).
+ t_swap_keys/1,
+ smoke_lcnt/1]).
init_per_testcase(_Case, Config) ->
Config.
@@ -43,7 +44,8 @@ suite() ->
{timetrap,{minutes,4}}].
all() ->
- [t_load, t_conflicts, t_locations, t_swap_keys].
+ [t_load, t_conflicts, t_locations, t_swap_keys,
+ smoke_lcnt].
%%----------------------------------------------------------------------
%% Tests
@@ -146,3 +148,82 @@ t_swap_keys_file([File|Files]) ->
ok = lcnt:conflicts(),
ok = lcnt:stop(),
t_swap_keys_file(Files).
+
+%% Simple smoke test of actual lock-counting, if running on
+%% a run-time with lock-counting enabled.
+
+smoke_lcnt(Config) ->
+ case erlang:system_info(build_type) of
+ lcnt ->
+ do_smoke_lcnt(Config);
+ _ ->
+ {skip,"Lock counting is not enabled"}
+ end.
+
+do_smoke_lcnt(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SaveFile = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)),
+ {Time,ok} = timer:tc(fun() -> lcnt:apply(fun() -> big_bang(200) end) end),
+ io:format("~p ms\n", [Time]),
+ ok = lcnt:conflicts(),
+ ok = lcnt:save(SaveFile),
+ ok = lcnt:load(SaveFile),
+ ok = lcnt:conflicts(),
+ lcnt:stop().
+
+
+%%%
+%%% A slightly modified version of Rickard Green's Big Bang benchmark.
+%%%
+
+big_bang(N) when is_integer(N) ->
+ Procs = spawn_procs(N),
+ RMsgs = lists:map(fun (P) -> {done, P} end, Procs),
+ send_procs(Procs, {procs, Procs, self()}),
+ receive_msgs(RMsgs),
+ lists:foreach(fun (P) -> exit(P, normal) end, Procs).
+
+pinger([], [], true) ->
+ receive
+ {procs, Procs, ReportTo} ->
+ pinger(Procs, [], ReportTo)
+ end;
+pinger([], [], false) ->
+ receive {ping, From} -> From ! {pong, self()} end,
+ pinger([],[],false);
+pinger([], [], ReportTo) ->
+ ReportTo ! {done, self()},
+ pinger([],[],false);
+pinger([],[Po|Pos] = Pongers, ReportTo) ->
+ receive
+ {ping, From} ->
+ From ! {pong, self()},
+ pinger([], Pongers, ReportTo);
+ {pong, Po} ->
+ pinger([], Pos, ReportTo)
+ end;
+pinger([Pi|Pis], Pongers, ReportTo) ->
+ receive {ping, From} -> From ! {pong, self()}
+ after 0 -> ok
+ end,
+ Pi ! {ping, self()},
+ pinger(Pis, [Pi|Pongers], ReportTo).
+
+spawn_procs(N) when N =< 0 ->
+ [];
+spawn_procs(N) ->
+ [spawn_link(fun () -> pinger([], [], true) end) | spawn_procs(N-1)].
+
+send_procs([], Msg) ->
+ Msg;
+send_procs([P|Ps], Msg) ->
+ P ! Msg,
+ send_procs(Ps, Msg).
+
+receive_msgs([]) ->
+ ok;
+receive_msgs([M|Ms]) ->
+ receive
+ M ->
+ receive_msgs(Ms)
+ end.
diff --git a/lib/wx/c_src/egl_impl.h b/lib/wx/c_src/egl_impl.h
index 719b4926db..7ecd484de5 100644
--- a/lib/wx/c_src/egl_impl.h
+++ b/lib/wx/c_src/egl_impl.h
@@ -112,7 +112,7 @@ typedef long int int32_t;
typedef long long int int64_t;
typedef unsigned long long int uint64_t;
#elif defined(WIN32) && defined(_MSC_VER)
-typedef long int int32_t;
+typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#elif defined(WIN32) && defined(__GNUC__)
diff --git a/make/otp.mk.in b/make/otp.mk.in
index cd4572d81b..83bab7065d 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -49,6 +49,7 @@ DEFAULT_TARGETS = opt debug release release_docs clean docs
DEFAULT_FLAVOR=@DEFAULT_FLAVOR@
FLAVORS=@FLAVORS@
+TYPES=@TYPES@
# Slash separated list of return values from $(origin VAR)
# that are untrusted - set default in this file instead.
diff --git a/make/otp_subdir.mk b/make/otp_subdir.mk
index fa6470ddd7..5734970298 100644
--- a/make/otp_subdir.mk
+++ b/make/otp_subdir.mk
@@ -19,13 +19,13 @@
#
# Make include file for otp
-.PHONY: debug opt release docs release_docs tests release_tests \
+.PHONY: debug opt lcnt release docs release_docs tests release_tests \
clean depend valgrind static_lib
#
# Targets that don't affect documentation directories
#
-opt debug release docs release_docs tests release_tests clean depend valgrind static_lib:
+opt debug lcnt release docs release_docs tests release_tests clean depend valgrind static_lib:
@set -e ; \
app_pwd=`pwd` ; \
if test -f vsn.mk; then \
diff --git a/system/doc/programming_examples/bit_syntax.xml b/system/doc/programming_examples/bit_syntax.xml
index 0af295b7b7..98ad2808cf 100644
--- a/system/doc/programming_examples/bit_syntax.xml
+++ b/system/doc/programming_examples/bit_syntax.xml
@@ -32,6 +32,8 @@
<section>
<title>Introduction</title>
+ <p>The complete specification for the bit syntax appears in the
+ <seealso marker="doc/reference_manual:expressions#bit_syntax">Reference Manual</seealso>.</p>
<p>In Erlang, a Bin is used for constructing binaries and matching
binary patterns. A Bin is written with the following syntax:</p>
<code type="none"><![CDATA[
@@ -45,7 +47,7 @@ Bin = <<E1, E2, ... En>>]]></code>
<<E1, E2, ... En>> = Bin ]]></code>
<p>Here, <c>Bin</c> is bound and the elements are bound or
unbound, as in any match.</p>
- <p>Since Erlang R12B, a Bin does not need to consist of a whole number of bytes.</p>
+ <p>A Bin does not need to consist of a whole number of bytes.</p>
<p>A <em>bitstring</em> is a sequence of zero or more bits, where
the number of bits does not need to be divisible by 8. If the number
@@ -165,8 +167,9 @@ end.]]></code>
separated by hyphens.</p>
<taglist>
<tag>Type</tag>
- <item>The type can be <c>integer</c>, <c>float</c>, or
- <c>binary</c>.</item>
+ <item>The most commonly used types are <c>integer</c>, <c>float</c>, and <c>binary</c>.
+ See <seealso marker="doc/reference_manual:expressions#bit_syntax">Bit Syntax Expressions in the Reference Manual</seealso> for a complete description.
+</item>
<tag>Signedness</tag>
<item>The signedness specification can be either <c>signed</c>
or <c>unsigned</c>. Notice that signedness only matters for
@@ -181,7 +184,7 @@ end.]]></code>
<item>The unit size is given as <c>unit:IntegerLiteral</c>.
The allowed range is 1-256. It is multiplied by
the <c>Size</c> specifier to give the effective size of
- the segment. Since Erlang R12B, the unit size specifies the alignment
+ the segment. The unit size specifies the alignment
for binary segments without size.</item>
</taglist>
<p><em>Example:</em></p>
@@ -319,21 +322,15 @@ foo(<<A:8,Rest/bitstring>>) ->]]></code>
<section>
<title>Appending to a Binary</title>
- <p>Since Erlang R12B, the following function for creating a binary out of
- a list of triples of integers is efficient:</p>
+ <p>Appending to a binary in an efficient way can be done as follows:</p>
<code type="none"><![CDATA[
triples_to_bin(T) ->
triples_to_bin(T, <<>>).
triples_to_bin([{X,Y,Z} | T], Acc) ->
- triples_to_bin(T, <<Acc/binary,X:32,Y:32,Z:32>>); % inefficient before R12B
+ triples_to_bin(T, <<Acc/binary,X:32,Y:32,Z:32>>);
triples_to_bin([], Acc) ->
Acc.]]></code>
- <p>In previous releases, this function was highly inefficient, because
- the binary constructed so far (<c>Acc</c>) was copied in each recursion step.
- That is no longer the case. For more information, see
- <seealso marker="doc/efficiency_guide:introduction">
- Efficiency Guide</seealso>.</p>
</section>
</chapter>