aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml24
-rw-r--r--HOWTO/INSTALL-CROSS.md21
-rw-r--r--HOWTO/INSTALL-WIN32.md22
-rw-r--r--HOWTO/INSTALL.md128
-rw-r--r--OTP_VERSION2
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin45428 -> 45492 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin54676 -> 54744 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_file_h.beambin4212 -> 4228 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sys.beambin8400 -> 8416 bytes
-rw-r--r--erts/configure.in3
-rw-r--r--erts/doc/src/erl_dist_protocol.xml2
-rw-r--r--erts/doc/src/erlang.xml3
-rw-r--r--erts/doc/src/notes.xml672
-rw-r--r--erts/emulator/Makefile.in3
-rw-r--r--erts/emulator/beam/atom.c3
-rw-r--r--erts/emulator/beam/beam_bif_load.c7
-rw-r--r--erts/emulator/beam/beam_bp.c3
-rw-r--r--erts/emulator/beam/beam_emu.c143
-rw-r--r--erts/emulator/beam/beam_load.h2
-rw-r--r--erts/emulator/beam/bif.c6
-rw-r--r--erts/emulator/beam/bif.tab5
-rw-r--r--erts/emulator/beam/big.c2
-rw-r--r--erts/emulator/beam/big.h2
-rw-r--r--erts/emulator/beam/code_ix.c3
-rw-r--r--erts/emulator/beam/dist.c3
-rw-r--r--erts/emulator/beam/erl_alloc.c3
-rw-r--r--erts/emulator/beam/erl_alloc.h13
-rw-r--r--erts/emulator/beam/erl_alloc.types7
-rw-r--r--erts/emulator/beam/erl_alloc_util.c57
-rw-r--r--erts/emulator/beam/erl_alloc_util.h7
-rw-r--r--erts/emulator/beam/erl_async.c6
-rw-r--r--erts/emulator/beam/erl_bif_info.c489
-rw-r--r--erts/emulator/beam/erl_bif_unique.c6
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c5
-rw-r--r--erts/emulator/beam/erl_cpu_topology.h2
-rw-r--r--erts/emulator/beam/erl_db.c63
-rw-r--r--erts/emulator/beam/erl_db.h5
-rw-r--r--erts/emulator/beam/erl_db_hash.c24
-rw-r--r--erts/emulator/beam/erl_db_hash.h4
-rw-r--r--erts/emulator/beam/erl_dirty_bif.tab5
-rw-r--r--erts/emulator/beam/erl_drv_thread.c29
-rw-r--r--erts/emulator/beam/erl_fun.c3
-rw-r--r--erts/emulator/beam/erl_gc.c3
-rw-r--r--erts/emulator/beam/erl_init.c11
-rw-r--r--erts/emulator/beam/erl_instrument.c6
-rw-r--r--erts/emulator/beam/erl_lock_check.c5
-rw-r--r--erts/emulator/beam/erl_lock_count.c963
-rw-r--r--erts/emulator/beam/erl_lock_count.h998
-rw-r--r--erts/emulator/beam/erl_lock_flags.c46
-rw-r--r--erts/emulator/beam/erl_lock_flags.h74
-rw-r--r--erts/emulator/beam/erl_msacc.c6
-rw-r--r--erts/emulator/beam/erl_mtrace.c6
-rw-r--r--erts/emulator/beam/erl_nif.c3
-rw-r--r--erts/emulator/beam/erl_node_tables.c44
-rw-r--r--erts/emulator/beam/erl_node_tables.h4
-rw-r--r--erts/emulator/beam/erl_port.h2
-rw-r--r--erts/emulator/beam/erl_port_task.c39
-rw-r--r--erts/emulator/beam/erl_port_task.h10
-rw-r--r--erts/emulator/beam/erl_process.c99
-rw-r--r--erts/emulator/beam/erl_process_lock.c181
-rw-r--r--erts/emulator/beam/erl_process_lock.h180
-rw-r--r--erts/emulator/beam/erl_ptab.c3
-rw-r--r--erts/emulator/beam/erl_sched_spec_pre_alloc.c2
-rw-r--r--erts/emulator/beam/erl_smp.h135
-rw-r--r--erts/emulator/beam/erl_thr_progress.c33
-rw-r--r--erts/emulator/beam/erl_threads.h350
-rw-r--r--erts/emulator/beam/erl_time_sup.c61
-rw-r--r--erts/emulator/beam/erl_trace.c10
-rw-r--r--erts/emulator/beam/erl_utils.h1
-rw-r--r--erts/emulator/beam/export.c3
-rw-r--r--erts/emulator/beam/global.h3
-rw-r--r--erts/emulator/beam/io.c223
-rw-r--r--erts/emulator/beam/module.c3
-rw-r--r--erts/emulator/beam/ops.tab104
-rw-r--r--erts/emulator/beam/register.c3
-rw-r--r--erts/emulator/beam/safe_hash.c23
-rw-r--r--erts/emulator/beam/safe_hash.h7
-rw-r--r--erts/emulator/beam/sys.h10
-rw-r--r--erts/emulator/beam/utils.c1
-rw-r--r--erts/emulator/drivers/common/efile_drv.c3
-rw-r--r--erts/emulator/drivers/common/inet_drv.c6
-rw-r--r--erts/emulator/hipe/hipe_bif0.c3
-rw-r--r--erts/emulator/sys/common/erl_check_io.c36
-rw-r--r--erts/emulator/sys/common/erl_check_io.h9
-rw-r--r--erts/emulator/sys/common/erl_mmap.c9
-rw-r--r--erts/emulator/sys/common/erl_mseg.c6
-rw-r--r--erts/emulator/sys/common/erl_poll.c38
-rw-r--r--erts/emulator/sys/common/erl_poll.h4
-rw-r--r--erts/emulator/sys/unix/sys.c15
-rw-r--r--erts/emulator/sys/unix/sys_time.c4
-rw-r--r--erts/emulator/sys/win32/erl_poll.c10
-rw-r--r--erts/emulator/sys/win32/sys.c15
-rw-r--r--erts/emulator/sys/win32/sys_env.c3
-rw-r--r--erts/emulator/sys/win32/sys_time.c4
-rw-r--r--erts/emulator/test/Makefile1
-rw-r--r--erts/emulator/test/a_SUITE.erl2
-rw-r--r--erts/emulator/test/after_SUITE.erl2
-rw-r--r--erts/emulator/test/bs_construct_SUITE.erl2
-rw-r--r--erts/emulator/test/busy_port_SUITE.erl2
-rw-r--r--erts/emulator/test/code_SUITE.erl2
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c2
-rw-r--r--erts/emulator/test/driver_SUITE.erl2
-rw-r--r--erts/emulator/test/efile_SUITE.erl2
-rw-r--r--erts/emulator/test/evil_SUITE.erl2
-rw-r--r--erts/emulator/test/exception_SUITE.erl2
-rw-r--r--erts/emulator/test/fun_SUITE.erl14
-rw-r--r--erts/emulator/test/gc_SUITE.erl2
-rw-r--r--erts/emulator/test/hipe_SUITE.erl2
-rw-r--r--erts/emulator/test/lcnt_SUITE.erl156
-rw-r--r--erts/emulator/test/long_timers_test.erl2
-rw-r--r--erts/emulator/test/lttng_SUITE.erl2
-rw-r--r--erts/emulator/test/message_queue_data_SUITE.erl2
-rw-r--r--erts/emulator/test/monitor_SUITE.erl2
-rw-r--r--erts/emulator/test/mtx_SUITE.erl2
-rw-r--r--erts/emulator/test/nested_SUITE.erl2
-rw-r--r--erts/emulator/test/port_SUITE.erl40
-rw-r--r--erts/emulator/test/port_SUITE_data/echo_drv.c31
-rw-r--r--erts/emulator/test/port_trace_SUITE.erl2
-rw-r--r--erts/emulator/test/process_SUITE.erl2
-rw-r--r--erts/emulator/test/receive_SUITE.erl2
-rw-r--r--erts/emulator/test/register_SUITE.erl9
-rw-r--r--erts/emulator/test/signal_SUITE.erl2
-rw-r--r--erts/emulator/test/system_profile_SUITE.erl2
-rw-r--r--erts/emulator/test/timer_bif_SUITE.erl2
-rw-r--r--erts/emulator/test/trace_SUITE.erl2
-rw-r--r--erts/emulator/test/trace_bif_SUITE.erl2
-rw-r--r--erts/emulator/test/trace_local_SUITE.erl2
-rw-r--r--erts/emulator/test/trace_meta_SUITE.erl2
-rw-r--r--erts/emulator/test/trace_nif_SUITE.erl2
-rw-r--r--erts/emulator/test/trace_port_SUITE.erl2
-rw-r--r--erts/emulator/test/tracer_SUITE.erl2
-rw-r--r--erts/emulator/test/unique_SUITE.erl2
-rwxr-xr-xerts/emulator/utils/beam_makeops2
-rw-r--r--erts/etc/common/Makefile.in3
-rw-r--r--erts/etc/common/escript.c21
-rw-r--r--erts/etc/unix/Makefile2
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin54832 -> 54872 bytes
-rw-r--r--erts/preloaded/ebin/erl_tracer.beambin2184 -> 2220 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin106168 -> 106204 bytes
-rw-r--r--erts/preloaded/ebin/erts_code_purger.beambin11380 -> 11412 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_code_checker.beambin2104 -> 2136 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin11064 -> 11100 bytes
-rw-r--r--erts/preloaded/ebin/erts_literal_area_collector.beambin3288 -> 3316 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin50300 -> 50344 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1424 -> 1460 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1496 -> 1540 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin43980 -> 44024 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin76036 -> 76084 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin22988 -> 23032 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin14280 -> 14316 bytes
-rw-r--r--erts/start_scripts/Makefile2
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/asn1/doc/src/notes.xml51
-rw-r--r--lib/asn1/doc/src/ref_man.xml2
-rw-r--r--lib/asn1/src/asn1ct_gen.erl48
-rw-r--r--lib/asn1/test/asn1_SUITE.erl12
-rw-r--r--lib/asn1/test/asn1_SUITE_data/EnumN2N.asn122
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/Makefile2
-rw-r--r--lib/common_test/doc/src/basics_chapter.xml2
-rw-r--r--lib/common_test/doc/src/ct_netconfc.xml2
-rw-r--r--lib/common_test/doc/src/ct_ssh.xml2
-rw-r--r--lib/common_test/doc/src/notes.xml94
-rw-r--r--lib/common_test/src/ct.erl6
-rw-r--r--lib/common_test/src/ct_config.erl6
-rw-r--r--lib/common_test/src/ct_conn_log_h.erl16
-rw-r--r--lib/common_test/src/ct_event.erl4
-rw-r--r--lib/common_test/src/ct_framework.erl42
-rw-r--r--lib/common_test/src/ct_ftp.erl26
-rw-r--r--lib/common_test/src/ct_gen_conn.erl10
-rw-r--r--lib/common_test/src/ct_groups.erl4
-rw-r--r--lib/common_test/src/ct_hooks.erl6
-rw-r--r--lib/common_test/src/ct_logs.erl50
-rw-r--r--lib/common_test/src/ct_make.erl54
-rw-r--r--lib/common_test/src/ct_master.erl26
-rw-r--r--lib/common_test/src/ct_master_event.erl6
-rw-r--r--lib/common_test/src/ct_master_logs.erl12
-rw-r--r--lib/common_test/src/ct_master_status.erl4
-rw-r--r--lib/common_test/src/ct_netconfc.erl2
-rw-r--r--lib/common_test/src/ct_property_test.erl6
-rw-r--r--lib/common_test/src/ct_release_test.erl19
-rw-r--r--lib/common_test/src/ct_repeat.erl10
-rw-r--r--lib/common_test/src/ct_run.erl68
-rw-r--r--lib/common_test/src/ct_ssh.erl78
-rw-r--r--lib/common_test/src/ct_telnet.erl30
-rw-r--r--lib/common_test/src/ct_telnet_client.erl4
-rw-r--r--lib/common_test/src/ct_testspec.erl4
-rw-r--r--lib/common_test/src/ct_util.erl14
-rw-r--r--lib/common_test/src/ct_webtool.erl34
-rw-r--r--lib/common_test/src/cth_conn_log.erl2
-rw-r--r--lib/common_test/src/cth_log_redirect.erl8
-rw-r--r--lib/common_test/src/cth_surefire.erl4
-rw-r--r--lib/common_test/src/test_server.erl34
-rw-r--r--lib/common_test/src/test_server_ctrl.erl157
-rw-r--r--lib/common_test/src/test_server_gl.erl12
-rw-r--r--lib/common_test/src/test_server_io.erl8
-rw-r--r--lib/common_test/src/test_server_node.erl18
-rw-r--r--lib/common_test/src/test_server_sup.erl26
-rw-r--r--lib/common_test/src/unix_telnet.erl10
-rw-r--r--lib/common_test/src/vts.erl6
-rw-r--r--lib/common_test/test/Makefile3
-rw-r--r--lib/common_test/test/ct_config_SUITE.erl10
-rw-r--r--lib/common_test/test/ct_keep_logs_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE.erl2
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl2
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl2
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/ns.erl2
-rw-r--r--lib/common_test/test/ct_surefire_SUITE.erl8
-rw-r--r--lib/common_test/test/ct_test_support.erl117
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE.erl8
-rw-r--r--lib/common_test/test/ct_unicode_SUITE.erl218
-rw-r--r--lib/common_test/test/ct_unicode_SUITE_data/unicode_atoms_SUITE.erl98
-rw-r--r--lib/common_test/test/ct_verbosity_SUITE.erl6
-rw-r--r--lib/common_test/test/erl2html2_SUITE.erl6
-rw-r--r--lib/common_test/test/test_server_test_lib.erl4
-rw-r--r--lib/common_test/test_server/ts_install.erl4
-rw-r--r--lib/common_test/test_server/ts_make.erl4
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/notes.xml174
-rw-r--r--lib/compiler/src/Makefile2
-rw-r--r--lib/compiler/src/compiler.app.src2
-rwxr-xr-xlib/compiler/src/genop.tab2
-rw-r--r--lib/compiler/src/sys_core_fold.erl2
-rw-r--r--lib/compiler/src/v3_core.erl45
-rw-r--r--lib/compiler/src/v3_kernel_pp.erl2
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl2
-rw-r--r--lib/compiler/test/bs_construct_SUITE.erl2
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl2
-rw-r--r--lib/compiler/test/compile_SUITE.erl35
-rw-r--r--lib/compiler/test/warnings_SUITE.erl2
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/crypto/doc/src/notes.xml128
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/doc/src/notes.xml36
-rw-r--r--lib/debugger/vsn.mk2
-rw-r--r--lib/dialyzer/doc/src/notes.xml69
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl86
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/doc/src/notes.xml70
-rw-r--r--lib/diameter/src/base/diameter_lib.erl2
-rw-r--r--lib/diameter/src/base/diameter_reg.erl2
-rw-r--r--lib/diameter/test/diameter_relay_SUITE.erl2
-rw-r--r--lib/edoc/doc/src/notes.xml24
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/erl_docgen/doc/src/notes.xml39
-rw-r--r--lib/erl_interface/doc/src/notes.xml39
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/eunit/doc/src/notes.xml15
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/hipe/doc/src/notes.xml95
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/inets/doc/src/http_uri.xml2
-rw-r--r--lib/inets/doc/src/httpc.xml2
-rw-r--r--lib/inets/doc/src/notes.xml39
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl2
-rw-r--r--lib/inets/src/http_lib/http_uri.erl2
-rw-r--r--lib/inets/src/http_lib/http_util.erl2
-rw-r--r--lib/inets/src/http_server/httpd_util.erl2
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl2
-rw-r--r--lib/inets/test/uri_SUITE.erl2
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/notes.xml28
-rw-r--r--lib/jinterface/vsn.mk2
-rw-r--r--lib/kernel/doc/src/disk_log.xml2
-rw-r--r--lib/kernel/doc/src/error_logger.xml20
-rw-r--r--lib/kernel/doc/src/kernel_app.xml2
-rw-r--r--lib/kernel/doc/src/net_kernel.xml2
-rw-r--r--lib/kernel/doc/src/notes.xml155
-rw-r--r--lib/kernel/src/Makefile2
-rw-r--r--lib/kernel/src/disk_log_1.erl2
-rw-r--r--lib/kernel/src/erts_debug.erl35
-rw-r--r--lib/kernel/src/group.erl2
-rw-r--r--lib/kernel/src/kernel.app.src4
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl2
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl44
-rw-r--r--lib/kernel/test/gen_udp_SUITE.erl2
-rw-r--r--lib/kernel/test/inet_sockopt_SUITE.erl2
-rw-r--r--lib/kernel/test/os_SUITE.erl2
-rw-r--r--lib/kernel/test/sendfile_SUITE.erl2
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/megaco/doc/src/notes.xml19
-rw-r--r--lib/megaco/vsn.mk4
-rw-r--r--lib/mnesia/doc/src/notes.xml21
-rw-r--r--lib/mnesia/src/mnesia_controller.erl2
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml57
-rw-r--r--lib/observer/src/etop.erl6
-rw-r--r--lib/observer/src/etop_txt.erl41
-rw-r--r--lib/observer/src/ttb.erl12
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/orber/test/multi_ORB_SUITE.erl2
-rw-r--r--lib/orber/test/orber_test_lib.erl2
-rw-r--r--lib/parsetools/doc/src/notes.xml29
-rw-r--r--lib/parsetools/vsn.mk2
-rw-r--r--lib/public_key/doc/src/notes.xml35
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/reltool/doc/src/notes.xml20
-rw-r--r--lib/reltool/doc/src/reltool_examples.xml2
-rw-r--r--lib/reltool/src/reltool.hrl2
-rw-r--r--lib/reltool/vsn.mk2
-rw-r--r--lib/runtime_tools/doc/src/notes.xml57
-rw-r--r--lib/runtime_tools/src/dbg.erl218
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/notes.xml29
-rw-r--r--lib/sasl/src/format_lib_supp.erl123
-rw-r--r--lib/sasl/src/misc_supp.erl20
-rw-r--r--lib/sasl/src/rb.erl113
-rw-r--r--lib/sasl/src/rb_format_supp.erl10
-rw-r--r--lib/sasl/src/release_handler.erl10
-rw-r--r--lib/sasl/src/systools_make.erl40
-rw-r--r--lib/sasl/src/systools_rc.erl32
-rw-r--r--lib/sasl/src/systools_relup.erl8
-rw-r--r--lib/sasl/test/sasl_report_SUITE.erl2
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/snmp/doc/src/notes.xml21
-rw-r--r--lib/snmp/src/agent/snmp_generic.erl2
-rw-r--r--lib/snmp/test/snmp_manager_test.erl2
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/src/notes.xml170
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml2
-rw-r--r--lib/ssh/test/ssh_property_test_SUITE.erl2
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml116
-rw-r--r--lib/ssl/src/dtls_handshake.hrl2
-rw-r--r--lib/ssl/test/make_certs.erl2
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl15
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl12
-rw-r--r--lib/ssl/test/ssl_test_lib.erl5
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl54
-rw-r--r--lib/stdlib/doc/src/lists.xml4
-rw-r--r--lib/stdlib/doc/src/notes.xml470
-rw-r--r--lib/stdlib/doc/src/sys.xml2
-rw-r--r--lib/stdlib/src/c.erl2
-rw-r--r--lib/stdlib/src/erl_lint.erl11
-rw-r--r--lib/stdlib/src/error_logger_file_h.erl2
-rw-r--r--lib/stdlib/src/gen.erl2
-rw-r--r--lib/stdlib/src/proc_lib.erl6
-rw-r--r--lib/stdlib/src/shell.erl16
-rw-r--r--lib/stdlib/src/string.erl10
-rw-r--r--lib/stdlib/src/sys.erl6
-rw-r--r--lib/stdlib/test/c_SUITE.erl53
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl49
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl41
-rw-r--r--lib/stdlib/test/shell_SUITE.erl50
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/doc/src/notes.xml17
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl2
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl6
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/tools/doc/src/lcnt.xml106
-rw-r--r--lib/tools/doc/src/lcnt_chapter.xml11
-rw-r--r--lib/tools/doc/src/notes.xml55
-rw-r--r--lib/tools/src/cover.erl42
-rw-r--r--lib/tools/src/fprof.erl20
-rw-r--r--lib/tools/src/lcnt.erl77
-rw-r--r--lib/tools/src/make.erl48
-rw-r--r--lib/tools/src/tools.app.src2
-rw-r--r--lib/tools/test/lcnt_SUITE.erl7
-rw-r--r--lib/tools/test/make_SUITE.erl43
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/wx/c_src/egl_impl.h2
-rw-r--r--lib/wx/doc/src/notes.xml17
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--lib/xmerl/doc/src/notes.xml19
-rw-r--r--lib/xmerl/src/xmerl_sax_old_dom.erl2
-rw-r--r--lib/xmerl/src/xmerl_sax_simple_dom.erl2
-rw-r--r--lib/xmerl/vsn.mk2
-rw-r--r--otp_versions.table3
-rw-r--r--system/doc/getting_started/conc_prog.xml2
-rw-r--r--system/doc/programming_examples/bit_syntax.xml2
-rw-r--r--system/doc/reference_manual/distributed.xml2
-rw-r--r--system/doc/top/templates/index.html.src2
372 files changed, 8130 insertions, 3215 deletions
diff --git a/.travis.yml b/.travis.yml
index eee75c9769..88a4c46f77 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -28,8 +28,18 @@ matrix:
- docker
script:
- ./scripts/build-docker-otp 32 sh -c "scripts/build-otp && ./otp_build tests && scripts/run-smoke-tests && bin/dialyzer --build_plt --apps erts kernel stdlib"
- after_success:
- after_script:
+ - env: Linux64Dialyzer
+ os: linux
+ script:
+ - ./scripts/build-otp
+ - ./scripts/run-dialyzer
+ - env: Linux64SmokeTest
+ os: linux
+ script:
+ - ./scripts/build-otp
+ - ./otp_build tests
+ - make release_docs
+ - ./scripts/run-smoke-tests
before_script:
- set -e
@@ -37,13 +47,3 @@ before_script:
- export PATH=$ERL_TOP/bin:$PATH
- export ERL_LIBS=''
- export MAKEFLAGS=-j4
-
-script:
- - ./scripts/build-otp
-
-after_success:
- - ./scripts/run-dialyzer
- - ./otp_build tests && make release_docs
-
-after_script:
- - ./scripts/run-smoke-tests
diff --git a/HOWTO/INSTALL-CROSS.md b/HOWTO/INSTALL-CROSS.md
index 29614966b8..3796bf8a59 100644
--- a/HOWTO/INSTALL-CROSS.md
+++ b/HOWTO/INSTALL-CROSS.md
@@ -521,27 +521,6 @@ When a variable has been set, no warning will be issued.
`posix_memalign` implementation that accepts larger than page size
alignment.
-Copyright and License
----------------------
-
-%CopyrightBegin%
-
-Copyright Ericsson AB 2009-2014. 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%
-
[$ERL_TOP/HOWTO/INSTALL.md]: INSTALL.md
diff --git a/HOWTO/INSTALL-WIN32.md b/HOWTO/INSTALL-WIN32.md
index 4304fb3fb8..98c608060d 100644
--- a/HOWTO/INSTALL-WIN32.md
+++ b/HOWTO/INSTALL-WIN32.md
@@ -884,28 +884,6 @@ to a common MSYS command prompt for building. Also all test suites
cannot be built as MsysGIT/MSYS does not handle symbolic links.
-Copyright and License
----------------------
-
-%CopyrightBegin%
-
-Copyright Ericsson AB 2003-2015. 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%
-
-
[1]: http://www.erlang.org/static/doc/mailinglist.html
[2]: http://bugs.erlang.org
[3]: https://github.com/erlang/otp
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md
index bec09bdae1..36365799e3 100644
--- a/HOWTO/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -780,134 +780,6 @@ Use `hipe:help_options/0` to print out the available options.
1> hipe:help_options().
-#### Running with GS ####
-
-The `gs` application requires the GUI toolkit Tcl/Tk to run. At least
-version 8.4 is required.
-
-Known platform issues
----------------------
-
-* Suse linux 9.1 is shipped with a patched GCC version 3.3.3, having the
- rpm named `gcc-3.3.3-41`. That version has a serious optimization bug
- that makes it unusable for building the Erlang emulator. Please
- upgrade GCC to a newer version before building on Suse 9.1. Suse Linux
- Enterprise edition 9 (SLES9) has `gcc-3.3.3-43` and is not affected.
-
-* `gcc-4.3.0` has a serious optimizer bug. It produces an Erlang emulator
- that will crash immediately. The bug is supposed to be fixed in
- `gcc-4.3.1`.
-
-* FreeBSD had a bug which caused `kqueue`/`poll`/`select` to fail to detect
- that a `writev()` on a pipe has been made. This bug should have been fixed
- in FreeBSD 6.3 and FreeBSD 7.0. NetBSD and DragonFlyBSD probably have or
- have had the same bug. More information can be found at:
-
- * <http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/kern/sys_pipe.c>
- * <http://lists.freebsd.org/pipermail/freebsd-arch/2007-September/006790.html>
-
-* `getcwd()` on Solaris 9 can cause an emulator crash. If you have
- async-threads enabled you can increase the stack size of the
- async-threads as a temporary workaround. See the `+a` command-line
- argument in the documentation of `erl(1)`. Without async-threads the
- emulator is not as vulnerable to this bug, but if you hit it without
- async-threads the only workaround available is to enable async-threads
- and increase the stack size of the async-threads. Oracle has however
- released patches that fixes the issue:
-
- > Problem Description: 6448300 large mnttab can cause stack overrun
- > during Solaris 9 getcwd
-
- More information can be found at:
- * <https://getupdates.oracle.com/readme/112874-40>
- * <https://getupdates.oracle.com/readme/114432-29>
-
-* `sed` on Solaris seem to have some problems. For example on
- Solaris 8, the BSD `sed` and XPG4 `sed` should be avoided.
- Make sure `/bin/sed` or `/usr/bin/sed` is used on the Solaris
- platform.
-
-
-Daily Build and Test
---------------------
-
-At Ericsson we have a "Daily Build and Test" that runs on:
-
-* Solaris 8, 9
- * Sparc32
- * Sparc64
-* Solaris 10
- * Sparc32
- * Sparc64
- * x86
-* SuSE Linux/GNU 9.4, 10.1
- * x86
-* SuSE Linux/GNU 10.0, 10.1, 11.0
- * x86
- * x86\_64
-* openSuSE 11.4 (Celadon)
- * x86\_64 (valgrind)
-* Fedora 7
- * PowerPC
-* Fedora 16
- * x86\_64
-* Gentoo Linux/GNU 1.12.11.1
- * x86
-* Ubuntu Linux/GNU 7.04, 10.04, 10.10, 11.04, 12.04
- * x86\_64
-* MontaVista Linux/GNU 4.0.1
- * PowerPC
-* FreeBSD 10.0
- * x86
-* OpenBSD 5.4
- * x86\_64
-* OS X 10.5.8 (Leopard), 10.7.5 (Lion), 10.9.1 (Mavericks)
- * x86
-* Windows XP SP3, 2003, Vista, 7
- * x86
-* Windows 7
- * x86\_64
-
-We also have the following "Daily Cross Builds":
-
-* SuSE Linux/GNU 10.1 x86 -> SuSE Linux/GNU 10.1 x86\_64
-* SuSE Linux/GNU 10.1 x86\_64 -> Linux/GNU TILEPro64
-
-and the following "Daily Cross Build Tests":
-
-* SuSE Linux/GNU 10.1 x86\_64
-
-
-Authors
--------
-
-Authors are mostly listed in the application's `AUTHORS` files,
-that is `$ERL_TOP/lib/*/AUTHORS` and `$ERL_TOP/erts/AUTHORS`,
-not in the individual source files.
-
-
-Copyright and License
----------------------
-
-%CopyrightBegin%
-
-Copyright Ericsson AB 1998-2015. 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%
-
-
diff --git a/OTP_VERSION b/OTP_VERSION
index ab1e4f6a31..06d4ac2bfd 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-20.0-rc2
+21.0-rc0
diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
index f67033483a..8deb4dcdd4 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/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam
index 5096b7f2e1..a5e95c8ecc 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/stdlib/ebin/error_logger_file_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
index 83e761e07c..d550ccd4de 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/sys.beam b/bootstrap/lib/stdlib/ebin/sys.beam
index f88a044ad1..ce0895e903 100644
--- a/bootstrap/lib/stdlib/ebin/sys.beam
+++ b/bootstrap/lib/stdlib/ebin/sys.beam
Binary files differ
diff --git a/erts/configure.in b/erts/configure.in
index 830e3d7776..2579e39b29 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -640,6 +640,7 @@ case $chk_arch_ in
armv7l) ARCH=arm;;
armv7hl) ARCH=arm;;
tile) ARCH=tile;;
+ e2k) ARCH=e2k;;
*) ARCH=noarch;;
esac
@@ -3855,7 +3856,7 @@ case $host_os in
darwin*)
# Mach-O linker: a shared lib and a loadable
# object file is not the same thing.
- DED_LDFLAGS="-bundle -flat_namespace -undefined suppress"
+ DED_LDFLAGS="-bundle -bundle_loader ${ERL_TOP}/bin/$host/beam.smp"
case $ARCH in
amd64)
DED_LDFLAGS="-m64 $DED_LDFLAGS"
diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml
index 8391408a2e..610351db6c 100644
--- a/erts/doc/src/erl_dist_protocol.xml
+++ b/erts/doc/src/erl_dist_protocol.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2016</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 687ff38cbf..105734d5b2 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -6483,6 +6483,9 @@ lists:map(
<p>This is the sum of the runtime for all threads
in the Erlang runtime system and can therefore be greater
than the wall clock time.</p>
+ <warning><p>This value might wrap due to limitations in the
+ underlying functionality provided by the operating system
+ that is used.</p></warning>
<p>Example:</p>
<pre>
> <input>statistics(runtime).</input>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index e61114c504..722f7aaebd 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,678 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 9.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a bug in gen_tcp:send where it never returned when
+ repeatedly called on a remotely closed TCP socket.</p>
+ <p>
+ Own Id: OTP-13939 Aux Id: ERL-193 </p>
+ </item>
+ <item>
+ <p>
+ Fixed segfault that could happen during cleanup of
+ aborted erlang:port_command/3 calls. A port_command is
+ aborted if the port is closed at the same time as the
+ port_command was issued. This bug was introduced in
+ erts-8.0.</p>
+ <p>
+ Own Id: OTP-14481</p>
+ </item>
+ <item>
+ <p>
+ Fixed implementation of <c>statistics(wall_clock)</c> and
+ <c>statistics(runtime)</c> so that values do not
+ unnecessarily wrap due to the emulator. Note that the
+ values returned by <c>statistics(runtime)</c> may still
+ wrap due to limitations in the underlying functionality
+ provided by the operating system.</p>
+ <p>
+ Own Id: OTP-14484</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 9.0</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fix various bugs regarding loading, upgrade and purge
+ of HiPE compiled code:</p> <list> <item>The native code
+ memory for a purged module was never deallocated.</item>
+ <item>Wrong functions could in some cases be called after
+ a module upgrade.</item>
+ <item><c>erlang:check_process_code</c> did not check for
+ recursive calls made from native code.</item> </list>
+ <p>
+ Own Id: OTP-13968</p>
+ </item>
+ <item>
+ <p>
+ Hipe optional LLVM backend does require LLVM version 3.9
+ or later as older versions forced strong dependencies on
+ erts internals structures.</p>
+ <p>
+ Own Id: OTP-14238</p>
+ </item>
+ <item>
+ <p>When an exception such as '<c>throw(HugeTerm)</c>' was
+ caught, <c>HugeTerm</c> term would be kept in the process
+ until the next exception occurred, potentially increasing
+ the heap size for the process. That has been
+ corrected.</p>
+ <p>
+ Own Id: OTP-14255 Aux Id: OTP-14400, OTP-14401 </p>
+ </item>
+ <item>
+ <p>
+ Slogans in crash dumps have been extended to print more
+ complex terms.</p>
+ <p>
+ Own Id: OTP-14303</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug when using <c>enif_inspect_binary</c> in
+ combination with <c>enif_copy</c>. In some circumstances
+ the inspected binary could be reallocated by the
+ <c>enif_copy</c> call when it shouldn't have been.</p>
+ <p>
+ Own Id: OTP-14304</p>
+ </item>
+ <item>
+ <p>
+ The address family <c>local</c> (AF_UNIX / AF_LOCAL) now
+ does not ensure zero termination of Linux Abstract
+ Addresses so they can use all bytes.</p>
+ <p>
+ Own Id: OTP-14305</p>
+ </item>
+ <item>
+ <p>
+ Use <c>-fno-PIE</c> for Gentoo Hardened and others that
+ don't accept linker flag <c>-no-pie</c>.</p>
+ <p>
+ Own Id: OTP-14307 Aux Id: PR-1379 </p>
+ </item>
+ <item>
+ <p>
+ Disable hipe for <c>ppc64le</c> architecture (little
+ endian) as it is not, and has never been, supported. It
+ was earlier equated with <c>ppc64</c> (big endian) which
+ lead to broken build without <c>--disable-hipe</c>.</p>
+ <p>
+ Own Id: OTP-14314 Aux Id: ERL-369, PR-1394 </p>
+ </item>
+ <item>
+ <p>
+ Fix 'epmd -kill' to return a failure exit status code if
+ epmd was not killed because of some error.</p>
+ <p>
+ Own Id: OTP-14324</p>
+ </item>
+ <item>
+ <p>Fixed the following dirty scheduler related bugs:</p>
+ <list> <item><p>the <c>+SDPcpu</c> command line argument
+ could cause the amount of dirty CPU schedulers to be set
+ to zero</p></item>
+ <item><p><c>erlang:system_flag(multi_scheduling, _)</c>
+ failed when only one normal scheduler was used together
+ with dirty scheduler support</p></item> </list>
+ <p>
+ Own Id: OTP-14335</p>
+ </item>
+ <item>
+ <p>
+ Fix erlexec to handle mismatch in sysconf and proc fs
+ when figuring out the cpu topology. This behaviour has
+ been seen when using docker together with
+ <c>--cpuset-cpus</c>.</p>
+ <p>
+ Own Id: OTP-14352</p>
+ </item>
+ <item>
+ <p>
+ Fixed memory segment cache used for multiblock carriers.
+ Huge (> 2GB) memory segments could cause a VM crash.
+ Creation of such huge memory segments used for multiblock
+ carriers is however very uncommon.</p>
+ <p>
+ Own Id: OTP-14360 Aux Id: ERL-401, PR-1417 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug causing <c>code:is_module_native</c> to falsely
+ return true when <c>local</c> call trace is enabled for
+ the module.</p>
+ <p>
+ Own Id: OTP-14390</p>
+ </item>
+ <item>
+ <p>
+ Fix emulator crash when receive tracing on a
+ <c>trace_delivered</c> message.</p>
+ <p>
+ Own Id: OTP-14411</p>
+ </item>
+ <item>
+ <p>
+ Fix file:sendfile error handling on SunOS when a
+ connection is closed during transmission.</p>
+ <p>
+ Own Id: OTP-14424</p>
+ </item>
+ <item>
+ <p>
+ <c>escript</c> did not handle paths with spaces correct.</p>
+ <p>
+ Own Id: OTP-14433</p>
+ </item>
+ <item>
+ <p>
+ Fix erroneous lock check assertion when <c>wx</c> is run
+ on MacOS X.</p>
+ <p>
+ Own Id: OTP-14437 Aux Id: ERL-360 </p>
+ </item>
+ <item>
+ <p>Active-mode TCP sockets are now cleaned up properly on
+ send/shutdown errors.</p>
+ <p>
+ Own Id: OTP-14441 Aux Id: ERL-430 </p>
+ </item>
+ <item>
+ <p>
+ Fix compilation of hipe_mkliterals when the LIBS
+ configure variable had to be set.</p>
+ <p>
+ Own Id: OTP-14447</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added <c>erlang:garbage_collect/2</c> that takes an
+ option list as the last argument that can be used to
+ control whether a minor or a major garbage collection is
+ to be done. Doing a minor collection only collects terms
+ that have recently died, but is cheaper than a major
+ collection.</p>
+ <p>
+ Own Id: OTP-11695</p>
+ </item>
+ <item>
+ <p>
+ Optimized test for tuples with an atom as first element.</p>
+ <p>
+ Own Id: OTP-12148</p>
+ </item>
+ <item>
+ <p>
+ Erlang literals are no longer copied during process to
+ process messaging.</p>
+ <p>
+ Own Id: OTP-13529</p>
+ </item>
+ <item>
+ <p>Add support in the <c>erl_nif</c> API for asynchronous
+ message notifications when sockets or other file
+ descriptors are ready to accept read or write operations.
+ The following functions have been added:</p> <list>
+ <item><p>enif_select</p></item>
+ <item><p>enif_monitor_process</p></item>
+ <item><p>enif_demonitor_process</p></item>
+ <item><p>enif_compare_monitors</p></item>
+ <item><p>enif_open_resource_type_x</p></item> </list>
+ <p>
+ Own Id: OTP-13684</p>
+ </item>
+ <item>
+ <p>There are two new guard BIFs '<c>floor/1</c>' and
+ '<c>ceil/1</c>'. They both return integers. In the
+ '<c>math</c>' module, there are two new BIFs with the
+ same names that return floating point values.</p>
+ <p>
+ Own Id: OTP-13692</p>
+ </item>
+ <item>
+ <p>
+ Remove deprecated <c>erlang:hash/2</c>.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13827</p>
+ </item>
+ <item>
+ <p>Replaced usage of deprecated symbolic <seealso
+ marker="erts:erlang#type-time_unit"><c>time
+ unit</c></seealso> representations.</p>
+ <p>
+ Own Id: OTP-13831 Aux Id: OTP-13735 </p>
+ </item>
+ <item>
+ <p>
+ Added support in zlib for extraction of the inflation
+ dictionary.</p>
+ <p>
+ Own Id: OTP-13842</p>
+ </item>
+ <item>
+ <p>
+ The previously used purge strategy has been removed. The
+ optional purge strategy introduced in ERTS version 8.1 is
+ now the only strategy available.</p>
+ <p>
+ The new purge strategy is slightly incompatible with the
+ old strategy. Previously processes holding <c>fun</c>s
+ that referred to the module being purged either failed a
+ soft purge, or was killed during a hard purge. The new
+ strategy completely ignores <c>fun</c>s. If <c>fun</c>s
+ referring to the code being purged exist, and are used
+ after a purge, an exception will be raised upon usage.
+ That is, the behavior will be exactly the same as the
+ case when a <c>fun</c> is received by a process after the
+ purge.</p>
+ <p>
+ For more information see the documentation of <seealso
+ marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13844 Aux Id: OTP-13833 </p>
+ </item>
+ <item>
+ <p>
+ Dirty schedulers are now enabled by default when the
+ runtime system is built with SMP support.</p>
+ <p>
+ Own Id: OTP-13860</p>
+ </item>
+ <item>
+ <p>
+ Improved ETS lookup/insert/delete speed for large
+ <c>set</c>, <c>bag</c> and <c>duplicate_bag</c> by a
+ significant reduction of the hash load factor. This speed
+ improvement comes at the expense of less than one word
+ per table entry. Tables with less than 256 entries are
+ not affected at all.</p>
+ <p>
+ Own Id: OTP-13903</p>
+ </item>
+ <item>
+ <p>
+ The NIF library <c>reload</c> feature is not supported
+ anymore. It has been marked as deprecated since OTP R15B.
+ This means that you are only allowed to do one successful
+ call to <c>erlang:load_nif/2</c> for each module
+ instance. A second call to <c>erlang:load_nif/2</c> will
+ return <c>{error, {reload, _}}</c> even if the NIF
+ library implements the <c>reload</c> callback.</p>
+ <p>
+ Runtime upgrade of a NIF library is still supported by
+ using the Erlang module upgrade mechanics with a current
+ and an old module instance existing at the same time with
+ their corresponding NIF libraries.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13908</p>
+ </item>
+ <item>
+ <p>
+ Add <c>erlang:system_info(atom_count)</c> and
+ <c>erlang:system_info(atom_limit)</c> to provide a way to
+ retrieve the current and maximum number of atoms.</p>
+ <p>
+ Own Id: OTP-13976</p>
+ </item>
+ <item>
+ <p>The function <c>fmod/2</c> has been added to the
+ <c>math</c> module.</p>
+ <p>
+ Own Id: OTP-14000</p>
+ </item>
+ <item>
+ <p>
+ <c>erlang:load_nif/2</c> returns new error type
+ <c>notsup</c> when called for a HiPE compiled module,
+ which is not supported.</p>
+ <p>
+ Own Id: OTP-14002</p>
+ </item>
+ <item>
+ <p>
+ Add driver and nif lock instrumentation to lcnt</p>
+ <p>
+ Own Id: OTP-14069</p>
+ </item>
+ <item>
+ <p>
+ Reduce memory pressure by converting sub-binaries to
+ heap-binaries when possible. This is done during garbage
+ collection.</p>
+ <p>
+ Own Id: OTP-14149</p>
+ </item>
+ <item>
+ <p>
+ Dirty schedulers are now enabled and supported on Erlang
+ runtime systems with SMP support.</p>
+ <p>
+ Besides support for dirty NIFs also support for dirty
+ BIFs and dirty garbage collection have been introduced.
+ All garbage collections that potentially will take a long
+ time to complete are now performed on dirty schedulers if
+ enabled.</p>
+ <p>
+ <seealso
+ marker="erts:erlang#statistics/1"><c>erlang:statistics/1</c></seealso>
+ with arguments inspecting scheduler and run queue states
+ have been changed due to the dirty scheduler support.
+ Code using this functionality may have to be rewritten
+ taking these incompatibilities into consideration.
+ Examples of such uses are calls to <seealso
+ marker="erts:erlang#statistics_scheduler_wall_time"><c>erlang:statistics(scheduler_wall_time)</c></seealso>,
+ <seealso
+ marker="erts:erlang#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>,
+ <seealso
+ marker="erts:erlang#statistics_total_active_tasks"><c>statistics(total_active_tasks)</c></seealso>,
+ etc.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14152</p>
+ </item>
+ <item>
+ <p>Atoms may now contain arbitrary Unicode
+ characters.</p>
+ <p>
+ Own Id: OTP-14178</p>
+ </item>
+ <item>
+ <p>
+ Introduce an event manager in Erlang to handle OS
+ signals. A subset of OS signals may be subscribed to and
+ those are described in the Kernel application.</p>
+ <p>
+ Own Id: OTP-14186</p>
+ </item>
+ <item>
+ <p>
+ The <c>escript</c> program now handles symbolic links to
+ escripts.</p>
+ <p>
+ This is useful for standalone systems with
+ <c>escript</c>s residing on a bin directory not included
+ in the execution path (as it may cause their <c>erl</c>
+ program(s) to override the desired one). Instead the
+ <c>escript</c>s can be referred to via symbolic links
+ from a bin directory in the path.</p>
+ <p>
+ Own Id: OTP-14201 Aux Id: PR-1293 </p>
+ </item>
+ <item>
+ <p>
+ All uses of the magic binary kludge has been replaced by
+ uses of erlang references.</p>
+ <p>
+ A magic binary was presented as an empty binary, but
+ actually referred other data internally in the Erlang VM.
+ Since they were presented as empty binaries, different
+ magic binaries compared as equal, and also lost their
+ internal data when passed out of an erlang node.</p>
+ <p>
+ The new usage of references has not got any of these
+ strange semantic issues, and the usage of these
+ references has been optimized to give the same
+ performance benefits as well as memory usage benefits as
+ magic binaries had.</p>
+ <p>
+ A couple of examples of previous uses of magic binaries
+ are match specifications and NIF resources.</p>
+ <p>
+ Own Id: OTP-14205</p>
+ </item>
+ <item>
+ <p>
+ The non-smp emulators have been deprecated and are
+ scheduled for removal in OTP-21.</p>
+ <p>
+ In preparation for this, the threaded non-smp emulator is
+ no longer built by default and has to be enabled using
+ the --enable-plain-emulator to configure.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14272</p>
+ </item>
+ <item>
+ <p>
+ Allow HiPE to run on VM built with
+ <c>--enable-m32-build</c>.</p>
+ <p>
+ Own Id: OTP-14330 Aux Id: PR-1397 </p>
+ </item>
+ <item>
+ <p>
+ Upgraded the OTP internal PCRE library from version 8.33
+ to version 8.40. This library is used for implementation
+ of the <seealso marker="stdlib:re"><c>re</c></seealso>
+ regular expressions module.</p>
+ <p>
+ Besides various bug fixes, the new version allows for
+ better stack protection. In order to utilize this
+ feature, the stack size of normal scheduler threads is
+ now by default set to 128 kilo words on all platforms.
+ The stack size of normal scheduler threads can be set
+ upon system start by passing the <seealso
+ marker="erts:erl#sched_thread_stack_size"><c>+sss</c></seealso>
+ command line argument to the <seealso
+ marker="erts:erl"><c>erl</c></seealso> command.</p>
+ <p>
+ See <url
+ href="http://pcre.org/original/changelog.txt"><c>http://pcre.org/original/changelog.txt</c></url>
+ for information about changes made to PCRE between the
+ versions 8.33 and 8.40.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14331 Aux Id: ERL-208 </p>
+ </item>
+ <item>
+ <p>
+ Remove generation of atoms in old latin1 external format
+ in the distribution between erlang nodes,
+ <c>erl_interface</c>, and <c>jinterface</c>. The new utf8
+ format for atoms was introduced in OTP R16. An OTP 20
+ node can therefore not connect to nodes older than R16.</p>
+ <p>
+ Atoms that can be encoded using latin1 are still encoded
+ by <c>term_to_binary()</c> using latin1 encoding. Note
+ that all atoms will by default be encoded using utf8 in a
+ future Erlang/OTP release. For more information see the
+ documentation of <seealso
+ marker="erts:erlang#term_to_binary/2"><c>erlang:term_to_binary/2</c></seealso>.</p>
+ <p>
+ Own Id: OTP-14337</p>
+ </item>
+ <item>
+ <p>
+ Added function <c>re:version/0</c> which returns
+ information about the OTP internal PCRE version used for
+ implementation of the <c>re</c> module.</p>
+ <p>
+ Own Id: OTP-14347 Aux Id: PR-1412 </p>
+ </item>
+ <item>
+ <p>
+ Added new debug bif <c>erlang:list_to_port/1</c>.</p>
+ <p>
+ Own Id: OTP-14348</p>
+ </item>
+ <item>
+ <p>
+ Various improvements of timer management internally in
+ the VM. These improvements both reduced memory
+ consumption of timer wheels as well as reduce the amount
+ of work that has to be performed in order to handle
+ timers.</p>
+ <p>
+ Own Id: OTP-14356</p>
+ </item>
+ <item>
+ <p> Sockets can now be bound to device (SO_BINDTODEVICE)
+ on platforms where it is supported. </p> <p> This has
+ been implemented e.g to support VRF-Lite under Linux; see
+ <url
+ href="https://www.kernel.org/doc/Documentation/networking/vrf.txt">
+ VRF </url>, and GitHub pull request <url
+ href="https://github.com/erlang/otp/pull/1326">#1326</url>.
+ </p>
+ <p>
+ Own Id: OTP-14357 Aux Id: PR-1326 </p>
+ </item>
+ <item>
+ <p>Added the following <seealso
+ marker="erl"><c>erl</c></seealso> command line arguments
+ with which you can set suggested stack for dirty
+ schedulers:</p> <taglist> <tag><seealso
+ marker="erl#dcpu_sched_thread_stack_size"><c>+sssdcpu</c></seealso></tag>
+ <item><p>for dirty CPU schedulers</p></item>
+ <tag><seealso
+ marker="erl#dio_sched_thread_stack_size"><c>+sssdio</c></seealso></tag>
+ <item><p>for dirty IO schedulers</p></item> </taglist>
+ <p>The default suggested stack size for dirty schedulers
+ is 40 kilo words.</p>
+ <p>
+ Own Id: OTP-14380</p>
+ </item>
+ <item>
+ <p>
+ Changed erts startup program name, argv 0, to use the
+ environment variable <c>ESCRIPT_NAME</c> so that
+ <c>erlc</c>, <c>dialyzer</c>, <c>typer</c>,
+ <c>ct_run</c>, or the escript name can be seen with
+ external programs, such as ps and htop (depending on
+ options), on unix.</p>
+ <p>
+ Own Id: OTP-14381</p>
+ </item>
+ <item>
+ <p>
+ Improvements of <c>escript</c> documentation.</p>
+ <p>
+ Own Id: OTP-14384 Aux Id: OTP-14201 </p>
+ </item>
+ <item>
+ <p>
+ Add function <c>enif_hash</c> for NIFs to calculate hash
+ values of arbitrary terms.</p>
+ <p>
+ Own Id: OTP-14385 Aux Id: PR-1413 </p>
+ </item>
+ <item>
+ <p>'<c>./configure --enable-lock-counter</c>' will
+ enabling building of an additional emulator that has
+ support for lock counting. (The option previously
+ existed, but would turn on lock counting in the default
+ emulator being built.) To start the lock-counting
+ emulator, use '<c>erl -emu_type lcnt</c>'.</p>
+ <p>On Windows, <c>erl</c> recognized the undocumented
+ option <c>-debug</c> for starting a debug-compiled
+ emulator. That option has been removed. Use '<c>erl
+ -emu_type debug</c>' instead.</p>
+ <p>
+ Own Id: OTP-14407</p>
+ </item>
+ <item>
+ <p>
+ Warnings have been added to the relevant documentation
+ about not using un-secure distributed nodes in exposed
+ environments.</p>
+ <p>
+ Own Id: OTP-14425</p>
+ </item>
+ <item>
+ <p>
+ Improvement of the documentation of the environment
+ variable <c>ERL_CRASH_DUMP_SECONDS</c> as well as the
+ default behavior when it is not set.</p>
+ <p>
+ Own Id: OTP-14434</p>
+ </item>
+ <item>
+ <p>
+ Enabled off-heap message queue for some system processes
+ that might receive large amounts of messages.</p>
+ <p>
+ Own Id: OTP-14438</p>
+ </item>
+ <item>
+ <p>ETS lock indexes have been replaced with the table
+ name in LCNT results.</p>
+ <p>
+ Own Id: OTP-14442 Aux Id: ERIERL-22 </p>
+ </item>
+ <item>
+ <p>
+ Introduced the new functions <seealso
+ marker="erl_nif#enif_whereis_pid"><c>enif_whereis_pid()</c></seealso>
+ and <seealso
+ marker="erl_nif#enif_whereis_port"><c>enif_whereis_port()</c></seealso>.</p>
+ <p>
+ Own Id: OTP-14453 Aux Id: PR-1400 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.5.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a bug in gen_tcp:send where it never returned when
+ repeatedly called on a remotely closed TCP socket.</p>
+ <p>
+ Own Id: OTP-13939 Aux Id: ERL-193 </p>
+ </item>
+ <item>
+ <p>
+ Fixed segfault that could happen during cleanup of
+ aborted erlang:port_command/3 calls. A port_command is
+ aborted if the port is closed at the same time as the
+ port_command was issued. This bug was introduced in
+ erts-8.0.</p>
+ <p>
+ Own Id: OTP-14481</p>
+ </item>
+ <item>
+ <p>
+ Fixed implementation of <c>statistics(wall_clock)</c> and
+ <c>statistics(runtime)</c> so that values do not
+ unnecessarily wrap due to the emulator. Note that the
+ values returned by <c>statistics(runtime)</c> may still
+ wrap due to limitations in the underlying functionality
+ provided by the operating system.</p>
+ <p>
+ Own Id: OTP-14484</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 8.3.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 61c1e14741..1ae905276d 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -116,6 +116,7 @@ ifeq ($(TYPE),lcnt)
PURIFY =
TYPEMARKER = .lcnt
TYPE_FLAGS = @CFLAGS@ -DERTS_ENABLE_LOCK_COUNT
+ENABLE_ALLOC_TYPE_VARS += lcnt
else
ifeq ($(TYPE),frmptr)
@@ -814,7 +815,7 @@ RUN_OBJS = \
$(OBJDIR)/erl_bif_binary.o $(OBJDIR)/erl_ao_firstfit_alloc.o \
$(OBJDIR)/erl_thr_queue.o $(OBJDIR)/erl_sched_spec_pre_alloc.o \
$(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o \
- $(OBJDIR)/erl_msacc.o
+ $(OBJDIR)/erl_msacc.o $(OBJDIR)/erl_lock_flags.o
LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o
NIF_OBJS = $(OBJDIR)/erl_tracer_nif.o
diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c
index 2055c29190..c2d78aaccb 100644
--- a/erts/emulator/beam/atom.c
+++ b/erts/emulator/beam/atom.c
@@ -442,7 +442,8 @@ init_atom_table(void)
erts_smp_atomic_init_nob(&atom_put_ops, 0);
#endif
- erts_smp_rwmtx_init_opt(&atom_table_lock, &rwmtx_opt, "atom_tab");
+ erts_smp_rwmtx_init_opt(&atom_table_lock, &rwmtx_opt, "atom_tab", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
f.hash = (H_FUN) atom_hash;
f.cmp = (HCMP_FUN) atom_cmp;
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 007bf99b6e..14ddb74324 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -97,7 +97,8 @@ init_purge_state(void)
{
purge_state.module = THE_NON_VALUE;
- erts_smp_mtx_init(&purge_state.mtx, "purge_state");
+ erts_smp_mtx_init(&purge_state.mtx, "purge_state", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
purge_state.pending_purge_lambda =
erts_export_put(am_erts_code_purger, am_pending_purge_lambda, 3);
@@ -118,7 +119,9 @@ init_purge_state(void)
void
erts_beam_bif_load_init(void)
{
- erts_smp_mtx_init(&release_literal_areas.mtx, "release_literal_areas");
+ erts_smp_mtx_init(&release_literal_areas.mtx, "release_literal_areas", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
+
release_literal_areas.first = NULL;
release_literal_areas.last = NULL;
erts_smp_atomic_init_nob(&erts_copy_literal_area__,
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index b9453c1d9a..950639f7ae 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -165,7 +165,8 @@ erts_bp_init(void) {
erts_smp_atomic32_init_nob(&erts_active_bp_index, 0);
erts_smp_atomic32_init_nob(&erts_staging_bp_index, 1);
#ifdef ERTS_DIRTY_SCHEDULERS
- erts_smp_mtx_init(&erts_dirty_bp_ix_mtx, "dirty_break_point_index");
+ erts_smp_mtx_init(&erts_dirty_bp_ix_mtx, "dirty_break_point_index", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
#endif
}
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 79d751d13e..872fba6899 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -3768,33 +3768,33 @@ do { \
Uint alloc;
Uint num_bytes;
- OpCase(i_bs_init_bits_heap_IIId): {
+ OpCase(i_bs_init_bits_heap_IIIx): {
num_bits = Arg(0);
alloc = Arg(1);
I++;
goto do_bs_init_bits_known;
}
- OpCase(i_bs_init_bits_IId): {
+ OpCase(i_bs_init_bits_IIx): {
num_bits = Arg(0);
alloc = 0;
goto do_bs_init_bits_known;
}
- OpCase(i_bs_init_bits_fail_heap_sIjId): {
+ OpCase(i_bs_init_bits_fail_heap_sIjIx): {
GetArg1(0, num_bits_term);
alloc = Arg(1);
I += 2;
goto do_bs_init_bits;
}
- OpCase(i_bs_init_bits_fail_yjId): {
+ OpCase(i_bs_init_bits_fail_yjIx): {
num_bits_term = yb(Arg(0));
I++;
alloc = 0;
goto do_bs_init_bits;
}
- OpCase(i_bs_init_bits_fail_xjId): {
+ OpCase(i_bs_init_bits_fail_xjIx): {
num_bits_term = xb(Arg(0));
I++;
alloc = 0;
@@ -3873,7 +3873,8 @@ do { \
new_binary = make_binary(sb);
}
HEAP_SPACE_VERIFIED(0);
- StoreBifResult(2, new_binary);
+ xb(Arg(2)) = new_binary;
+ Next(3);
} else {
Binary* bptr;
ProcBin* pb;
@@ -3908,21 +3909,21 @@ do { \
{
Eterm BsOp1, BsOp2;
- OpCase(i_bs_init_fail_heap_sIjId): {
+ OpCase(i_bs_init_fail_heap_sIjIx): {
GetArg1(0, BsOp1);
BsOp2 = Arg(1);
I += 2;
goto do_bs_init;
}
- OpCase(i_bs_init_fail_yjId): {
+ OpCase(i_bs_init_fail_yjIx): {
BsOp1 = yb(Arg(0));
BsOp2 = 0;
I++;
goto do_bs_init;
}
- OpCase(i_bs_init_fail_xjId): {
+ OpCase(i_bs_init_fail_xjIx): {
BsOp1 = xb(Arg(0));
BsOp2 = 0;
I++;
@@ -3954,14 +3955,14 @@ do { \
}
- OpCase(i_bs_init_heap_IIId): {
+ OpCase(i_bs_init_heap_IIIx): {
BsOp1 = Arg(0);
BsOp2 = Arg(1);
I++;
goto do_proc_bin_alloc;
}
- OpCase(i_bs_init_IId): {
+ OpCase(i_bs_init_IIx): {
BsOp1 = Arg(0);
BsOp2 = 0;
}
@@ -3996,17 +3997,18 @@ do { \
OH_OVERHEAD(&(MSO(c_p)), BsOp1 / sizeof(Eterm));
- StoreBifResult(2, make_binary(pb));
+ xb(Arg(2)) = make_binary(pb);
+ Next(3);
}
- OpCase(i_bs_init_heap_bin_heap_IIId): {
+ OpCase(i_bs_init_heap_bin_heap_IIIx): {
BsOp1 = Arg(0);
BsOp2 = Arg(1);
I++;
goto do_heap_bin_alloc;
}
- OpCase(i_bs_init_heap_bin_IId): {
+ OpCase(i_bs_init_heap_bin_IIx): {
BsOp1 = Arg(0);
BsOp2 = 0;
}
@@ -4026,11 +4028,12 @@ do { \
hb->size = BsOp1;
erts_current_bin = (byte *) hb->data;
BsOp1 = make_binary(hb);
- StoreBifResult(2, BsOp1);
+ xb(Arg(2)) = BsOp1;
+ Next(3);
}
}
- OpCase(bs_add_jssId): {
+ OpCase(bs_add_jssIx): {
Eterm Op1, Op2;
Uint Unit = Arg(3);
@@ -4060,7 +4063,8 @@ do { \
Op1 = erts_make_integer(Op1, c_p);
HTOP = HEAP_TOP(c_p);
}
- StoreBifResult(4, Op1);
+ xb(Arg(4)) = Op1;
+ Next(5);
}
goto badarg;
} else {
@@ -4129,7 +4133,7 @@ do { \
* Operands: Fail ExtraHeap Live Unit Size Dst
*/
- OpCase(i_bs_append_jIIIsd): {
+ OpCase(i_bs_append_jIIIsx): {
Uint live = Arg(2);
Uint res;
Eterm Size;
@@ -4143,13 +4147,14 @@ do { \
/* c_p->freason is already set (may be either BADARG or SYSTEM_LIMIT). */
goto lb_Cl_error;
}
- StoreBifResult(5, res);
+ xb(Arg(5)) = res;
+ Next(6);
}
/*
* Operands: Fail Size Src Unit Dst
*/
- OpCase(i_bs_private_append_jIssd): {
+ OpCase(i_bs_private_append_jIssx): {
Eterm res;
Eterm Size, Src;
@@ -4159,7 +4164,8 @@ do { \
/* c_p->freason is already set (may be either BADARG or SYSTEM_LIMIT). */
goto lb_Cl_error;
}
- StoreBifResult(4, res);
+ xb(Arg(4)) = res;
+ Next(5);
}
OpCase(bs_init_writable): {
@@ -4176,7 +4182,7 @@ do { \
* can get away with that because we KNOW that bs_put_utf8 will do
* full error checking.
*/
- OpCase(i_bs_utf8_size_sd): {
+ OpCase(i_bs_utf8_size_sx): {
Eterm arg;
Eterm result;
@@ -4190,7 +4196,8 @@ do { \
} else {
result = make_small(4);
}
- StoreBifResult(1, result);
+ xb(Arg(1)) = result;
+ Next(2);
}
OpCase(i_bs_put_utf8_js): {
@@ -4211,7 +4218,7 @@ do { \
* full error checking.
*/
- OpCase(i_bs_utf16_size_sd): {
+ OpCase(i_bs_utf16_size_sx): {
Eterm arg;
Eterm result = make_small(2);
@@ -4219,7 +4226,8 @@ do { \
if (arg >= make_small(0x10000UL)) {
result = make_small(4);
}
- StoreBifResult(1, result);
+ xb(Arg(1)) = result;
+ Next(2);
}
OpCase(bs_put_utf16_jIs): {
@@ -4313,7 +4321,7 @@ do { \
*HTOP = HEADER_BIN_MATCHSTATE(slots);
HTOP += wordsneeded;
HEAP_SPACE_VERIFIED(0);
- StoreResult(make_matchstate(dst), Arg(3));
+ xb(Arg(3)) = make_matchstate(dst);
}
} else if (is_binary_header(header)) {
Eterm result;
@@ -4330,19 +4338,19 @@ do { \
if (is_non_value(result)) {
ClauseFail();
} else {
- StoreResult(result, Arg(3));
+ xb(Arg(3)) = result;
}
} else {
ClauseFail();
}
NextPF(4, next);
- OpCase(i_bs_start_match2_xfIId): {
+ OpCase(i_bs_start_match2_xfIIx): {
context = xb(Arg(0));
I++;
goto do_start_match;
}
- OpCase(i_bs_start_match2_yfIId): {
+ OpCase(i_bs_start_match2_yfIIx): {
context = yb(Arg(0));
I++;
goto do_start_match;
@@ -4397,7 +4405,7 @@ do { \
{
Eterm bs_get_integer8_context;
- OpCase(i_bs_get_integer_8_xfd): {
+ OpCase(i_bs_get_integer_8_xfx): {
ErlBinMatchBuffer *_mb;
Eterm _result;
bs_get_integer8_context = xb(Arg(0));
@@ -4412,14 +4420,15 @@ do { \
_result = make_small(_mb->base[BYTE_OFFSET(_mb->offset)]);
_mb->offset += 8;
}
- StoreBifResult(1, _result);
+ xb(Arg(1)) = _result;
+ Next(2);
}
}
{
Eterm bs_get_integer_16_context;
- OpCase(i_bs_get_integer_16_xfd):
+ OpCase(i_bs_get_integer_16_xfx):
bs_get_integer_16_context = xb(Arg(0));
I++;
@@ -4436,14 +4445,15 @@ do { \
_result = make_small(get_int16(_mb->base+BYTE_OFFSET(_mb->offset)));
_mb->offset += 16;
}
- StoreBifResult(1, _result);
+ xb(Arg(1)) = _result;
+ Next(2);
}
}
{
Eterm bs_get_integer_32_context;
- OpCase(i_bs_get_integer_32_xfId):
+ OpCase(i_bs_get_integer_32_xfIx):
bs_get_integer_32_context = xb(Arg(0));
I++;
@@ -4471,7 +4481,8 @@ do { \
HEAP_SPACE_VERIFIED(0);
}
#endif
- StoreBifResult(2, _result);
+ xb(Arg(2)) = _result;
+ Next(3);
}
}
@@ -4479,7 +4490,7 @@ do { \
Eterm Ms, Sz;
/* Operands: x(Reg) Size Live Fail Flags Dst */
- OpCase(i_bs_get_integer_imm_xIIfId): {
+ OpCase(i_bs_get_integer_imm_xIIfIx): {
Uint wordsneeded;
Ms = xb(Arg(0));
Sz = Arg(1);
@@ -4491,7 +4502,7 @@ do { \
}
/* Operands: x(Reg) Size Fail Flags Dst */
- OpCase(i_bs_get_integer_small_imm_xIfId): {
+ OpCase(i_bs_get_integer_small_imm_xIfIx): {
Ms = xb(Arg(0));
Sz = Arg(1);
I += 2;
@@ -4516,14 +4527,15 @@ do { \
if (is_non_value(result)) {
ClauseFail();
}
- StoreBifResult(2, result);
+ xb(Arg(2)) = result;
+ Next(3);
}
}
/*
* Operands: Fail Live FlagsAndUnit Ms Sz Dst
*/
- OpCase(i_bs_get_integer_fIIssd): {
+ OpCase(i_bs_get_integer_fIIssx): {
Uint flags;
Uint size;
Eterm Ms;
@@ -4558,14 +4570,15 @@ do { \
if (is_non_value(result)) {
ClauseFail();
}
- StoreBifResult(5, result);
+ xb(Arg(5)) = result;
+ Next(6);
}
{
Eterm get_utf8_context;
/* Operands: MatchContext Fail Dst */
- OpCase(i_bs_get_utf8_xfd): {
+ OpCase(i_bs_get_utf8_xfx): {
get_utf8_context = xb(Arg(0));
I++;
}
@@ -4580,7 +4593,8 @@ do { \
if (is_non_value(result)) {
ClauseFail();
}
- StoreBifResult(1, result);
+ xb(Arg(1)) = result;
+ Next(2);
}
}
@@ -4588,7 +4602,7 @@ do { \
Eterm get_utf16_context;
/* Operands: MatchContext Fail Flags Dst */
- OpCase(i_bs_get_utf16_xfId): {
+ OpCase(i_bs_get_utf16_xfIx): {
get_utf16_context = xb(Arg(0));
I++;
}
@@ -4603,7 +4617,8 @@ do { \
if (is_non_value(result)) {
ClauseFail();
}
- StoreBifResult(2, result);
+ xb(Arg(2)) = result;
+ Next(3);
}
}
@@ -6354,7 +6369,7 @@ BeamInstr *I, Uint stack_offset)
{
int arity;
Export* ep;
- Eterm tmp, this;
+ Eterm tmp;
/*
* Check the arguments which should be of the form apply(Module,
@@ -6377,20 +6392,8 @@ BeamInstr *I, Uint stack_offset)
while (1) {
Eterm m, f, a;
- /* The module argument may be either an atom or an abstract module
- * (currently implemented using tuples, but this might change).
- */
- this = THE_NON_VALUE;
- if (is_not_atom(module)) {
- Eterm* tp;
-
- if (is_not_tuple(module)) goto error;
- tp = tuple_val(module);
- if (arityval(tp[0]) < 1) goto error;
- this = module;
- module = tp[1];
- if (is_not_atom(module)) goto error;
- }
+
+ if (is_not_atom(module)) goto error;
if (module != am_erlang || function != am_apply)
break;
@@ -6425,9 +6428,7 @@ BeamInstr *I, Uint stack_offset)
}
/*
* Walk down the 3rd parameter of apply (the argument list) and copy
- * the parameters to the x registers (reg[]). If the module argument
- * was an abstract module, add 1 to the function arity and put the
- * module argument in the n+1st x register as a THIS reference.
+ * the parameters to the x registers (reg[]).
*/
tmp = args;
@@ -6444,9 +6445,6 @@ BeamInstr *I, Uint stack_offset)
if (is_not_nil(tmp)) { /* Must be well-formed list */
goto error;
}
- if (this != THE_NON_VALUE) {
- reg[arity++] = this;
- }
/*
* Get the index into the export table, or failing that the export
@@ -6485,18 +6483,7 @@ fixed_apply(Process* p, Eterm* reg, Uint arity,
return 0;
}
- /* The module argument may be either an atom or an abstract module
- * (currently implemented using tuples, but this might change).
- */
- if (is_not_atom(module)) {
- Eterm* tp;
- if (is_not_tuple(module)) goto error;
- tp = tuple_val(module);
- if (arityval(tp[0]) < 1) goto error;
- module = tp[1];
- if (is_not_atom(module)) goto error;
- ++arity;
- }
+ if (is_not_atom(module)) goto error;
/* Handle apply of apply/3... */
if (module == am_erlang && function == am_apply && arity == 3)
@@ -6829,7 +6816,7 @@ apply_fun(Process* p, Eterm fun, Eterm args, Eterm* reg)
}
if (is_not_nil(tmp)) { /* Must be well-formed list */
- p->freason = EXC_UNDEF;
+ p->freason = EXC_BADARG;
return NULL;
}
reg[arity] = fun;
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index b8d8634e28..c088bdb751 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1999-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/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 40dd4129d2..890277a3ba 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -62,9 +62,6 @@ static erts_smp_atomic32_t msacc;
static Export *await_sched_wall_time_mod_trap;
static erts_smp_atomic32_t sched_wall_time;
-static erts_smp_mtx_t ports_snapshot_mtx;
-erts_smp_atomic_t erts_dead_ports_ptr; /* To store dying ports during snapshot */
-
#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
/*
@@ -5138,9 +5135,6 @@ void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
void erts_init_bif(void)
{
- erts_smp_mtx_init(&ports_snapshot_mtx, "ports_snapshot");
- erts_smp_atomic_init_nob(&erts_dead_ports_ptr, (erts_aint_t) NULL);
-
/*
* bif_return_trap/2 is a hidden BIF that bifs that need to
* yield the calling process traps to.
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index a8bbf5f8c1..962b00ae7b 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -437,7 +437,10 @@ bif erts_debug:dump_links/1
#
# Lock counter bif's
#
-bif erts_debug:lock_counters/1
+bif erts_debug:lcnt_control/2
+bif erts_debug:lcnt_control/1
+bif erts_debug:lcnt_collect/0
+bif erts_debug:lcnt_clear/0
#
# New Bifs in R8.
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 1f6feade1c..7128b8ed23 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -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.
diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h
index 258038a157..48efce20e7 100644
--- a/erts/emulator/beam/big.h
+++ b/erts/emulator/beam/big.h
@@ -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.
diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c
index ec6267711b..8a3d1b20b4 100644
--- a/erts/emulator/beam/code_ix.c
+++ b/erts/emulator/beam/code_ix.c
@@ -57,7 +57,8 @@ void erts_code_ix_init(void)
*/
erts_smp_atomic32_init_nob(&the_active_code_index, 0);
erts_smp_atomic32_init_nob(&the_staging_code_index, 0);
- erts_smp_mtx_init(&code_write_permission_mtx, "code_write_permission");
+ erts_smp_mtx_init(&code_write_permission_mtx, "code_write_permission", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_tsd_key_create(&has_code_write_permission,
"erts_has_code_write_permission");
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 982f1066df..09fdb897f5 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -3223,7 +3223,8 @@ static ErtsNodesMonitor *nodes_monitors_end;
static void
init_nodes_monitors(void)
{
- erts_smp_mtx_init(&nodes_monitors_mtx, "nodes_monitors");
+ erts_smp_mtx_init(&nodes_monitors_mtx, "nodes_monitors", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
nodes_monitors = NULL;
nodes_monitors_end = NULL;
}
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index 169e1e423d..c7ab444c96 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -3819,7 +3819,8 @@ hdbg_init(void)
hdbg_mblks[ERL_ALC_HDBG_MAX_MBLK-1].next = NULL;
free_hdbg_mblks = &hdbg_mblks[0];
used_hdbg_mblks = NULL;
- erts_mtx_init(&hdbg_mblk_mtx, "erts_alloc_hard_debug");
+ erts_mtx_init(&hdbg_mblk_mtx, "erts_alloc_hard_debug", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR);
}
static void *check_memory_fence(void *ptr,
diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h
index 758d529f87..97a1cf1308 100644
--- a/erts/emulator/beam/erl_alloc.h
+++ b/erts/emulator/beam/erl_alloc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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.
@@ -344,7 +344,8 @@ ERTS_QUICK_ALLOC_IMPL(NAME, TYPE, PASZ, ALCT, \
#define ERTS_SMP_QUALLOC_IMPL(NAME, TYPE, PASZ, ALCT) \
static erts_smp_spinlock_t NAME##_lck; \
ERTS_QUICK_ALLOC_IMPL(NAME, TYPE, PASZ, ALCT, \
- erts_smp_spinlock_init(&NAME##_lck, #NAME "_alloc_lock"),\
+ erts_smp_spinlock_init(&NAME##_lck, #NAME "_alloc_lock", NIL, \
+ ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR),\
erts_smp_spin_lock(&NAME##_lck), \
erts_smp_spin_unlock(&NAME##_lck))
@@ -358,7 +359,8 @@ ERTS_SMP_QUALLOC_IMPL(NAME, TYPE, PASZ, ALCT)
#define ERTS_TS_QUALLOC_IMPL(NAME, TYPE, PASZ, ALCT) \
static erts_mtx_t NAME##_lck; \
ERTS_QUICK_ALLOC_IMPL(NAME, TYPE, PASZ, ALCT, \
- erts_mtx_init(NAME##_lck, #NAME "_alloc_lock"), \
+ erts_mtx_init(NAME##_lck, #NAME "_alloc_lock", NIL, \
+ ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR),\
erts_mtx_lock(&NAME##_lck), \
erts_mtx_unlock(&NAME##_lck))
@@ -371,7 +373,8 @@ ERTS_PRE_ALLOC_IMPL(NAME, TYPE, PASZ, (void) 0, (void) 0, (void) 0)
#define ERTS_TS_PALLOC_IMPL(NAME, TYPE, PASZ) \
static erts_spinlock_t NAME##_lck; \
ERTS_PRE_ALLOC_IMPL(NAME, TYPE, PASZ, \
- erts_spinlock_init(&NAME##_lck, #NAME "_alloc_lock"),\
+ erts_spinlock_init(&NAME##_lck, #NAME "_alloc_lock", NIL, \
+ ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR),\
erts_spin_lock(&NAME##_lck), \
erts_spin_unlock(&NAME##_lck))
@@ -448,7 +451,7 @@ NAME##_free(TYPE *p) \
}
#ifdef DEBUG
-#define ERTS_PRE_ALLOC_SIZE(SZ) 2
+#define ERTS_PRE_ALLOC_SIZE(SZ) ((SZ) < 1000 ? (SZ)/10 + 10 : 100)
#define ERTS_PRE_ALLOC_CLOBBER(P, T) memset((void *) (P), 0xfd, sizeof(T))
#else
#define ERTS_PRE_ALLOC_SIZE(SZ) ((SZ) > 1 ? (SZ) : 1)
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 8a23a1526e..50a1d97dd5 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -368,6 +368,13 @@ type SSB SHORT_LIVED PROCESSES ssb
+endif
++if lcnt
+
+type LCNT_CARRIER STANDARD SYSTEM lcnt_lock_info_carrier
+type LCNT_VECTOR SHORT_LIVED SYSTEM lcnt_sample_vector
+
++endif
+
type DEBUG SHORT_LIVED SYSTEM debugging
type DDLL_PROCESS STANDARD SYSTEM ddll_processes
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 6fddba4b34..af86ad0548 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -6135,16 +6135,8 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
if (init->ts) {
allctr->thread_safe = 1;
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_mtx_init_x_opt(&allctr->mutex,
- "alcu_allocator",
- make_small(allctr->alloc_no),
- ERTS_LCNT_LT_ALLOC);
-#else
- erts_mtx_init_x(&allctr->mutex,
- "alcu_allocator",
- make_small(allctr->alloc_no));
-#endif /*ERTS_ENABLE_LOCK_COUNT*/
+ erts_mtx_init(&allctr->mutex, "alcu_allocator", make_small(allctr->alloc_no),
+ ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR);
#ifdef DEBUG
allctr->debug.saved_tid = 0;
@@ -6324,7 +6316,8 @@ erts_alcu_init(AlcUInit_t *init)
carrier_alignment = sizeof(Unit_t);
#endif
- erts_mtx_init(&init_atoms_mtx, "alcu_init_atoms");
+ erts_mtx_init(&init_atoms_mtx, "alcu_init_atoms", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR);
atoms_initialized = 0;
initialized = 1;
@@ -6592,3 +6585,45 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
#endif /* ERTS_ALLOC_UTIL_HARD_DEBUG */
+#ifdef ERTS_ENABLE_LOCK_COUNT
+
+static void lcnt_enable_allocator_lock_count(Allctr_t *allocator, int enable) {
+ if(!allocator->thread_safe) {
+ return;
+ }
+
+ if(enable) {
+ erts_lcnt_install_new_lock_info(&allocator->mutex.lcnt,
+ "alcu_allocator", make_small(allocator->alloc_no),
+ ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR);
+ } else {
+ erts_lcnt_uninstall(&allocator->mutex.lcnt);
+ }
+}
+
+static void lcnt_update_thread_spec_locks(ErtsAllocatorThrSpec_t *tspec, int enable) {
+ if(tspec->enabled) {
+ int i;
+
+ for(i = 0; i < tspec->size; i++) {
+ lcnt_enable_allocator_lock_count(tspec->allctr[i], enable);
+ }
+ }
+}
+
+void erts_lcnt_update_allocator_locks(int enable) {
+ int i;
+
+ for(i = ERTS_ALC_A_MIN; i < ERTS_ALC_A_MAX; i++) {
+ ErtsAllocatorInfo_t *ai = &erts_allctrs_info[i];
+
+ if(ai->enabled && ai->alloc_util) {
+ if(ai->thr_spec) {
+ lcnt_update_thread_spec_locks((ErtsAllocatorThrSpec_t*)ai->extra, enable);
+ } else {
+ lcnt_enable_allocator_lock_count((Allctr_t*)ai->extra, enable);
+ }
+ }
+ }
+}
+#endif /* ERTS_ENABLE_LOCK_COUNT */
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index 7a96bd0319..73c467aa0a 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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.
@@ -220,6 +220,10 @@ void* erts_alcu_literal_32_sys_realloc(Allctr_t*, void *ptr, Uint *size_p, Uint
void erts_alcu_literal_32_sys_dealloc(Allctr_t*, void *ptr, Uint size, int superalign);
#endif
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void erts_lcnt_update_allocator_locks(int enable);
+#endif
+
#endif /* !ERL_ALLOC_UTIL__ */
#if defined(GET_ERL_ALLOC_UTIL_IMPL) && !defined(ERL_ALLOC_UTIL_IMPL__)
@@ -673,7 +677,6 @@ void erts_alcu_assert_failed(char* expr, char* file, int line, char *func);
int is_sbc_blk(Block_t*);
#endif
-
#endif /* #if defined(GET_ERL_ALLOC_UTIL_IMPL)
&& !defined(ERL_ALLOC_UTIL_IMPL__) */
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index 84254af0c2..9a93034fcb 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -194,7 +194,8 @@ erts_init_async(void)
ptr += ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsAsyncData));
async->init.data.no_initialized = 0;
- erts_mtx_init(&async->init.data.mtx, "async_init_mtx");
+ erts_mtx_init(&async->init.data.mtx, "async_init_mtx", NIL,
+ ERTS_LOCK_FLAGS_CATEGORY_SCHEDULER);
erts_cnd_init(&async->init.data.cnd);
erts_atomic_init_nob(&async->init.data.id, 0);
@@ -213,7 +214,8 @@ erts_init_async(void)
for (i = 1; i <= erts_no_schedulers; i++) {
ErtsAsyncReadyQ *arq = async_ready_q(i);
#if ERTS_USE_ASYNC_READY_ENQ_MTX
- erts_mtx_init(&arq->x.data.enq_mtx, "async_enq_mtx");
+ erts_mtx_init(&arq->x.data.enq_mtx, "async_enq_mtx", make_small(i),
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_SCHEDULER);
#endif
erts_thr_q_finalize_dequeue_state_init(&arq->fin_deq);
qinit.arg = (void *) (SWord) i;
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index e2773475b0..e5d7efcc72 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -3544,24 +3544,32 @@ BIF_RETTYPE statistics_1(BIF_ALIST_1)
res = TUPLE2(hp, b1, b2);
BIF_RET(res);
} else if (BIF_ARG_1 == am_runtime) {
- UWord u1, u2, dummy;
+ ErtsMonotonicTime u1, u2;
Eterm b1, b2;
- elapsed_time_both(&u1,&dummy,&u2,&dummy);
- b1 = erts_make_integer(u1,BIF_P);
- b2 = erts_make_integer(u2,BIF_P);
- hp = HAlloc(BIF_P,3);
+ Uint hsz;
+ elapsed_time_both(&u1, NULL, &u2, NULL);
+ hsz = 3; /* 2-tuple */
+ (void) erts_bld_monotonic_time(NULL, &hsz, u1);
+ (void) erts_bld_monotonic_time(NULL, &hsz, u2);
+ hp = HAlloc(BIF_P, hsz);
+ b1 = erts_bld_monotonic_time(&hp, NULL, u1);
+ b2 = erts_bld_monotonic_time(&hp, NULL, u2);
res = TUPLE2(hp, b1, b2);
BIF_RET(res);
} else if (BIF_ARG_1 == am_run_queue) {
res = erts_run_queues_len(NULL, 1, 0, 0);
BIF_RET(make_small(res));
} else if (BIF_ARG_1 == am_wall_clock) {
- UWord w1, w2;
+ ErtsMonotonicTime w1, w2;
Eterm b1, b2;
+ Uint hsz;
wall_clock_elapsed_time_both(&w1, &w2);
- b1 = erts_make_integer((Uint) w1,BIF_P);
- b2 = erts_make_integer((Uint) w2,BIF_P);
- hp = HAlloc(BIF_P,3);
+ hsz = 3; /* 2-tuple */
+ (void) erts_bld_monotonic_time(NULL, &hsz, w1);
+ (void) erts_bld_monotonic_time(NULL, &hsz, w2);
+ hp = HAlloc(BIF_P, hsz);
+ b1 = erts_bld_monotonic_time(&hp, NULL, w1);
+ b2 = erts_bld_monotonic_time(&hp, NULL, w2);
res = TUPLE2(hp, b1, b2);
BIF_RET(res);
} else if (BIF_ARG_1 == am_io) {
@@ -4444,48 +4452,120 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
}
#ifdef ERTS_ENABLE_LOCK_COUNT
+
+typedef struct {
+ /* info->location_count may increase between size calculation and term
+ * building, so we cap it at the value sampled in lcnt_build_result_vector.
+ *
+ * Shrinking is safe though. */
+ int max_location_count;
+ erts_lcnt_lock_info_t *info;
+} lcnt_sample_t;
+
+typedef struct lcnt_sample_vector_ {
+ lcnt_sample_t *elements;
+ size_t size;
+} lcnt_sample_vector_t;
+
+static lcnt_sample_vector_t lcnt_build_sample_vector(erts_lcnt_lock_info_list_t *list) {
+ erts_lcnt_lock_info_t *iterator;
+ lcnt_sample_vector_t result;
+ size_t allocated_entries;
+
+ allocated_entries = 64;
+ result.size = 0;
+
+ result.elements = erts_alloc(ERTS_ALC_T_LCNT_VECTOR,
+ allocated_entries * sizeof(lcnt_sample_t));
+
+ iterator = NULL;
+ while(erts_lcnt_iterate_list(list, &iterator)) {
+ erts_lcnt_retain_lock_info(iterator);
+
+ result.elements[result.size].max_location_count = iterator->location_count;
+ result.elements[result.size].info = iterator;
+
+ result.size++;
+
+ if(result.size >= allocated_entries) {
+ allocated_entries *= 2;
+
+ result.elements = erts_realloc(ERTS_ALC_T_LCNT_VECTOR, result.elements,
+ allocated_entries * sizeof(lcnt_sample_t));
+ }
+ }
+
+ return result;
+}
+
+static void lcnt_destroy_sample_vector(lcnt_sample_vector_t *vector) {
+ size_t i;
+
+ for(i = 0; i < vector->size; i++) {
+ erts_lcnt_release_lock_info(vector->elements[i].info);
+ }
+
+ erts_free(ERTS_ALC_T_LCNT_VECTOR, vector->elements);
+}
+
+/* The size of an integer is not guaranteed to be constant since we're walking
+ * over live data, and may cross over into bignum territory between size calc
+ * and the actual build. This takes care of that through always assuming the
+ * worst, but needs to be fixed up with HRelease once the final term has been
+ * built. */
+static ERTS_INLINE Eterm bld_unstable_uint64(Uint **hpp, Uint *szp, Uint64 ui) {
+ Eterm res = THE_NON_VALUE;
+
+ if(szp) {
+ *szp += ERTS_UINT64_HEAP_SIZE(~((Uint64) 0));
+ }
+
+ if(hpp) {
+ if (IS_USMALL(0, ui)) {
+ res = make_small(ui);
+ } else {
+ res = erts_uint64_to_big(ui, hpp);
+ }
+ }
+
+ return res;
+}
+
static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_stats_t *stats, Eterm res) {
- Uint tries = 0, colls = 0;
- unsigned long timer_s = 0, timer_ns = 0, timer_n = 0;
- unsigned int line = 0;
unsigned int i;
-
+ const char *file;
+
Eterm af, uil;
Eterm uit, uic;
Eterm uits, uitns, uitn;
Eterm tt, tstat, tloc, t;
Eterm thist, vhist[ERTS_LCNT_HISTOGRAM_SLOT_SIZE];
-
+
/* term:
- * [{{file, line}, {tries, colls, {seconds, nanoseconds, n_blocks}},
- * { .. histogram .. }]
- */
+ * [{{file, line},
+ {tries, colls, {seconds, nanoseconds, n_blocks}},
+ * { .. histogram .. }] */
- tries = (Uint) ethr_atomic_read(&stats->tries);
- colls = (Uint) ethr_atomic_read(&stats->colls);
-
- line = stats->line;
- timer_s = stats->timer.s;
- timer_ns = stats->timer.ns;
- timer_n = stats->timer_n;
-
- af = erts_atom_put((byte *)stats->file, strlen(stats->file), ERTS_ATOM_ENC_LATIN1, 1);
- uil = erts_bld_uint( hpp, szp, line);
+ file = stats->file ? stats->file : "undefined";
+
+ af = erts_atom_put((byte *)file, strlen(file), ERTS_ATOM_ENC_LATIN1, 1);
+ uil = erts_bld_uint( hpp, szp, stats->line);
tloc = erts_bld_tuple(hpp, szp, 2, af, uil);
-
- uit = erts_bld_uint( hpp, szp, tries);
- uic = erts_bld_uint( hpp, szp, colls);
- uits = erts_bld_uint( hpp, szp, timer_s);
- uitns = erts_bld_uint( hpp, szp, timer_ns);
- uitn = erts_bld_uint( hpp, szp, timer_n);
+ uit = bld_unstable_uint64(hpp, szp, (Uint)ethr_atomic_read(&stats->attempts));
+ uic = bld_unstable_uint64(hpp, szp, (Uint)ethr_atomic_read(&stats->collisions));
+
+ uits = bld_unstable_uint64(hpp, szp, stats->total_time_waited.s);
+ uitns = bld_unstable_uint64(hpp, szp, stats->total_time_waited.ns);
+ uitn = bld_unstable_uint64(hpp, szp, stats->times_waited);
tt = erts_bld_tuple(hpp, szp, 3, uits, uitns, uitn);
tstat = erts_bld_tuple(hpp, szp, 3, uit, uic, tt);
for(i = 0; i < ERTS_LCNT_HISTOGRAM_SLOT_SIZE; i++) {
- vhist[i] = erts_bld_uint(hpp, szp, stats->hist.ns[i]);
+ vhist[i] = bld_unstable_uint64(hpp, szp, stats->wait_time_histogram.ns[i]);
}
+
thist = erts_bld_tuplev(hpp, szp, ERTS_LCNT_HISTOGRAM_SLOT_SIZE, vhist);
t = erts_bld_tuple(hpp, szp, 3, tloc, tstat, thist);
@@ -4494,185 +4574,266 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s
return res;
}
-static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_t *lock, Eterm res) {
+static Eterm lcnt_pretty_print_lock_id(erts_lcnt_lock_info_t *info) {
+ Eterm id = info->id;
+
+ if((info->flags & ERTS_LOCK_FLAGS_MASK_TYPE) == ERTS_LOCK_TYPE_PROCLOCK) {
+ /* Use registered names as id's for process locks if available. Thread
+ * progress is delayed since we may be running on a dirty scheduler. */
+ ErtsThrPrgrDelayHandle delay_handle;
+ Process *process;
+
+ delay_handle = erts_thr_progress_unmanaged_delay();
+
+ process = erts_proc_lookup(info->id);
+ if (process && process->common.u.alive.reg) {
+ id = process->common.u.alive.reg->name;
+ }
+
+ erts_thr_progress_unmanaged_continue(delay_handle);
+ } else if(info->flags & ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR) {
+ if(is_small(id) && !sys_strcmp(info->name, "alcu_allocator")) {
+ const char *name = (const char*)ERTS_ALC_A2AD(signed_val(id));
+ id = erts_atom_put((byte*)name, strlen(name), ERTS_ATOM_ENC_LATIN1, 1);
+ }
+ }
+
+ return id;
+}
+
+static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, lcnt_sample_t *sample, Eterm res) {
+ erts_lcnt_lock_info_t *info = sample->info;
+
Eterm name, type, id, stats = NIL, t;
- Process *proc = NULL;
- char *ltype;
+ const char *lock_desc;
int i;
+
+ /* term: [{name, id, type, stats()}] */
+
+ ASSERT(info->name);
- /* term:
- * [{name, id, type, stats()}]
- */
-
- ASSERT(lock->name);
-
- ltype = erts_lcnt_lock_type(lock->flag);
-
- ASSERT(ltype);
-
- type = erts_atom_put((byte *)ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1);
- name = erts_atom_put((byte *)lock->name, strlen(lock->name), ERTS_ATOM_ENC_LATIN1, 1);
-
- if (lock->flag & ERTS_LCNT_LT_ALLOC) {
- /* use allocator types names as id's for allocator locks */
- ltype = (char *) ERTS_ALC_A2AD(signed_val(lock->id));
- id = erts_atom_put((byte *)ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1);
- } else if (lock->flag & ERTS_LCNT_LT_PROCLOCK) {
- /* use registered names as id's for process locks if available */
- proc = erts_proc_lookup(lock->id);
- if (proc && proc->common.u.alive.reg) {
- id = proc->common.u.alive.reg->name;
- } else {
- /* otherwise use process id */
- id = lock->id;
- }
+ lock_desc = erts_lock_flags_get_type_name(info->flags);
+
+ type = erts_atom_put((byte*)lock_desc, strlen(lock_desc), ERTS_ATOM_ENC_LATIN1, 1);
+ name = erts_atom_put((byte*)info->name, strlen(info->name), ERTS_ATOM_ENC_LATIN1, 1);
+
+ /* Only attempt to resolve ids when actually emitting the term. This ought
+ * to be safe since all immediates are the same size. */
+ if(hpp != NULL) {
+ id = lcnt_pretty_print_lock_id(info);
} else {
- id = lock->id;
+ id = NIL;
}
- for (i = 0; i < lock->n_stats; i++) {
- stats = lcnt_build_lock_stats_term(hpp, szp, &(lock->stats[i]), stats);
+ for(i = 0; i < MIN(info->location_count, sample->max_location_count); i++) {
+ stats = lcnt_build_lock_stats_term(hpp, szp, &(info->location_stats[i]), stats);
}
t = erts_bld_tuple(hpp, szp, 4, name, id, type, stats);
- res = erts_bld_cons( hpp, szp, t, res);
+ res = erts_bld_cons(hpp, szp, t, res);
return res;
}
-static Eterm lcnt_build_result_term(Eterm **hpp, Uint *szp, erts_lcnt_data_t *data, Eterm res) {
+static Eterm lcnt_build_result_term(Eterm **hpp, Uint *szp, erts_lcnt_time_t *duration,
+ lcnt_sample_vector_t *current_locks,
+ lcnt_sample_vector_t *deleted_locks, Eterm res) {
+ const char *str_duration = "duration";
+ const char *str_locks = "locks";
+
Eterm dts, dtns, tdt, adur, tdur, aloc, lloc = NIL, tloc;
- erts_lcnt_lock_t *lock = NULL;
- char *str_duration = "duration";
- char *str_locks = "locks";
-
- /* term:
- * [{'duration', {seconds, nanoseconds}}, {'locks', locks()}]
- */
-
+ size_t i;
+
+ /* term: [{'duration', {seconds, nanoseconds}}, {'locks', locks()}] */
+
/* duration tuple */
- dts = erts_bld_uint( hpp, szp, data->duration.s);
- dtns = erts_bld_uint( hpp, szp, data->duration.ns);
+ dts = bld_unstable_uint64(hpp, szp, duration->s);
+ dtns = bld_unstable_uint64(hpp, szp, duration->ns);
tdt = erts_bld_tuple(hpp, szp, 2, dts, dtns);
-
+
adur = erts_atom_put((byte *)str_duration, strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1);
tdur = erts_bld_tuple(hpp, szp, 2, adur, tdt);
/* lock tuple */
-
aloc = erts_atom_put((byte *)str_locks, strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1);
-
- for (lock = data->current_locks->head; lock != NULL ; lock = lock->next ) {
- lloc = lcnt_build_lock_term(hpp, szp, lock, lloc);
+
+ for(i = 0; i < current_locks->size; i++) {
+ lloc = lcnt_build_lock_term(hpp, szp, &current_locks->elements[i], lloc);
}
-
- for (lock = data->deleted_locks->head; lock != NULL ; lock = lock->next ) {
- lloc = lcnt_build_lock_term(hpp, szp, lock, lloc);
+
+ for(i = 0; i < deleted_locks->size; i++) {
+ lloc = lcnt_build_lock_term(hpp, szp, &deleted_locks->elements[i], lloc);
}
-
+
tloc = erts_bld_tuple(hpp, szp, 2, aloc, lloc);
-
- res = erts_bld_cons( hpp, szp, tloc, res);
- res = erts_bld_cons( hpp, szp, tdur, res);
+
+ res = erts_bld_cons(hpp, szp, tloc, res);
+ res = erts_bld_cons(hpp, szp, tdur, res);
return res;
-}
+}
+
+static struct {
+ const char *name;
+ erts_lock_flags_t flag;
+} lcnt_category_map[] = {
+ {"allocator", ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR},
+ {"db", ERTS_LOCK_FLAGS_CATEGORY_DB},
+ {"debug", ERTS_LOCK_FLAGS_CATEGORY_DEBUG},
+ {"distribution", ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION},
+ {"generic", ERTS_LOCK_FLAGS_CATEGORY_GENERIC},
+ {"io", ERTS_LOCK_FLAGS_CATEGORY_IO},
+ {"process", ERTS_LOCK_FLAGS_CATEGORY_PROCESS},
+ {"scheduler", ERTS_LOCK_FLAGS_CATEGORY_SCHEDULER},
+ {NULL, 0}
+ };
+
+static erts_lock_flags_t lcnt_atom_to_lock_category(Eterm atom) {
+ int i = 0;
+
+ for(i = 0; lcnt_category_map[i].name != NULL; i++) {
+ if(erts_is_atom_str(lcnt_category_map[i].name, atom, 0)) {
+ return lcnt_category_map[i].flag;
+ }
+ }
+
+ return 0;
+}
+
+static Eterm lcnt_build_category_list(Eterm **hpp, Uint *szp, erts_lock_flags_t mask) {
+ Eterm res;
+ int i;
+
+ res = NIL;
+
+ for(i = 0; lcnt_category_map[i].name != NULL; i++) {
+ if(mask & lcnt_category_map[i].flag) {
+ Eterm category = erts_atom_put((byte*)lcnt_category_map[i].name,
+ strlen(lcnt_category_map[i].name),
+ ERTS_ATOM_ENC_UTF8, 0);
+
+ res = erts_bld_cons(hpp, szp, category, res);
+ }
+ }
+
+ return res;
+}
+
#endif
-BIF_RETTYPE erts_debug_lock_counters_1(BIF_ALIST_1)
+BIF_RETTYPE erts_debug_lcnt_clear_0(BIF_ALIST_0)
{
-#ifdef ERTS_ENABLE_LOCK_COUNT
- Eterm res = NIL;
-#endif
+#ifndef ERTS_ENABLE_LOCK_COUNT
+ BIF_RET(am_error);
+#else
+ erts_lcnt_clear_counters();
+ BIF_RET(am_ok);
+#endif
+}
- if (BIF_ARG_1 == am_enabled) {
-#ifdef ERTS_ENABLE_LOCK_COUNT
- BIF_RET(am_true);
+BIF_RETTYPE erts_debug_lcnt_collect_0(BIF_ALIST_0)
+{
+#ifndef ERTS_ENABLE_LOCK_COUNT
+ BIF_RET(am_error);
#else
- BIF_RET(am_false);
-#endif
- }
-#ifdef ERTS_ENABLE_LOCK_COUNT
+ lcnt_sample_vector_t current_locks, deleted_locks;
+ erts_lcnt_data_t data;
- else if (BIF_ARG_1 == am_info) {
- erts_lcnt_data_t *data;
- Uint hsize = 0;
- Uint *szp;
- Eterm* hp;
+ Eterm *term_heap_start, *term_heap_end;
+ Uint term_heap_size = 0;
+ Eterm result;
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ data = erts_lcnt_get_data();
- erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_SUSPEND);
- data = erts_lcnt_get_data();
+ current_locks = lcnt_build_sample_vector(data.current_locks);
+ deleted_locks = lcnt_build_sample_vector(data.deleted_locks);
- /* calculate size */
+ lcnt_build_result_term(NULL, &term_heap_size, &data.duration,
+ &current_locks, &deleted_locks, NIL);
- szp = &hsize;
- lcnt_build_result_term(NULL, szp, data, NIL);
+ term_heap_start = HAlloc(BIF_P, term_heap_size);
+ term_heap_end = term_heap_start;
- /* alloc and build */
+ result = lcnt_build_result_term(&term_heap_end, NULL,
+ &data.duration, &current_locks, &deleted_locks, NIL);
- hp = HAlloc(BIF_P, hsize);
+ HRelease(BIF_P, term_heap_start + term_heap_size, term_heap_end);
- res = lcnt_build_result_term(&hp, NULL, data, res);
-
- erts_lcnt_clear_rt_opt(ERTS_LCNT_OPT_SUSPEND);
+ lcnt_destroy_sample_vector(&current_locks);
+ lcnt_destroy_sample_vector(&deleted_locks);
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
-
- BIF_RET(res);
- } else if (BIF_ARG_1 == am_clear) {
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ BIF_RET(result);
+#endif
+}
- erts_lcnt_clear_counters();
+BIF_RETTYPE erts_debug_lcnt_control_1(BIF_ALIST_1)
+{
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ if(ERTS_IS_ATOM_STR("mask", BIF_ARG_1)) {
+ erts_lock_flags_t mask;
+ Eterm *term_heap_block;
+ Uint term_heap_size;
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ mask = erts_lcnt_get_category_mask();
+ term_heap_size = 0;
- BIF_RET(am_ok);
- } else if (is_tuple(BIF_ARG_1)) {
- Eterm* ptr = tuple_val(BIF_ARG_1);
-
- if ((arityval(ptr[0]) == 2) && (ptr[2] == am_false || ptr[2] == am_true)) {
- int lock_opt = 0, enable = (ptr[2] == am_true) ? 1 : 0;
- if (ERTS_IS_ATOM_STR("copy_save", ptr[1])) {
- lock_opt = ERTS_LCNT_OPT_COPYSAVE;
- } else if (ERTS_IS_ATOM_STR("process_locks", ptr[1])) {
- lock_opt = ERTS_LCNT_OPT_PROCLOCK;
- } else if (ERTS_IS_ATOM_STR("port_locks", ptr[1])) {
- lock_opt = ERTS_LCNT_OPT_PORTLOCK;
- } else if (ERTS_IS_ATOM_STR("suspend", ptr[1])) {
- lock_opt = ERTS_LCNT_OPT_SUSPEND;
- } else if (ERTS_IS_ATOM_STR("location", ptr[1])) {
- lock_opt = ERTS_LCNT_OPT_LOCATION;
- } else {
- BIF_ERROR(BIF_P, BADARG);
- }
+ lcnt_build_category_list(NULL, &term_heap_size, mask);
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ term_heap_block = HAlloc(BIF_P, term_heap_size);
- if (enable) res = erts_lcnt_set_rt_opt(lock_opt) ? am_true : am_false;
- else res = erts_lcnt_clear_rt_opt(lock_opt) ? am_true : am_false;
-
-#ifdef ERTS_SMP
- if (res != ptr[2] && lock_opt == ERTS_LCNT_OPT_PORTLOCK) {
- erts_lcnt_enable_io_lock_count(enable);
- } else if (res != ptr[2] && lock_opt == ERTS_LCNT_OPT_PROCLOCK) {
- erts_lcnt_enable_proc_lock_count(enable);
- }
+ BIF_RET(lcnt_build_category_list(&term_heap_block, NULL, mask));
+ } else if(ERTS_IS_ATOM_STR("copy_save", BIF_ARG_1)) {
+ if(erts_lcnt_get_preserve_info()) {
+ BIF_RET(am_true);
+ }
+
+ BIF_RET(am_false);
+ }
#endif
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- BIF_RET(res);
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+BIF_RETTYPE erts_debug_lcnt_control_2(BIF_ALIST_2)
+{
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ if(ERTS_IS_ATOM_STR("mask", BIF_ARG_1)) {
+ erts_lock_flags_t category_mask = 0;
+ Eterm categories = BIF_ARG_2;
+
+ if(!(is_list(categories) || is_nil(categories))) {
+ BIF_ERROR(BIF_P, BADARG);
}
- }
-#endif
+ while(is_list(categories)) {
+ Eterm *cell = list_val(categories);
+ erts_lock_flags_t category;
+
+ category = lcnt_atom_to_lock_category(CAR(cell));
+
+ if(!category) {
+ Eterm *hp = HAlloc(BIF_P, 4);
+
+ BIF_RET(TUPLE3(hp, am_error, am_badarg, CAR(cell)));
+ }
+
+ category_mask |= category;
+ categories = CDR(cell);
+ }
+
+ erts_lcnt_set_category_mask(category_mask);
+
+ BIF_RET(am_ok);
+ } else if(BIF_ARG_2 == am_true || BIF_ARG_2 == am_false) {
+ int enabled = (BIF_ARG_2 == am_true);
+
+ if(ERTS_IS_ATOM_STR("copy_save", BIF_ARG_1)) {
+ erts_lcnt_set_preserve_info(enabled);
+
+ BIF_RET(am_ok);
+ }
+ }
+#endif
BIF_ERROR(BIF_P, BADARG);
}
diff --git a/erts/emulator/beam/erl_bif_unique.c b/erts/emulator/beam/erl_bif_unique.c
index fc6fb5f868..2f8adc87d5 100644
--- a/erts/emulator/beam/erl_bif_unique.c
+++ b/erts/emulator/beam/erl_bif_unique.c
@@ -392,7 +392,8 @@ init_magic_ref_tables(void)
erts_snprintf(&tblp->name[0], sizeof(tblp->name),
"magic_ref_table_0");
hash_init(0, &tblp->hash, &tblp->name[0], 1, hash_funcs);
- erts_rwmtx_init(&tblp->rwmtx, "magic_ref_table");
+ erts_rwmtx_init(&tblp->rwmtx, "magic_ref_table", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
hash_funcs.hash = nsched_mreft_hash;
hash_funcs.cmp = nsched_mreft_cmp;
@@ -402,7 +403,8 @@ init_magic_ref_tables(void)
erts_snprintf(&tblp->name[0], sizeof(tblp->name),
"magic_ref_table_%d", i);
hash_init(0, &tblp->hash, &tblp->name[0], 1, hash_funcs);
- erts_rwmtx_init(&tblp->rwmtx, "magic_ref_table");
+ erts_rwmtx_init(&tblp->rwmtx, "magic_ref_table", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
}
}
diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c
index d0fd763798..f8b2fa744f 100644
--- a/erts/emulator/beam/erl_cpu_topology.c
+++ b/erts/emulator/beam/erl_cpu_topology.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2010-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.
@@ -1706,7 +1706,8 @@ erts_init_cpu_topology(void)
{
int ix;
- erts_smp_rwmtx_init(&cpuinfo_rwmtx, "cpu_info");
+ erts_smp_rwmtx_init(&cpuinfo_rwmtx, "cpu_info", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
erts_smp_rwmtx_rwlock(&cpuinfo_rwmtx);
scheduler2cpu_map = erts_alloc(ERTS_ALC_T_CPUDATA,
diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h
index cf139d95a9..c922214702 100644
--- a/erts/emulator/beam/erl_cpu_topology.h
+++ b/erts/emulator/beam/erl_cpu_topology.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2010-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/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 17e0f2aeec..b83134c79c 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -575,9 +575,7 @@ delete_owned_table(Process *p, DbTable *tb)
table_dec_refc(tb, 1);
}
-
-static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock,
- char *rwname, char* fixname)
+static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock)
{
#ifdef ERTS_SMP
erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
@@ -587,9 +585,10 @@ static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock,
rwmtx_opt.main_spincount = erts_ets_rwmtx_spin_count;
#endif
#ifdef ERTS_SMP
- erts_smp_rwmtx_init_opt_x(&tb->common.rwlock, &rwmtx_opt,
- rwname, tb->common.the_name);
- erts_smp_mtx_init_x(&tb->common.fixlock, fixname, tb->common.the_name);
+ erts_smp_rwmtx_init_opt(&tb->common.rwlock, &rwmtx_opt, "db_tab",
+ tb->common.the_name, ERTS_LOCK_FLAGS_CATEGORY_DB);
+ erts_smp_mtx_init(&tb->common.fixlock, "db_tab_fix",
+ tb->common.the_name, ERTS_LOCK_FLAGS_CATEGORY_DB);
tb->common.is_thread_safe = !(tb->common.status & DB_FINE_LOCKED);
#endif
}
@@ -1753,8 +1752,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
/* Note, 'type' is *read only* from now on... */
#endif
erts_smp_refc_init(&tb->common.fix_count, 0);
- db_init_lock(tb, status & (DB_FINE_LOCKED|DB_FREQ_READ),
- "db_tab", "db_tab_fix");
+ db_init_lock(tb, status & (DB_FINE_LOCKED|DB_FREQ_READ));
tb->common.keypos = keypos;
tb->common.owner = BIF_P->common.id;
set_heir(BIF_P, tb, heir, heir_data);
@@ -3391,8 +3389,9 @@ void init_db(ErtsDbSpinCount db_spin_count)
rwmtx_opt.main_spincount = erts_ets_rwmtx_spin_count;
for (i=0; i<META_NAME_TAB_LOCK_CNT; i++) {
- erts_smp_rwmtx_init_opt_x(&meta_name_tab_rwlocks[i].lck, &rwmtx_opt,
- "meta_name_tab", make_small(i));
+ erts_smp_rwmtx_init_opt(&meta_name_tab_rwlocks[i].lck, &rwmtx_opt,
+ "meta_name_tab", make_small(i),
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DB);
}
#endif
@@ -4334,3 +4333,47 @@ erts_ets_colliding_names(Process* p, Eterm name, Uint cnt)
return list;
}
+#ifdef ERTS_ENABLE_LOCK_COUNT
+
+void erts_lcnt_enable_db_lock_count(DbTable *tb, int enable) {
+ if(enable) {
+ erts_lcnt_install_new_lock_info(&tb->common.rwlock.lcnt, "db_tab",
+ tb->common.the_name, ERTS_LOCK_TYPE_RWMUTEX | ERTS_LOCK_FLAGS_CATEGORY_DB);
+ erts_lcnt_install_new_lock_info(&tb->common.fixlock.lcnt, "db_tab_fix",
+ tb->common.the_name, ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DB);
+ } else {
+ erts_lcnt_uninstall(&tb->common.rwlock.lcnt);
+ erts_lcnt_uninstall(&tb->common.fixlock.lcnt);
+ }
+
+ if(IS_HASH_TABLE(tb->common.status)) {
+ erts_lcnt_enable_db_hash_lock_count(&tb->hash, enable);
+ }
+}
+
+static void lcnt_update_db_locks_per_sched(void *enable) {
+ ErtsSchedulerData *esdp;
+ DbTable *head;
+
+ esdp = erts_get_scheduler_data();
+ head = esdp->ets_tables.clist;
+
+ if(head) {
+ DbTable *iterator = head;
+
+ do {
+ if(is_table_alive(iterator)) {
+ erts_lcnt_enable_db_lock_count(iterator, !!enable);
+ }
+
+ iterator = iterator->common.all.next;
+ } while (iterator != head);
+ }
+}
+
+void erts_lcnt_update_db_locks(int enable) {
+ erts_schedule_multi_misc_aux_work(0, erts_no_schedulers,
+ &lcnt_update_db_locks_per_sched, (void*)(UWord)enable);
+}
+
+#endif /* ERTS_ENABLE_LOCK_COUNT */ \ No newline at end of file
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index 4ff9f224e8..d83126b3a2 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -129,6 +129,11 @@ extern erts_smp_atomic_t erts_ets_misc_mem_size;
Eterm erts_ets_colliding_names(Process*, Eterm name, Uint cnt);
Uint erts_db_get_max_tabs(void);
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void erts_lcnt_enable_db_lock_count(DbTable *tb, int enable);
+void erts_lcnt_update_db_locks(int enable);
+#endif
+
#endif /* ERL_DB_H__ */
#if defined(ERTS_WANT_DB_INTERNAL__) && !defined(ERTS_HAVE_DB_INTERNAL__)
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index 0addfaa3c7..ae9322dfd3 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -675,8 +675,8 @@ int db_create_hash(Process *p, DbTable *tbl)
(DbTable *) tb,
sizeof(DbTableHashFineLocks));
for (i=0; i<DB_HASH_LOCK_CNT; ++i) {
- erts_smp_rwmtx_init_opt_x(&tb->locks->lck_vec[i].lck, &rwmtx_opt,
- "db_hash_slot", tb->common.the_name);
+ erts_smp_rwmtx_init_opt(&tb->locks->lck_vec[i].lck, &rwmtx_opt,
+ "db_hash_slot", tb->common.the_name, ERTS_LOCK_FLAGS_CATEGORY_DB);
}
/* This important property is needed to guarantee the two buckets
* involved in a grow/shrink operation it protected by the same lock:
@@ -3206,3 +3206,23 @@ Eterm erts_ets_hash_sizeof_ext_segtab(void)
return make_small(((SIZEOF_EXT_SEGTAB(0)-1) / sizeof(UWord)) + 1);
}
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void erts_lcnt_enable_db_hash_lock_count(DbTableHash *tb, int enable) {
+ int i;
+
+ if(tb->locks == NULL) {
+ return;
+ }
+
+ for(i = 0; i < DB_HASH_LOCK_CNT; i++) {
+ erts_lcnt_ref_t *ref = &tb->locks->lck_vec[i].lck.lcnt;
+
+ if(enable) {
+ erts_lcnt_install_new_lock_info(ref, "db_hash_slot", tb->common.the_name,
+ ERTS_LOCK_TYPE_RWMUTEX | ERTS_LOCK_FLAGS_CATEGORY_DB);
+ } else {
+ erts_lcnt_uninstall(ref);
+ }
+ }
+}
+#endif /* ERTS_ENABLE_LOCK_COUNT */
diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h
index f491c85d95..523ed7860e 100644
--- a/erts/emulator/beam/erl_db_hash.h
+++ b/erts/emulator/beam/erl_db_hash.h
@@ -103,4 +103,8 @@ typedef struct {
void db_calc_stats_hash(DbTableHash* tb, DbHashStats*);
Eterm erts_ets_hash_sizeof_ext_segtab(void);
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void erts_lcnt_enable_db_hash_lock_count(DbTableHash *tb, int enable);
+#endif
+
#endif /* _DB_HASH_H */
diff --git a/erts/emulator/beam/erl_dirty_bif.tab b/erts/emulator/beam/erl_dirty_bif.tab
index 69421dcfcc..10c76d2579 100644
--- a/erts/emulator/beam/erl_dirty_bif.tab
+++ b/erts/emulator/beam/erl_dirty_bif.tab
@@ -46,6 +46,11 @@
dirty-cpu erts_debug:dirty_cpu/2
dirty-io erts_debug:dirty_io/2
+# lcnt_control/1 doesn't need to be dirty.
+dirty-cpu erts_debug:lcnt_control/2
+dirty-cpu erts_debug:lcnt_collect/0
+dirty-cpu erts_debug:lcnt_clear/0
+
# --- TEST of Dirty BIF functionality ---
# Functions below will execute on dirty schedulers when emulator has
# been configured for testing dirty schedulers. This is used for test
diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c
index 0e6aadf568..49bbab55a8 100644
--- a/erts/emulator/beam/erl_drv_thread.c
+++ b/erts/emulator/beam/erl_drv_thread.c
@@ -55,7 +55,7 @@ fatal_error(int err, char *func)
struct ErlDrvMutex_ {
ethr_mutex mtx;
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_t lcnt;
+ erts_lcnt_ref_t lcnt;
#endif
char *name;
};
@@ -68,7 +68,7 @@ struct ErlDrvCond_ {
struct ErlDrvRWLock_ {
ethr_rwmutex rwmtx;
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_t lcnt;
+ erts_lcnt_ref_t lcnt;
#endif
char *name;
};
@@ -146,7 +146,8 @@ void erl_drv_thr_init(void)
sizeof(char *)*ERL_DRV_TSD_KEYS_INC);
for (i = 0; i < ERL_DRV_TSD_KEYS_INC; i++)
used_tsd_keys[i] = NULL;
- erts_mtx_init(&tsd_mtx, "drv_tsd");
+ erts_mtx_init(&tsd_mtx, "drv_tsd", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
}
/*
@@ -176,7 +177,8 @@ erl_drv_mutex_create(char *name)
dmtx->name = no_name;
}
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock(&dmtx->lcnt, dmtx->name, ERTS_LCNT_LT_MUTEX);
+ erts_lcnt_init_ref_x(&dmtx->lcnt, dmtx->name, NIL,
+ ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_IO);
#endif
}
return dmtx;
@@ -191,7 +193,7 @@ erl_drv_mutex_destroy(ErlDrvMutex *dmtx)
#ifdef USE_THREADS
int res;
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_destroy_lock(&dmtx->lcnt);
+ erts_lcnt_uninstall(&dmtx->lcnt);
#endif
res = dmtx ? ethr_mutex_destroy(&dmtx->mtx) : EINVAL;
if (res != 0)
@@ -368,7 +370,8 @@ erl_drv_rwlock_create(char *name)
drwlck->name = no_name;
}
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock(&drwlck->lcnt, drwlck->name, ERTS_LCNT_LT_RWMUTEX);
+ erts_lcnt_init_ref_x(&drwlck->lcnt, drwlck->name, NIL,
+ ERTS_LOCK_TYPE_RWMUTEX | ERTS_LOCK_FLAGS_CATEGORY_IO);
#endif
}
return drwlck;
@@ -383,7 +386,7 @@ erl_drv_rwlock_destroy(ErlDrvRWLock *drwlck)
#ifdef USE_THREADS
int res;
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_destroy_lock(&drwlck->lcnt);
+ erts_lcnt_uninstall(&drwlck->lcnt);
#endif
res = drwlck ? ethr_rwmutex_destroy(&drwlck->rwmtx) : EINVAL;
if (res != 0)
@@ -411,7 +414,7 @@ erl_drv_rwlock_tryrlock(ErlDrvRWLock *drwlck)
fatal_error(EINVAL, "erl_drv_rwlock_tryrlock()");
res = ethr_rwmutex_tryrlock(&drwlck->rwmtx);
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_trylock_opt(&drwlck->lcnt, res, ERTS_LCNT_LO_READ);
+ erts_lcnt_trylock_opt(&drwlck->lcnt, res, ERTS_LOCK_OPTION_READ);
#endif
return res;
#else
@@ -426,7 +429,7 @@ erl_drv_rwlock_rlock(ErlDrvRWLock *drwlck)
if (!drwlck)
fatal_error(EINVAL, "erl_drv_rwlock_rlock()");
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_opt(&drwlck->lcnt, ERTS_LCNT_LO_READ);
+ erts_lcnt_lock_opt(&drwlck->lcnt, ERTS_LOCK_OPTION_READ);
#endif
ethr_rwmutex_rlock(&drwlck->rwmtx);
#ifdef ERTS_ENABLE_LOCK_COUNT
@@ -442,7 +445,7 @@ erl_drv_rwlock_runlock(ErlDrvRWLock *drwlck)
if (!drwlck)
fatal_error(EINVAL, "erl_drv_rwlock_runlock()");
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_unlock_opt(&drwlck->lcnt, ERTS_LCNT_LO_READ);
+ erts_lcnt_unlock_opt(&drwlck->lcnt, ERTS_LOCK_OPTION_READ);
#endif
ethr_rwmutex_runlock(&drwlck->rwmtx);
#endif
@@ -457,7 +460,7 @@ erl_drv_rwlock_tryrwlock(ErlDrvRWLock *drwlck)
fatal_error(EINVAL, "erl_drv_rwlock_tryrwlock()");
res = ethr_rwmutex_tryrwlock(&drwlck->rwmtx);
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_trylock_opt(&drwlck->lcnt, res, ERTS_LCNT_LO_READ_WRITE);
+ erts_lcnt_trylock_opt(&drwlck->lcnt, res, ERTS_LOCK_OPTION_RDWR);
#endif
return res;
#else
@@ -472,7 +475,7 @@ erl_drv_rwlock_rwlock(ErlDrvRWLock *drwlck)
if (!drwlck)
fatal_error(EINVAL, "erl_drv_rwlock_rwlock()");
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_opt(&drwlck->lcnt, ERTS_LCNT_LO_READ_WRITE);
+ erts_lcnt_lock_opt(&drwlck->lcnt, ERTS_LOCK_OPTION_RDWR);
#endif
ethr_rwmutex_rwlock(&drwlck->rwmtx);
#ifdef ERTS_ENABLE_LOCK_COUNT
@@ -488,7 +491,7 @@ erl_drv_rwlock_rwunlock(ErlDrvRWLock *drwlck)
if (!drwlck)
fatal_error(EINVAL, "erl_drv_rwlock_rwunlock()");
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_unlock_opt(&drwlck->lcnt, ERTS_LCNT_LO_READ_WRITE);
+ erts_lcnt_unlock_opt(&drwlck->lcnt, ERTS_LOCK_OPTION_RDWR);
#endif
ethr_rwmutex_rwunlock(&drwlck->rwmtx);
#endif
diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c
index d18016c42e..535f677bb3 100644
--- a/erts/emulator/beam/erl_fun.c
+++ b/erts/emulator/beam/erl_fun.c
@@ -63,7 +63,8 @@ erts_init_fun_table(void)
rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ;
rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED;
- erts_smp_rwmtx_init_opt(&erts_fun_table_lock, &rwmtx_opt, "fun_tab");
+ erts_smp_rwmtx_init_opt(&erts_fun_table_lock, &rwmtx_opt, "fun_tab", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
f.hash = (H_FUN) fun_hash;
f.cmp = (HCMP_FUN) fun_cmp;
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 3c8bdaa62e..8cb977a7f3 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -274,7 +274,8 @@ erts_init_gc(void)
}
#ifdef ERTS_DIRTY_SCHEDULERS
- erts_smp_mtx_init(&dirty_gc.mtx, "dirty_gc_info");
+ erts_smp_mtx_init(&dirty_gc.mtx, "dirty_gc_info", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
init_gc_info(&dirty_gc.info);
#endif
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 6172595552..5206d7564f 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -2357,8 +2357,12 @@ erl_start(int argc, char **argv)
#ifdef ERTS_SMP
erts_start_schedulers();
- /* Let system specific code decide what to do with the main thread... */
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_post_startup();
+#endif
+
+ /* Let system specific code decide what to do with the main thread... */
erts_sys_main_thread(); /* May or may not return! */
#else
{
@@ -2373,6 +2377,11 @@ erl_start(int argc, char **argv)
erts_sched_init_time_sup(esdp);
erts_ets_sched_spec_data_init(esdp);
erts_aux_work_timeout_late_init(esdp);
+
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_post_startup();
+#endif
+
process_main(esdp->x_reg_array, esdp->f_reg_array);
}
#endif
diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c
index 4d4defd8b5..634509f880 100644
--- a/erts/emulator/beam/erl_instrument.c
+++ b/erts/emulator/beam/erl_instrument.c
@@ -1200,7 +1200,8 @@ erts_instr_init(int stat, int map_stat)
stats = erts_alloc(ERTS_ALC_T_INSTR_INFO, sizeof(struct stats_));
- erts_mtx_init(&instr_mutex, "instr");
+ erts_mtx_init(&instr_mutex, "instr", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
mem_anchor = NULL;
@@ -1223,7 +1224,8 @@ erts_instr_init(int stat, int map_stat)
if (map_stat) {
- erts_mtx_init(&instr_x_mutex, "instr_x");
+ erts_mtx_init(&instr_x_mutex, "instr_x", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
erts_instr_memory_map = 1;
erts_instr_stat = 1;
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index f270d8baef..85ee703c99 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -104,7 +104,6 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "db_tab", "address" },
{ "proc_status", "pid" },
{ "proc_trace", "pid" },
- { "ports_snapshot", NULL },
{ "db_tab_fix", "address" },
{ "db_hash_slot", "address" },
{ "node_table", NULL },
@@ -161,6 +160,9 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "mtrace_op", NULL },
{ "instr_x", NULL },
{ "instr", NULL },
+#ifdef ERTS_SMP
+ { "pollsets_lock", NULL },
+#endif
{ "alcu_allocator", "index" },
{ "mseg", NULL },
#ifdef ERTS_SMP
@@ -173,7 +175,6 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "get_time", NULL },
{ "get_corrected_time", NULL },
{ "breakpoints", NULL },
- { "pollsets_lock", NULL },
{ "pix_lock", "address" },
{ "run_queues_lists", NULL },
{ "sched_stat", NULL },
diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c
index aee9796171..d2e8f47d59 100644
--- a/erts/emulator/beam/erl_lock_count.c
+++ b/erts/emulator/beam/erl_lock_count.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2008-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.
@@ -18,51 +18,37 @@
* %CopyrightEnd%
*/
-/*
- * Description: Statistics for locks.
- *
- * Author: Björn-Egil Dahlberg
- * Date: 2008-07-03
- */
-
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-/* Needed for VxWorks va_arg */
+#ifdef ERTS_ENABLE_LOCK_COUNT
+
#include "sys.h"
-#ifdef ERTS_ENABLE_LOCK_COUNT
+#include "global.h"
#include "erl_lock_count.h"
-#include "ethread.h"
-#include "erl_term.h"
-#include "atom.h"
-#include <stdio.h>
-
-/* globals, dont access these without locks or blocks */
+#include "erl_thr_progress.h"
-ethr_mutex lcnt_data_lock;
-erts_lcnt_data_t *erts_lcnt_data;
-Uint16 erts_lcnt_rt_options;
-erts_lcnt_time_t timer_start;
-const char *str_undefined = "undefined";
+#include "erl_node_tables.h"
+#include "erl_alloc_util.h"
+#include "erl_check_io.h"
+#include "erl_poll.h"
+#include "erl_db.h"
-static ethr_tsd_key lcnt_thr_data_key;
-static int lcnt_n_thr;
-static erts_lcnt_thread_data_t *lcnt_thread_data[2048];
+#define LCNT_MAX_CARRIER_ENTRIES 255
-/* local functions */
+/* - Locals that are shared with the header implementation - */
-static ERTS_INLINE void lcnt_lock(void) {
- ethr_mutex_lock(&lcnt_data_lock);
-}
+#ifdef DEBUG
+int lcnt_initialization_completed__;
+#endif
-static ERTS_INLINE void lcnt_unlock(void) {
- ethr_mutex_unlock(&lcnt_data_lock);
-}
+erts_lock_flags_t lcnt_category_mask__;
+ethr_tsd_key lcnt_thr_data_key__;
-const int log2_tab64[64] = {
+const int lcnt_log2_tab64__[64] = {
63, 0, 58, 1, 59, 47, 53, 2,
60, 39, 48, 27, 54, 33, 42, 3,
61, 51, 37, 40, 49, 18, 28, 20,
@@ -72,635 +58,624 @@ const int log2_tab64[64] = {
56, 45, 25, 31, 35, 16, 9, 12,
44, 24, 15, 8, 23, 7, 6, 5};
-static ERTS_INLINE int lcnt_log2(Uint64 v) {
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- v |= v >> 32;
- return log2_tab64[((Uint64)((v - (v >> 1))*0x07EDD5E59A4E28C2)) >> 58];
-}
-
-static char* lcnt_lock_type(Uint16 flag) {
- switch(flag & ERTS_LCNT_LT_ALL) {
- case ERTS_LCNT_LT_SPINLOCK: return "spinlock";
- case ERTS_LCNT_LT_RWSPINLOCK: return "rw_spinlock";
- case ERTS_LCNT_LT_MUTEX: return "mutex";
- case ERTS_LCNT_LT_RWMUTEX: return "rw_mutex";
- case ERTS_LCNT_LT_PROCLOCK: return "proclock";
- default: return "";
- }
-}
+/* - Local variables - */
-static void lcnt_clear_stats(erts_lcnt_lock_stats_t *stats) {
- ethr_atomic_set(&stats->tries, 0);
- ethr_atomic_set(&stats->colls, 0);
- stats->timer.s = 0;
- stats->timer.ns = 0;
- stats->timer_n = 0;
- stats->file = (char *)str_undefined;
- stats->line = 0;
- sys_memzero(stats->hist.ns, sizeof(stats->hist.ns));
-}
+typedef struct lcnt_static_lock_ref_ {
+ erts_lcnt_ref_t *reference;
-static void lcnt_time(erts_lcnt_time_t *time) {
- /*
- * erts_sys_hrtime() is the highest resolution
- * we could find, it may or may not be monotonic...
- */
- ErtsMonotonicTime mtime = erts_sys_hrtime();
- time->s = (unsigned long) (mtime / 1000000000LL);
- time->ns = (unsigned long) (mtime - 1000000000LL*time->s);
-}
+ erts_lock_flags_t flags;
+ const char *name;
+ Eterm id;
-static void lcnt_time_diff(erts_lcnt_time_t *d, erts_lcnt_time_t *t1, erts_lcnt_time_t *t0) {
- long ds;
- long dns;
+ struct lcnt_static_lock_ref_ *next;
+} lcnt_static_lock_ref_t;
- ds = t1->s - t0->s;
- dns = t1->ns - t0->ns;
+static ethr_atomic_t lcnt_static_lock_registry;
- /* the difference should not be able to get bigger than 1 sec in ns*/
+static erts_lcnt_lock_info_list_t lcnt_current_lock_list;
+static erts_lcnt_lock_info_list_t lcnt_deleted_lock_list;
- if (dns < 0) {
- ds -= 1;
- dns += 1000000000LL;
- }
+static erts_lcnt_time_t lcnt_timer_start;
- ASSERT(ds >= 0);
+static int lcnt_preserve_info;
- d->s = ds;
- d->ns = dns;
-}
+/* local functions */
+
+static void lcnt_clear_stats(erts_lcnt_lock_info_t *info) {
+ size_t i;
+
+ for(i = 0; i < ERTS_LCNT_MAX_LOCK_LOCATIONS; i++) {
+ erts_lcnt_lock_stats_t *stats = &info->location_stats[i];
-/* difference d must be non-negative */
+ sys_memzero(&stats->wait_time_histogram, sizeof(stats->wait_time_histogram));
-static void lcnt_time_add(erts_lcnt_time_t *t, erts_lcnt_time_t *d) {
- t->s += d->s;
- t->ns += d->ns;
+ stats->total_time_waited.s = 0;
+ stats->total_time_waited.ns = 0;
- t->s += t->ns / 1000000000LL;
- t->ns = t->ns % 1000000000LL;
+ stats->times_waited = 0;
+
+ stats->file = NULL;
+ stats->line = 0;
+
+ ethr_atomic_set(&stats->attempts, 0);
+ ethr_atomic_set(&stats->collisions, 0);
+ }
+
+ info->location_count = 1;
}
-static erts_lcnt_thread_data_t *lcnt_thread_data_alloc(void) {
- erts_lcnt_thread_data_t *eltd;
+static lcnt_thread_data_t__ *lcnt_thread_data_alloc(void) {
+ lcnt_thread_data_t__ *eltd =
+ (lcnt_thread_data_t__*)malloc(sizeof(lcnt_thread_data_t__));
- eltd = (erts_lcnt_thread_data_t*)malloc(sizeof(erts_lcnt_thread_data_t));
- if (!eltd) {
- ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!");
+ if(!eltd) {
+ ERTS_INTERNAL_ERROR("Failed to allocate lcnt thread data.");
}
+
eltd->timer_set = 0;
eltd->lock_in_conflict = 0;
- eltd->id = lcnt_n_thr++;
- /* set thread data to array */
- lcnt_thread_data[eltd->id] = eltd;
-
return eltd;
}
-static erts_lcnt_thread_data_t *lcnt_get_thread_data(void) {
- return (erts_lcnt_thread_data_t *)ethr_tsd_get(lcnt_thr_data_key);
-}
+/* - List operations -
+ *
+ * Info entries are kept in a doubly linked list where each entry is locked
+ * with its neighbors rather than a global lock. Deletion is rather quick, but
+ * insertion is still serial since the head becomes a de facto global lock.
+ *
+ * We rely on ad-hoc spinlocks to avoid "recursing" into this module. */
-/* debug */
+#define LCNT_SPINLOCK_YIELD_ITERATIONS 50
-#if 0
-static char* lock_opt(Uint16 flag) {
- if ((flag & ERTS_LCNT_LO_WRITE) && (flag & ERTS_LCNT_LO_READ)) return "rw";
- if (flag & ERTS_LCNT_LO_READ ) return "r ";
- if (flag & ERTS_LCNT_LO_WRITE) return " w";
- return "--";
-}
+#define LCNT_SPINLOCK_HELPER_INIT \
+ Uint failed_spin_count = 0;
-static void print_lock_x(erts_lcnt_lock_t *lock, Uint16 flag, char *action) {
- erts_aint_t w_state, r_state;
- char *type;
+#define LCNT_SPINLOCK_HELPER_YIELD \
+ do { \
+ failed_spin_count++; \
+ if(!(failed_spin_count % LCNT_SPINLOCK_YIELD_ITERATIONS)) { \
+ erts_thr_yield(); \
+ } else { \
+ ERTS_SPIN_BODY; \
+ } \
+ } while(0)
- if (strcmp(lock->name, "run_queue") != 0) return;
- type = lcnt_lock_type(lock->flag);
- r_state = ethr_atomic_read(&lock->r_state);
- w_state = ethr_atomic_read(&lock->w_state);
+static void lcnt_unlock_list_entry(erts_lcnt_lock_info_t *info) {
+ ethr_atomic32_set_relb(&info->lock, 0);
+}
- if (lock->flag & flag) {
- erts_fprintf(stderr,"%10s [%24s] [r/w state %4ld/%4ld] %2s id %T\r\n",
- action,
- lock->name,
- r_state,
- w_state,
- type,
- lock->id);
- }
+static int lcnt_try_lock_list_entry(erts_lcnt_lock_info_t *info) {
+ return ethr_atomic32_cmpxchg_acqb(&info->lock, 1, 0) == 0;
}
-#endif
-static erts_lcnt_lock_stats_t *lcnt_get_lock_stats(erts_lcnt_lock_t *lock, char *file, unsigned int line) {
- unsigned int i;
- erts_lcnt_lock_stats_t *stats = NULL;
+static void lcnt_lock_list_entry(erts_lcnt_lock_info_t *info) {
+ LCNT_SPINLOCK_HELPER_INIT;
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_LOCATION) {
- for (i = 0; i < lock->n_stats; i++) {
- if ((lock->stats[i].file == file) && (lock->stats[i].line == line)) {
- return &(lock->stats[i]);
- }
- }
- if (lock->n_stats < ERTS_LCNT_MAX_LOCK_LOCATIONS) {
- stats = &lock->stats[lock->n_stats];
- lock->n_stats++;
- stats->file = file;
- stats->line = line;
- return stats;
- }
+ while(!lcnt_try_lock_list_entry(info)) {
+ LCNT_SPINLOCK_HELPER_YIELD;
}
- return &lock->stats[0];
}
-static void lcnt_update_stats_hist(erts_lcnt_hist_t *hist, erts_lcnt_time_t *time_wait) {
- int idx;
- unsigned long r;
+static void lcnt_lock_list_entry_with_neighbors(erts_lcnt_lock_info_t *info) {
+ LCNT_SPINLOCK_HELPER_INIT;
- if (time_wait->s > 0 || time_wait->ns > ERTS_LCNT_HISTOGRAM_MAX_NS) {
- idx = ERTS_LCNT_HISTOGRAM_SLOT_SIZE - 1;
- } else {
- r = time_wait->ns >> ERTS_LCNT_HISTOGRAM_RSHIFT;
- if (r) idx = lcnt_log2(r);
- else idx = 0;
+ for(;;) {
+ if(!lcnt_try_lock_list_entry(info))
+ goto retry_after_entry_failed;
+ if(!lcnt_try_lock_list_entry(info->next))
+ goto retry_after_next_failed;
+ if(!lcnt_try_lock_list_entry(info->prev))
+ goto retry_after_prev_failed;
+
+ return;
+
+ retry_after_prev_failed:
+ lcnt_unlock_list_entry(info->next);
+ retry_after_next_failed:
+ lcnt_unlock_list_entry(info);
+ retry_after_entry_failed:
+ LCNT_SPINLOCK_HELPER_YIELD;
}
- hist->ns[idx]++;
}
-static void lcnt_update_stats(erts_lcnt_lock_stats_t *stats, int lock_in_conflict,
- erts_lcnt_time_t *time_wait) {
+static void lcnt_unlock_list_entry_with_neighbors(erts_lcnt_lock_info_t *info) {
+ lcnt_unlock_list_entry(info->prev);
+ lcnt_unlock_list_entry(info->next);
+ lcnt_unlock_list_entry(info);
+}
- ethr_atomic_inc(&stats->tries);
+static void lcnt_insert_list_entry(erts_lcnt_lock_info_list_t *list, erts_lcnt_lock_info_t *info) {
+ erts_lcnt_lock_info_t *next, *prev;
- if (lock_in_conflict)
- ethr_atomic_inc(&stats->colls);
+ prev = &list->head;
- if (time_wait) {
- lcnt_time_add(&(stats->timer), time_wait);
- stats->timer_n++;
- lcnt_update_stats_hist(&stats->hist,time_wait);
- }
-}
+ lcnt_lock_list_entry(prev);
-/* interface */
+ next = prev->next;
-void erts_lcnt_init() {
- erts_lcnt_thread_data_t *eltd = NULL;
+ lcnt_lock_list_entry(next);
- /* init lock */
- if (ethr_mutex_init(&lcnt_data_lock) != 0) abort();
+ info->next = next;
+ info->prev = prev;
- /* init tsd */
- lcnt_n_thr = 0;
- ethr_tsd_key_create(&lcnt_thr_data_key, "lcnt_data");
+ prev->next = info;
+ next->prev = info;
- lcnt_lock();
+ lcnt_unlock_list_entry(next);
+ lcnt_unlock_list_entry(prev);
+}
+
+static void lcnt_insert_list_carrier(erts_lcnt_lock_info_list_t *list,
+ erts_lcnt_lock_info_carrier_t *carrier) {
+ erts_lcnt_lock_info_t *next, *prev;
+ size_t i;
- erts_lcnt_rt_options = ERTS_LCNT_OPT_LOCATION | ERTS_LCNT_OPT_PROCLOCK;
- eltd = lcnt_thread_data_alloc();
- ethr_tsd_set(lcnt_thr_data_key, eltd);
+ for(i = 0; i < carrier->entry_count; i++) {
+ erts_lcnt_lock_info_t *info = &carrier->entries[i];
- /* init lcnt structure */
- erts_lcnt_data = (erts_lcnt_data_t*)malloc(sizeof(erts_lcnt_data_t));
- if (!erts_lcnt_data) {
- ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!");
+ info->prev = &carrier->entries[i - 1];
+ info->next = &carrier->entries[i + 1];
}
- erts_lcnt_data->current_locks = erts_lcnt_list_init();
- erts_lcnt_data->deleted_locks = erts_lcnt_list_init();
- lcnt_unlock();
+ prev = &list->head;
+
+ lcnt_lock_list_entry(prev);
+
+ next = prev->next;
+
+ lcnt_lock_list_entry(next);
+
+ next->prev = &carrier->entries[carrier->entry_count - 1];
+ carrier->entries[carrier->entry_count - 1].next = next;
+ prev->next = &carrier->entries[0];
+ carrier->entries[0].prev = prev;
+
+ lcnt_unlock_list_entry(next);
+ lcnt_unlock_list_entry(prev);
}
-void erts_lcnt_late_init() {
- /* set start timer and zero statistics */
- erts_lcnt_clear_counters();
- erts_thr_install_exit_handler(erts_lcnt_thread_exit_handler);
+static void lcnt_init_list(erts_lcnt_lock_info_list_t *list) {
+ /* Ensure that ref_count operations explode when touching the sentinels in
+ * DEBUG mode. */
+ ethr_atomic_init(&(list->head.ref_count), -1);
+ ethr_atomic_init(&(list->tail.ref_count), -1);
+
+ ethr_atomic32_init(&(list->head.lock), 0);
+ (list->head).next = &list->tail;
+ (list->head).prev = &list->tail;
+
+ ethr_atomic32_init(&(list->tail.lock), 0);
+ (list->tail).next = &list->head;
+ (list->tail).prev = &list->head;
}
-/* list operations */
+/* - Carrier operations - */
-/* BEGIN ASSUMPTION: lcnt_data_lock taken */
+int lcnt_thr_progress_unmanaged_delay__(void) {
+ return erts_thr_progress_unmanaged_delay();
+}
-erts_lcnt_lock_list_t *erts_lcnt_list_init(void) {
- erts_lcnt_lock_list_t *list;
+void lcnt_thr_progress_unmanaged_continue__(int handle) {
+ return erts_thr_progress_unmanaged_continue(handle);
+}
- list = (erts_lcnt_lock_list_t*)malloc(sizeof(erts_lcnt_lock_list_t));
- if (!list) {
- ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!");
- }
- list->head = NULL;
- list->tail = NULL;
- list->n = 0;
- return list;
+void lcnt_deallocate_carrier__(erts_lcnt_lock_info_carrier_t *carrier) {
+ ASSERT(ethr_atomic_read(&carrier->ref_count) == 0);
+ erts_free(ERTS_ALC_T_LCNT_CARRIER, (void*)carrier);
}
-static void lcnt_list_free(erts_lcnt_lock_t *head) {
- erts_lcnt_lock_t *lock, *next;
+static void lcnt_thr_prg_cleanup_carrier(void *data) {
+ erts_lcnt_lock_info_carrier_t *carrier = data;
+ size_t entry_count, i;
+
+ /* carrier->entry_count will be replaced with garbage if it's deallocated
+ * on the final iteration, so we'll tuck it away to get a clean exit. */
+ entry_count = carrier->entry_count;
- lock = head;
+ for(i = 0; i < entry_count; i++) {
+ ASSERT(ethr_atomic_read(&carrier->ref_count) >= (entry_count - i));
- while(lock != NULL) {
- next = lock->next;
- free(lock);
- lock = next;
+ erts_lcnt_release_lock_info(&carrier->entries[i]);
}
}
-void erts_lcnt_list_insert(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) {
- erts_lcnt_lock_t *tail = NULL;
+static void lcnt_schedule_carrier_cleanup(void *data) {
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+
+ /* We can't issue cleanup jobs on anything other than normal schedulers, so
+ * we move to the first scheduler if required. */
- tail = list->tail;
- if (tail) {
- tail->next = lock;
- lock->prev = tail;
+ if(!esdp || esdp->type != ERTS_SCHED_NORMAL) {
+ erts_schedule_misc_aux_work(1, &lcnt_schedule_carrier_cleanup, data);
} else {
- list->head = lock;
- lock->prev = NULL;
- ASSERT(!lock->next);
+ erts_lcnt_lock_info_carrier_t *carrier = data;
+ size_t carrier_size;
+
+ carrier_size = sizeof(erts_lcnt_lock_info_carrier_t) +
+ sizeof(erts_lcnt_lock_info_t) * carrier->entry_count;
+
+ erts_schedule_thr_prgr_later_cleanup_op(&lcnt_thr_prg_cleanup_carrier,
+ data, (ErtsThrPrgrLaterOp*)&carrier->release_entries, carrier_size);
}
- lock->next = NULL;
- list->tail = lock;
+}
- list->n++;
+static void lcnt_info_deallocate(erts_lcnt_lock_info_t *info) {
+ lcnt_release_carrier__(info->carrier);
}
-void erts_lcnt_list_delete(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) {
- if (lock->next) lock->next->prev = lock->prev;
- if (lock->prev) lock->prev->next = lock->next;
- if (list->head == lock) list->head = lock->next;
- if (list->tail == lock) list->tail = lock->prev;
+static void lcnt_info_dispose(erts_lcnt_lock_info_t *info) {
+ ASSERT(ethr_atomic_read(&info->ref_count) == 0);
+
+ if(lcnt_preserve_info) {
+ ethr_atomic_set(&info->ref_count, 1);
+
+ /* Move straight to deallocation the next time around. */
+ info->dispose = &lcnt_info_deallocate;
- lock->prev = NULL;
- lock->next = NULL;
- list->n--;
+ lcnt_insert_list_entry(&lcnt_deleted_lock_list, info);
+ } else {
+ lcnt_info_deallocate(info);
+ }
}
-/* END ASSUMPTION: lcnt_data_lock taken */
+static void lcnt_lock_info_init_helper(erts_lcnt_lock_info_t *info) {
+ ethr_atomic_init(&info->ref_count, 1);
+ ethr_atomic32_init(&info->lock, 0);
+
+ ethr_atomic_init(&info->r_state, 0);
+ ethr_atomic_init(&info->w_state, 0);
-/* lock operations */
+ info->dispose = &lcnt_info_dispose;
-/* interface to erl_threads.h */
-/* only lock on init and destroy, all others should use atomics */
-void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag ) {
- erts_lcnt_init_lock_x(lock, name, flag, NIL);
+ lcnt_clear_stats(info);
}
-void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id) {
- int i;
+erts_lcnt_lock_info_carrier_t *erts_lcnt_create_lock_info_carrier(int entry_count) {
+ erts_lcnt_lock_info_carrier_t *result;
+ size_t carrier_size, i;
- if (flag & ERTS_LCNT_LT_DISABLE) {
- ERTS_LCNT_CLEAR_FLAG(lock);
- return;
- }
+ ASSERT(entry_count > 0 && entry_count <= LCNT_MAX_CARRIER_ENTRIES);
+ ASSERT(lcnt_initialization_completed__);
- lock->next = NULL;
- lock->prev = NULL;
- lock->flag = flag;
- lock->name = name;
- lock->id = id;
+ carrier_size = sizeof(erts_lcnt_lock_info_carrier_t) +
+ sizeof(erts_lcnt_lock_info_t) * entry_count;
- ethr_atomic_init(&lock->r_state, 0);
- ethr_atomic_init(&lock->w_state, 0);
-#ifdef DEBUG
- ethr_atomic_init(&lock->flowstate, 0);
-#endif
+ result = (erts_lcnt_lock_info_carrier_t*)erts_alloc(ERTS_ALC_T_LCNT_CARRIER, carrier_size);
+ result->entry_count = entry_count;
- lock->n_stats = 1;
+ ethr_atomic_init(&result->ref_count, entry_count);
- for (i = 0; i < ERTS_LCNT_MAX_LOCK_LOCATIONS; i++) {
- lcnt_clear_stats(&lock->stats[i]);
- }
+ for(i = 0; i < entry_count; i++) {
+ erts_lcnt_lock_info_t *info = &result->entries[i];
- lcnt_lock();
- erts_lcnt_list_insert(erts_lcnt_data->current_locks, lock);
- lcnt_unlock();
-}
+ lcnt_lock_info_init_helper(info);
-/* init empty, instead of zero struct
- * used by process locks probes
- */
-void erts_lcnt_init_lock_empty(erts_lcnt_lock_t *lock) {
- lock->next = NULL;
- lock->prev = NULL;
- lock->flag = 0;
- lock->name = NULL;
- lock->id = NIL;
- ethr_atomic_init(&lock->r_state, 0);
- ethr_atomic_init(&lock->w_state, 0);
-#ifdef DEBUG
- ethr_atomic_init(&lock->flowstate, 0);
-#endif
- lock->n_stats = 0;
- sys_memzero(lock->stats, sizeof(lock->stats));
-}
-/* destroy lock */
-void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) {
- if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return;
- lcnt_lock();
-
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_COPYSAVE) {
- erts_lcnt_lock_t *deleted_lock;
- /* copy structure and insert the copy */
- deleted_lock = (erts_lcnt_lock_t*)malloc(sizeof(erts_lcnt_lock_t));
- if (!deleted_lock) {
- ERTS_INTERNAL_ERROR("Lock counter failed to allocate memory!");
- }
- memcpy(deleted_lock, lock, sizeof(erts_lcnt_lock_t));
- deleted_lock->next = NULL;
- deleted_lock->prev = NULL;
- erts_lcnt_list_insert(erts_lcnt_data->deleted_locks, deleted_lock);
+ info->carrier = result;
}
- /* delete original */
- erts_lcnt_list_delete(erts_lcnt_data->current_locks, lock);
- ERTS_LCNT_CLEAR_FLAG(lock);
- lcnt_unlock();
+ return result;
}
-/* lock */
+void erts_lcnt_install(erts_lcnt_ref_t *ref, erts_lcnt_lock_info_carrier_t *carrier) {
+ ethr_sint_t swapped_carrier;
-void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option) {
- erts_aint_t r_state = 0, w_state = 0;
- erts_lcnt_thread_data_t *eltd;
+#ifdef DEBUG
+ int i;
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
- if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return;
+ /* Verify that all locks share the same categories/static property; all
+ * other flags are fair game. */
+ for(i = 1; i < carrier->entry_count; i++) {
+ const erts_lock_flags_t SIGNIFICANT_DIFF_MASK =
+ ERTS_LOCK_FLAGS_MASK_CATEGORY | ERTS_LOCK_FLAGS_PROPERTY_STATIC;
- eltd = lcnt_get_thread_data();
- ASSERT(eltd);
+ erts_lcnt_lock_info_t *previous, *current;
- w_state = ethr_atomic_read(&lock->w_state);
+ previous = &carrier->entries[i - 1];
+ current = &carrier->entries[i];
- if (option & ERTS_LCNT_LO_WRITE) {
- r_state = ethr_atomic_read(&lock->r_state);
- ethr_atomic_inc( &lock->w_state);
- }
- if (option & ERTS_LCNT_LO_READ) {
- ethr_atomic_inc( &lock->r_state);
+ ASSERT(!((previous->flags ^ current->flags) & SIGNIFICANT_DIFF_MASK));
}
+#endif
- /* we cannot acquire w_lock if either w or r are taken */
- /* we cannot acquire r_lock if w_lock is taken */
+ swapped_carrier = ethr_atomic_cmpxchg_mb(ref, (ethr_sint_t)carrier, (ethr_sint_t)NULL);
- if ((w_state > 0) || (r_state > 0)) {
- eltd->lock_in_conflict = 1;
- if (eltd->timer_set == 0) {
- lcnt_time(&eltd->timer);
- }
- eltd->timer_set++;
+ if(swapped_carrier != (ethr_sint_t)NULL) {
+#ifdef DEBUG
+ ASSERT(ethr_atomic_read(&carrier->ref_count) == carrier->entry_count);
+ ethr_atomic_set(&carrier->ref_count, 0);
+#endif
+
+ lcnt_deallocate_carrier__(carrier);
} else {
- eltd->lock_in_conflict = 0;
+ lcnt_insert_list_carrier(&lcnt_current_lock_list, carrier);
}
}
-void erts_lcnt_lock(erts_lcnt_lock_t *lock) {
- erts_aint_t w_state;
- erts_lcnt_thread_data_t *eltd;
+void erts_lcnt_uninstall(erts_lcnt_ref_t *ref) {
+ ethr_sint_t previous_carrier, swapped_carrier;
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
- if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return;
+ previous_carrier = ethr_atomic_read(ref);
+ swapped_carrier = ethr_atomic_cmpxchg_mb(ref, (ethr_sint_t)NULL, previous_carrier);
- w_state = ethr_atomic_read(&lock->w_state);
- ethr_atomic_inc(&lock->w_state);
- eltd = lcnt_get_thread_data();
+ if(previous_carrier && previous_carrier == swapped_carrier) {
+ lcnt_schedule_carrier_cleanup((void*)previous_carrier);
+ }
+}
- ASSERT(eltd);
+/* - Static lock registry -
+ *
+ * Since static locks can be trusted to never disappear, we can track them
+ * pretty cheaply and won't need to bother writing an "erts_lcnt_update_xx"
+ * variant. */
+
+static void lcnt_init_static_lock_registry(void) {
+ ethr_atomic_init(&lcnt_static_lock_registry, (ethr_sint_t)NULL);
+}
+
+static void lcnt_update_static_locks(void) {
+ lcnt_static_lock_ref_t *iterator =
+ (lcnt_static_lock_ref_t*)ethr_atomic_read(&lcnt_static_lock_registry);
+
+ while(iterator != NULL) {
+ if(!erts_lcnt_check_enabled(iterator->flags)) {
+ erts_lcnt_uninstall(iterator->reference);
+ } else if(!erts_lcnt_check_ref_installed(iterator->reference)) {
+ erts_lcnt_lock_info_carrier_t *carrier = erts_lcnt_create_lock_info_carrier(1);
+
+ erts_lcnt_init_lock_info_idx(carrier, 0, iterator->name, iterator->id, iterator->flags);
- if (w_state > 0) {
- eltd->lock_in_conflict = 1;
- /* only set the timer if nobody else has it
- * This should only happen when proc_locks aquires several locks
- * 'atomicly'. All other locks will block the thread if w_state > 0
- * i.e. locked.
- */
- if (eltd->timer_set == 0) {
- lcnt_time(&eltd->timer);
+ erts_lcnt_install(iterator->reference, carrier);
}
- eltd->timer_set++;
- } else {
- eltd->lock_in_conflict = 0;
+
+ iterator = iterator->next;
}
}
-/* if a lock wasn't really a lock operation, bad bad process locks */
+void lcnt_register_static_lock__(erts_lcnt_ref_t *reference, const char *name, Eterm id,
+ erts_lock_flags_t flags) {
+ lcnt_static_lock_ref_t *lock = malloc(sizeof(lcnt_static_lock_ref_t));
+ int retry_insertion;
+
+ ASSERT(flags & ERTS_LOCK_FLAGS_PROPERTY_STATIC);
+
+ lock->reference = reference;
+ lock->flags = flags;
+ lock->name = name;
+ lock->id = id;
+
+ do {
+ ethr_sint_t swapped_head;
-void erts_lcnt_lock_unaquire(erts_lcnt_lock_t *lock) {
- /* should check if this thread was "waiting" */
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
- if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return;
+ lock->next = (lcnt_static_lock_ref_t*)ethr_atomic_read(&lcnt_static_lock_registry);
- ethr_atomic_dec(&lock->w_state);
+ swapped_head = ethr_atomic_cmpxchg_acqb(
+ &lcnt_static_lock_registry,
+ (ethr_sint_t)lock,
+ (ethr_sint_t)lock->next);
+
+ retry_insertion = (swapped_head != (ethr_sint_t)lock->next);
+ } while(retry_insertion);
}
-/*
- * erts_lcnt_lock_post
- *
- * Used when we get a lock (i.e. directly after a lock operation)
- * if the timer was set then we had to wait for the lock
- * lock_post will calculate the wait time.
- */
+/* - Initialization - */
+
+void erts_lcnt_pre_thr_init() {
+ /* Ensure that the dependency hack mentioned in the header doesn't
+ * explode at runtime. */
+ ERTS_CT_ASSERT(sizeof(LcntThrPrgrLaterOp) >= sizeof(ErtsThrPrgrLaterOp));
+ ERTS_CT_ASSERT(ERTS_THR_PRGR_DHANDLE_MANAGED ==
+ (ErtsThrPrgrDelayHandle)LCNT_THR_PRGR_DHANDLE_MANAGED);
-void erts_lcnt_lock_post(erts_lcnt_lock_t *lock) {
- erts_lcnt_lock_post_x(lock, (char*)str_undefined, 0);
+ lcnt_init_list(&lcnt_current_lock_list);
+ lcnt_init_list(&lcnt_deleted_lock_list);
+
+ lcnt_init_static_lock_registry();
}
-void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line) {
- erts_lcnt_thread_data_t *eltd;
- erts_lcnt_time_t timer;
- erts_lcnt_time_t time_wait;
- erts_lcnt_lock_stats_t *stats;
-#ifdef DEBUG
- erts_aint_t flowstate;
-#endif
+void erts_lcnt_post_thr_init() {
+ /* ASSUMPTION: this is safe since it runs prior to the creation of other
+ * threads (Directly after ethread init). */
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
- if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return;
+ ethr_tsd_key_create(&lcnt_thr_data_key__, "lcnt_data");
+
+ erts_lcnt_thread_setup();
+}
+
+void erts_lcnt_late_init() {
+ /* Set start timer and zero all statistics */
+ erts_lcnt_clear_counters();
+ erts_thr_install_exit_handler(erts_lcnt_thread_exit_handler);
#ifdef DEBUG
- if (!(lock->flag & (ERTS_LCNT_LT_RWMUTEX | ERTS_LCNT_LT_RWSPINLOCK))) {
- flowstate = ethr_atomic_read(&lock->flowstate);
- ASSERT(flowstate == 0);
- ethr_atomic_inc(&lock->flowstate);
- }
+ /* It's safe to use erts_alloc and thread progress past this point. */
+ lcnt_initialization_completed__ = 1;
#endif
+}
- eltd = lcnt_get_thread_data();
-
- ASSERT(eltd);
+void erts_lcnt_post_startup(void) {
+ /* Default to capturing everything to match the behavior of the old lock
+ * counter build. */
+ erts_lcnt_set_category_mask(ERTS_LOCK_FLAGS_MASK_CATEGORY);
+}
- /* if lock was in conflict, time it */
- stats = lcnt_get_lock_stats(lock, file, line);
- if (eltd->timer_set) {
- lcnt_time(&timer);
+void erts_lcnt_thread_setup() {
+ lcnt_thread_data_t__ *eltd = lcnt_thread_data_alloc();
- lcnt_time_diff(&time_wait, &timer, &(eltd->timer));
- lcnt_update_stats(stats, eltd->lock_in_conflict, &time_wait);
- eltd->timer_set--;
- ASSERT(eltd->timer_set >= 0);
- } else {
- lcnt_update_stats(stats, eltd->lock_in_conflict, NULL);
- }
+ ASSERT(eltd);
+ ethr_tsd_set(lcnt_thr_data_key__, eltd);
}
-/* unlock */
+void erts_lcnt_thread_exit_handler() {
+ lcnt_thread_data_t__ *eltd = lcnt_get_thread_data__();
-void erts_lcnt_unlock_opt(erts_lcnt_lock_t *lock, Uint16 option) {
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
- if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return;
- if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_dec(&lock->w_state);
- if (option & ERTS_LCNT_LO_READ ) ethr_atomic_dec(&lock->r_state);
+ if (eltd) {
+ free(eltd);
+ }
}
-void erts_lcnt_unlock(erts_lcnt_lock_t *lock) {
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
- if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return;
+/* - BIF interface - */
+
+void erts_lcnt_retain_lock_info(erts_lcnt_lock_info_t *info) {
#ifdef DEBUG
- {
- erts_aint_t w_state;
- erts_aint_t flowstate;
-
- /* flowstate */
- flowstate = ethr_atomic_read(&lock->flowstate);
- ASSERT(flowstate == 1);
- ethr_atomic_dec(&lock->flowstate);
-
- /* write state */
- w_state = ethr_atomic_read(&lock->w_state);
- ASSERT(w_state > 0);
- }
+ ASSERT(ethr_atomic_inc_read_acqb(&info->ref_count) >= 2);
+#else
+ ethr_atomic_inc_acqb(&info->ref_count);
#endif
- ethr_atomic_dec(&lock->w_state);
}
-/* trylock */
+void erts_lcnt_release_lock_info(erts_lcnt_lock_info_t *info) {
+ ethr_sint_t count;
+
+ /* We need to acquire the lock before decrementing ref_count to avoid
+ * racing with list iteration; there's a short window between reading the
+ * reference to info and increasing its ref_count. */
+ lcnt_lock_list_entry_with_neighbors(info);
+
+ count = ethr_atomic_dec_read(&info->ref_count);
-void erts_lcnt_trylock_opt(erts_lcnt_lock_t *lock, int res, Uint16 option) {
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
- if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return;
- /* Determine lock_state via res instead of state */
- if (res != EBUSY) {
- if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_inc(&lock->w_state);
- if (option & ERTS_LCNT_LO_READ ) ethr_atomic_inc(&lock->r_state);
- lcnt_update_stats(&(lock->stats[0]), 0, NULL);
+ ASSERT(count >= 0);
+
+ if(count > 0) {
+ lcnt_unlock_list_entry_with_neighbors(info);
} else {
- ethr_atomic_inc(&lock->stats[0].tries);
- ethr_atomic_inc(&lock->stats[0].colls);
+ (info->next)->prev = info->prev;
+ (info->prev)->next = info->next;
+
+ lcnt_unlock_list_entry_with_neighbors(info);
+
+ info->dispose(info);
}
}
+erts_lock_flags_t erts_lcnt_get_category_mask() {
+ return lcnt_category_mask__;
+}
-void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res) {
- /* Determine lock_state via res instead of state */
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
- if (ERTS_LCNT_IS_LOCK_INVALID(lock)) return;
- if (res != EBUSY) {
-#ifdef DEBUG
- {
- erts_aint_t flowstate;
- flowstate = ethr_atomic_read(&lock->flowstate);
- ASSERT(flowstate == 0);
- ethr_atomic_inc( &lock->flowstate);
- }
+#ifdef ERTS_ENABLE_KERNEL_POLL
+/* erl_poll/erl_check_io only exports one of these variants at a time, and we
+ * may need to use either one depending on emulator startup flags. */
+void erts_lcnt_update_pollset_locks_nkp(int);
+void erts_lcnt_update_pollset_locks_kp(int);
+
+void erts_lcnt_update_cio_locks_nkp(int);
+void erts_lcnt_update_cio_locks_kp(int);
#endif
- ethr_atomic_inc(&lock->w_state);
- lcnt_update_stats(&(lock->stats[0]), 0, NULL);
- } else {
- ethr_atomic_inc(&lock->stats[0].tries);
- ethr_atomic_inc(&lock->stats[0].colls);
+
+void erts_lcnt_set_category_mask(erts_lock_flags_t mask) {
+ erts_lock_flags_t changed_categories;
+
+ ASSERT(!(mask & ~ERTS_LOCK_FLAGS_MASK_CATEGORY));
+ ASSERT(lcnt_initialization_completed__);
+
+ changed_categories = (lcnt_category_mask__ ^ mask);
+ lcnt_category_mask__ = mask;
+
+ if(changed_categories) {
+ lcnt_update_static_locks();
}
-}
-/* thread operations */
+ if(changed_categories & ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION) {
+ erts_lcnt_update_distribution_locks(mask & ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+ }
-void erts_lcnt_thread_setup(void) {
- erts_lcnt_thread_data_t *eltd;
+ if(changed_categories & ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR) {
+ erts_lcnt_update_allocator_locks(mask & ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR);
+ }
- lcnt_lock();
- /* lock for thread id global update */
- eltd = lcnt_thread_data_alloc();
- lcnt_unlock();
- ASSERT(eltd);
- ethr_tsd_set(lcnt_thr_data_key, eltd);
-}
+ if(changed_categories & ERTS_LOCK_FLAGS_CATEGORY_PROCESS) {
+ erts_lcnt_update_process_locks(mask & ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
+ }
-void erts_lcnt_thread_exit_handler() {
- erts_lcnt_thread_data_t *eltd;
+ if(changed_categories & ERTS_LOCK_FLAGS_CATEGORY_IO) {
+#ifdef ERTS_ENABLE_KERNEL_POLL
+ if(erts_use_kernel_poll) {
+ erts_lcnt_update_pollset_locks_kp(mask & ERTS_LOCK_FLAGS_CATEGORY_IO);
+ erts_lcnt_update_cio_locks_kp(mask & ERTS_LOCK_FLAGS_CATEGORY_IO);
+ } else {
+ erts_lcnt_update_pollset_locks_nkp(mask & ERTS_LOCK_FLAGS_CATEGORY_IO);
+ erts_lcnt_update_cio_locks_nkp(mask & ERTS_LOCK_FLAGS_CATEGORY_IO);
+ }
+#else
+ erts_lcnt_update_pollset_locks(mask & ERTS_LOCK_FLAGS_CATEGORY_IO);
+ erts_lcnt_update_cio_locks(mask & ERTS_LOCK_FLAGS_CATEGORY_IO);
+#endif
- eltd = ethr_tsd_get(lcnt_thr_data_key);
+ erts_lcnt_update_driver_locks(mask & ERTS_LOCK_FLAGS_CATEGORY_IO);
+ erts_lcnt_update_port_locks(mask & ERTS_LOCK_FLAGS_CATEGORY_IO);
+ }
- if (eltd) {
- free(eltd);
+ if(changed_categories & ERTS_LOCK_FLAGS_CATEGORY_DB) {
+ erts_lcnt_update_db_locks(mask & ERTS_LOCK_FLAGS_CATEGORY_DB);
}
}
-/* bindings for bifs */
-
-Uint16 erts_lcnt_set_rt_opt(Uint16 opt) {
- Uint16 prev;
- prev = (erts_lcnt_rt_options & opt);
- erts_lcnt_rt_options |= opt;
- return prev;
+void erts_lcnt_set_preserve_info(int enable) {
+ lcnt_preserve_info = enable;
}
-Uint16 erts_lcnt_clear_rt_opt(Uint16 opt) {
- Uint16 prev;
- prev = (erts_lcnt_rt_options & opt);
- erts_lcnt_rt_options &= ~opt;
- return prev;
+int erts_lcnt_get_preserve_info() {
+ return lcnt_preserve_info;
}
void erts_lcnt_clear_counters(void) {
- erts_lcnt_lock_t *lock;
- erts_lcnt_lock_list_t *list;
- erts_lcnt_lock_stats_t *stats;
- int i;
+ erts_lcnt_lock_info_t *iterator;
- lcnt_lock();
+ lcnt_time__(&lcnt_timer_start);
- list = erts_lcnt_data->current_locks;
+ iterator = NULL;
+ while(erts_lcnt_iterate_list(&lcnt_current_lock_list, &iterator)) {
+ lcnt_clear_stats(iterator);
+ }
- for (lock = list->head; lock != NULL; lock = lock->next) {
- for( i = 0; i < ERTS_LCNT_MAX_LOCK_LOCATIONS; i++) {
- stats = &lock->stats[i];
- lcnt_clear_stats(stats);
- }
- lock->n_stats = 1;
+ iterator = NULL;
+ while(erts_lcnt_iterate_list(&lcnt_deleted_lock_list, &iterator)) {
+ erts_lcnt_release_lock_info(iterator);
}
+}
- lock = erts_lcnt_data->deleted_locks->head;
- erts_lcnt_data->deleted_locks->head = NULL;
- erts_lcnt_data->deleted_locks->tail = NULL;
- erts_lcnt_data->deleted_locks->n = 0;
+erts_lcnt_data_t erts_lcnt_get_data(void) {
+ erts_lcnt_time_t timer_stop;
+ erts_lcnt_data_t result;
- lcnt_time(&timer_start);
+ lcnt_time__(&timer_stop);
- lcnt_unlock();
+ result.timer_start = lcnt_timer_start;
- /* free deleted locks */
- lcnt_list_free(lock);
+ result.current_locks = &lcnt_current_lock_list;
+ result.deleted_locks = &lcnt_deleted_lock_list;
+
+ lcnt_time_diff__(&result.duration, &timer_stop, &result.timer_start);
+
+ return result;
}
-erts_lcnt_data_t *erts_lcnt_get_data(void) {
- erts_lcnt_time_t timer_stop;
+int erts_lcnt_iterate_list(erts_lcnt_lock_info_list_t *list, erts_lcnt_lock_info_t **iterator) {
+ erts_lcnt_lock_info_t *current, *next;
- lcnt_lock();
+ current = *iterator ? *iterator : &list->head;
- lcnt_time(&timer_stop);
- lcnt_time_diff(&(erts_lcnt_data->duration), &timer_stop, &timer_start);
+ ASSERT(current != &list->tail);
- lcnt_unlock();
+ lcnt_lock_list_entry(current);
- return erts_lcnt_data;
-}
+ next = current->next;
+
+ if(next != &list->tail) {
+ erts_lcnt_retain_lock_info(next);
+ }
+
+ lcnt_unlock_list_entry(current);
+
+ if(current != &list->head) {
+ erts_lcnt_release_lock_info(current);
+ }
+
+ *iterator = next;
-char *erts_lcnt_lock_type(Uint16 type) {
- return lcnt_lock_type(type);
+ return next != &list->tail;
}
-#endif /* ifdef ERTS_ENABLE_LOCK_COUNT */
+#endif /* #ifdef ERTS_ENABLE_LOCK_COUNT */
diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h
index 6caffbfe86..3181dbcad4 100644
--- a/erts/emulator/beam/erl_lock_count.h
+++ b/erts/emulator/beam/erl_lock_count.h
@@ -18,64 +18,51 @@
* %CopyrightEnd%
*/
-/*
- * Description: Statistics for locks.
- *
- * Author: Björn-Egil Dahlberg
- * Date: 2008-07-03
- * Abstract:
- * Locks statistics internal representation.
- *
- * Conceptual representation,
- * - set name
- * | - id (the unique lock)
- * | | - lock type
- * | | - statistics
- * | | | - location (file and line number)
- * | | | - tries
- * | | | - collisions (including trylock busy)
- * | | | - timer (time spent in waiting for lock)
- * | | | - n_timer (collisions excluding trylock busy)
- * | | | - histogram
- * | | | | - # 0 = log2(lock wait_time ns)
- * | | | | - ...
- * | | | | - # n = log2(lock wait_time ns)
- *
- * Each instance of a lock is the unique lock, i.e. set and id in that set.
- * For each lock there is a set of statistics with where and what impact
- * the lock aqusition had.
- *
- * Runtime options
- * - suspend, used when internal lock-counting can't be applied. For instance
- * when allocating a term for the outside and halloc needs to be used.
- * Default: off.
- * - location, reserved and not used.
- * - proclock, disable proclock counting. Used when performance might be an
- * issue. Accessible from erts_debug:lock_counters({process_locks, bool()}).
- * Default: off.
- * - copysave, enable saving of destroyed locks (and thereby its statistics).
- * If memory constraints is an issue this need to be disabled.
- * Accessible from erts_debug:lock_counters({copy_save, bool()}).
- * Default: off.
+/**
+ * @description Statistics for locks.
+ * @file erl_lock_count.h
+ *
+ * @author Björn-Egil Dahlberg
+ * @author John Högberg
+ *
+ * Conceptual representation:
*
+ * - set name
+ * | - id (the unique lock)
+ * | | - lock type
+ * | | - statistics
+ * | | | - location (file and line number)
+ * | | | - attempts
+ * | | | - collisions (including trylock busy)
+ * | | | - timer (time spent in waiting for lock)
+ * | | | - n_timer (collisions excluding trylock busy)
+ * | | | - histogram
+ * | | | | - # 0 = log2(lock wait_time ns)
+ * | | | | - ...
+ * | | | | - # n = log2(lock wait_time ns)
+ *
+ * Each instance of a lock is the unique lock, i.e. set and id in that set.
+ * For each lock there is a set of statistics with where and what impact
+ * the lock acquisition had.
*/
-#include "sys.h"
-
#ifndef ERTS_LOCK_COUNT_H__
#define ERTS_LOCK_COUNT_H__
#ifdef ERTS_ENABLE_LOCK_COUNT
#ifndef ERTS_ENABLE_LOCK_POSITION
-/* Enable in order for _x variants of mtx functions to be used. */
+/** @brief Controls whether _x variants of mtx functions are used. */
#define ERTS_ENABLE_LOCK_POSITION 1
#endif
+#include "sys.h"
#include "ethread.h"
-#define ERTS_LCNT_MAX_LOCK_LOCATIONS (10)
+#include "erl_term.h"
+#include "erl_lock_flags.h"
+
+#define ERTS_LCNT_MAX_LOCK_LOCATIONS (5)
-/* histogram */
#define ERTS_LCNT_HISTOGRAM_MAX_NS (((unsigned long)1LL << 28) - 1)
#if 0 || defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT)
#define ERTS_LCNT_HISTOGRAM_SLOT_SIZE (30)
@@ -85,154 +72,857 @@
#define ERTS_LCNT_HISTOGRAM_RSHIFT (10)
#endif
-#define ERTS_LCNT_LT_SPINLOCK (((Uint16) 1) << 0)
-#define ERTS_LCNT_LT_RWSPINLOCK (((Uint16) 1) << 1)
-#define ERTS_LCNT_LT_MUTEX (((Uint16) 1) << 2)
-#define ERTS_LCNT_LT_RWMUTEX (((Uint16) 1) << 3)
-#define ERTS_LCNT_LT_PROCLOCK (((Uint16) 1) << 4)
-#define ERTS_LCNT_LT_ALLOC (((Uint16) 1) << 5)
+typedef struct {
+ unsigned long s;
+ unsigned long ns;
+} erts_lcnt_time_t;
+
+typedef struct {
+ /* @brief log2 array of nano seconds occurences */
+ Uint32 ns[ERTS_LCNT_HISTOGRAM_SLOT_SIZE];
+} erts_lcnt_hist_t;
-#define ERTS_LCNT_LO_READ (((Uint16) 1) << 6)
-#define ERTS_LCNT_LO_WRITE (((Uint16) 1) << 7)
+typedef struct {
+ /** @brief In which file the lock was taken. May be NULL. */
+ const char *file;
+ /** @brief Line number in \c file */
+ unsigned int line;
-#define ERTS_LCNT_LT_DISABLE (((Uint16) 1) << 8)
+ /* "attempts" and "collisions" need to be atomic since try_lock busy does
+ * not acquire a lock and there is no post action to rectify the
+ * situation. */
-#define ERTS_LCNT_LO_READ_WRITE ( ERTS_LCNT_LO_READ \
- | ERTS_LCNT_LO_WRITE )
+ ethr_atomic_t attempts;
+ ethr_atomic_t collisions;
-#define ERTS_LCNT_LT_ALL ( ERTS_LCNT_LT_SPINLOCK \
- | ERTS_LCNT_LT_RWSPINLOCK \
- | ERTS_LCNT_LT_MUTEX \
- | ERTS_LCNT_LT_RWMUTEX \
- | ERTS_LCNT_LT_PROCLOCK )
+ erts_lcnt_time_t total_time_waited;
+ Uint64 times_waited;
-#define ERTS_LCNT_LOCK_TYPE(lock) ((lock)->flag & ERTS_LCNT_LT_ALL)
-#define ERTS_LCNT_IS_LOCK_INVALID(lock) (!((lock)->flag & ERTS_LCNT_LT_ALL))
-#define ERTS_LCNT_CLEAR_FLAG(lock) ((lock)->flag = 0)
+ erts_lcnt_hist_t wait_time_histogram;
+} erts_lcnt_lock_stats_t;
-/* runtime options */
+typedef struct lcnt_lock_info_t_ {
+ erts_lock_flags_t flags;
+ const char *name;
+ /** @brief Id if possible, must be an immediate */
+ Eterm id;
-#define ERTS_LCNT_OPT_SUSPEND (((Uint16) 1) << 0)
-#define ERTS_LCNT_OPT_LOCATION (((Uint16) 1) << 1)
-#define ERTS_LCNT_OPT_PROCLOCK (((Uint16) 1) << 2)
-#define ERTS_LCNT_OPT_PORTLOCK (((Uint16) 1) << 3)
-#define ERTS_LCNT_OPT_COPYSAVE (((Uint16) 1) << 4)
+ /* The first entry is reserved as a fallback for when location information
+ * is missing, and when the lock is used in more than (MAX_LOCK_LOCATIONS
+ * - 1) different places. */
+ erts_lcnt_lock_stats_t location_stats[ERTS_LCNT_MAX_LOCK_LOCATIONS];
+ unsigned int location_count;
-typedef struct {
- unsigned long s;
- unsigned long ns;
-} erts_lcnt_time_t;
+ /* -- Everything below is internal to this module ---------------------- */
+
+ /* Lock states; rw locks uses both states, other locks only uses w_state */
-extern erts_lcnt_time_t timer_start;
+ /** @brief Write state. 0 = not taken, otherwise n threads waiting */
+ ethr_atomic_t w_state;
+ /** @brief Read state. 0 = not taken, > 0 -> writes will wait */
+ ethr_atomic_t r_state;
+
+ struct lcnt_lock_info_t_ *prev;
+ struct lcnt_lock_info_t_ *next;
+
+ /** @brief Used in place of erts_refc_t to avoid a circular dependency. */
+ ethr_atomic_t ref_count;
+ ethr_atomic32_t lock;
+
+ /** @brief Deletion hook called once \c ref_count reaches 0; may defer
+ * deletion by modifying \c ref_count. */
+ void (*dispose)(struct lcnt_lock_info_t_ *);
+
+ struct lcnt_lock_info_carrier_ *carrier;
+} erts_lcnt_lock_info_t;
+
+typedef struct lcnt_lock_info_list_ {
+ erts_lcnt_lock_info_t head;
+ erts_lcnt_lock_info_t tail;
+} erts_lcnt_lock_info_list_t;
typedef struct {
- Uint32 ns[ERTS_LCNT_HISTOGRAM_SLOT_SIZE]; /* log2 array of nano seconds occurences */
-} erts_lcnt_hist_t;
+ erts_lcnt_time_t timer_start; /**< Time of last clear */
+ erts_lcnt_time_t duration; /**< Time since last clear */
-typedef struct erts_lcnt_lock_stats_s {
- /* "tries" and "colls" needs to be atomic since
- * trylock busy does not acquire a lock and there
- * is no post action to rectify the situation
- */
+ erts_lcnt_lock_info_list_t *current_locks;
+ erts_lcnt_lock_info_list_t *deleted_locks;
+} erts_lcnt_data_t;
- char *file; /* which file the lock was taken */
- unsigned int line; /* line number in file */
+typedef struct lcnt_lock_info_carrier_ erts_lcnt_lock_info_carrier_t;
- ethr_atomic_t tries; /* n tries to get lock */
- ethr_atomic_t colls; /* n collisions of tries to get lock */
+typedef ethr_atomic_t erts_lcnt_ref_t;
- unsigned long timer_n; /* #times waited for lock */
- erts_lcnt_time_t timer; /* total wait time for lock */
- erts_lcnt_hist_t hist;
-} erts_lcnt_lock_stats_t;
+/* -- Globals -------------------------------------------------------------- */
+
+/** @brief Checks whether counting is enabled for any of the given
+ * categories. */
+#define erts_lcnt_check_enabled(flags) \
+ (lcnt_category_mask__ & flags)
+
+/* -- Lock operations ------------------------------------------------------
+ *
+ * All of these will nop if there's nothing "installed" on the given reference,
+ * in order to transparently support enable/disable at runtime. */
+
+/** @brief Records that a lock is being acquired. */
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_lock(erts_lcnt_ref_t *ref);
+
+/** @copydoc erts_lcnt_lock
+ * @param option Notes whether the lock is a read or write lock. */
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_lock_opt(erts_lcnt_ref_t *ref, Uint16 option);
+
+/** @brief Records that a lock has been acquired. */
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_lock_post(erts_lcnt_ref_t *ref);
+
+/** @copydoc erts_lcnt_lock_post.
+ * @param file The name of the file where the lock was acquired.
+ * @param line The line at which the lock was acquired. */
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_lock_post_x(erts_lcnt_ref_t *ref, char *file, unsigned int line);
+
+/** @brief Records that a lock has been released. */
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_unlock(erts_lcnt_ref_t *ref);
+
+/** @copydoc erts_lcnt_unlock_opt
+ * @param option Whether the lock is a read or write lock. */
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_unlock_opt(erts_lcnt_ref_t *ref, Uint16 option);
+
+/** @brief Rectifies the case where a lock wasn't actually a lock operation.
+ *
+ * Only used for process locks at the moment. */
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_lock_unacquire(erts_lcnt_ref_t *ref);
+
+/** @brief Records the result of a trylock, placing the queried lock status in
+ * \c result. */
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_trylock(erts_lcnt_ref_t *ref, int result);
+
+/** @copydoc erts_lcnt_trylock
+ * @param option Whether the lock is a read or write lock. */
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_trylock_opt(erts_lcnt_ref_t *ref, int result, Uint16 option);
+
+/* Indexed variants of the standard lock operations, for use when a single
+ * reference contains many counters (eg. process locks).
+ *
+ * erts_lcnt_open_ref must be used to safely extract the installed carrier,
+ * which must released with erts_lcnt_close_reference on success.
+ *
+ * Refer to \c erts_lcnt_lock for example usage. */
+
+ERTS_GLB_INLINE
+void erts_lcnt_lock_idx(erts_lcnt_lock_info_carrier_t *carrier, int index);
+ERTS_GLB_INLINE
+void erts_lcnt_lock_opt_idx(erts_lcnt_lock_info_carrier_t *carrier, int index, Uint16 option);
+
+ERTS_GLB_INLINE
+void erts_lcnt_lock_post_idx(erts_lcnt_lock_info_carrier_t *carrier, int index);
+ERTS_GLB_INLINE
+void erts_lcnt_lock_post_x_idx(erts_lcnt_lock_info_carrier_t *carrier, int index, char *file, unsigned int line);
+
+ERTS_GLB_INLINE
+void erts_lcnt_lock_unacquire_idx(erts_lcnt_lock_info_carrier_t *carrier, int index);
+
+ERTS_GLB_INLINE
+void erts_lcnt_unlock_idx(erts_lcnt_lock_info_carrier_t *carrier, int index);
+ERTS_GLB_INLINE
+void erts_lcnt_unlock_opt_idx(erts_lcnt_lock_info_carrier_t *carrier, int index, Uint16 option);
+
+ERTS_GLB_INLINE
+void erts_lcnt_trylock_idx(erts_lcnt_lock_info_carrier_t *carrier, int index, int result);
+ERTS_GLB_INLINE
+void erts_lcnt_trylock_opt_idx(erts_lcnt_lock_info_carrier_t *carrier, int index, int result, Uint16 option);
+
+/* -- Reference operations ------------------------------------------------- */
+
+/** @brief Registers a lock counter reference; this must be called prior to
+ * using any other functions in this module. */
+ERTS_GLB_INLINE
+void erts_lcnt_init_ref(erts_lcnt_ref_t *ref);
+
+/** @brief As \c erts_lcnt_init_ref, but also enables lock counting right
+ * away if appropriate to reduce noise.
+ * @param id An immediate erlang term with whatever extra data you want to
+ * identify this lock with. */
+ERTS_GLB_INLINE
+void erts_lcnt_init_ref_x(erts_lcnt_ref_t *ref, const char *name,
+ Eterm id, erts_lock_flags_t flags);
+
+/** @brief Checks whether counting is enabled on the given reference. */
+ERTS_GLB_FORCE_INLINE
+int erts_lcnt_check_ref_installed(erts_lcnt_ref_t *ref);
+
+/** @brief Convenience macro to re/enable counting on an already initialized
+ * reference. Don't forget to specify the lock type in \c flags! */
+#define erts_lcnt_install_new_lock_info(ref, name, id, flags) \
+ if(!erts_lcnt_check_ref_installed(ref)) { \
+ erts_lcnt_lock_info_carrier_t *__carrier; \
+ __carrier = erts_lcnt_create_lock_info_carrier(1);\
+ erts_lcnt_init_lock_info_idx(__carrier, 0, name, id, flags); \
+ erts_lcnt_install(ref, __carrier);\
+ } while(0)
+
+erts_lcnt_lock_info_carrier_t *erts_lcnt_create_lock_info_carrier(int count);
+
+/* @brief Initializes the lock info at the given index.
+ * @param id An immediate erlang term with whatever extra data you want to
+ * identify this lock with.
+ * @param flags The flags the lock itself was initialized with. Keep in mind
+ * that all locks in a carrier must share the same category/static property. */
+ERTS_GLB_INLINE
+void erts_lcnt_init_lock_info_idx(erts_lcnt_lock_info_carrier_t *carrier, int index,
+ const char *name, Eterm id, erts_lock_flags_t flags);
+
+/** @brief Atomically installs the given lock counters. Nops (and releases the
+ * provided carrier) if something was already installed. */
+void erts_lcnt_install(erts_lcnt_ref_t *ref, erts_lcnt_lock_info_carrier_t *carrier);
+
+/** @brief Atomically removes the currently installed lock counters. Nops if
+ * nothing was installed. */
+void erts_lcnt_uninstall(erts_lcnt_ref_t *ref);
+
+ERTS_GLB_FORCE_INLINE
+int erts_lcnt_open_ref(erts_lcnt_ref_t *ref, int *handle, erts_lcnt_lock_info_carrier_t **result);
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_close_ref(int handle, erts_lcnt_lock_info_carrier_t *carrier);
+
+/* -- Module initialization ------------------------------------------------ */
+
+void erts_lcnt_pre_thr_init(void);
+void erts_lcnt_post_thr_init(void);
+void erts_lcnt_late_init(void);
+
+/* @brief Called after everything in the system has been initialized, including
+ * the schedulers. This is mainly a backwards compatibility shim for matching
+ * the old lcnt behavior where all lock counting was enabled by default. */
+void erts_lcnt_post_startup(void);
+
+void erts_lcnt_thread_setup(void);
+void erts_lcnt_thread_exit_handler(void);
+
+/* -- BIF interface -------------------------------------------------------- */
-/* rw locks uses both states, other locks only uses w_state */
-typedef struct erts_lcnt_lock_s {
- char *name; /* lock name */
- Uint16 flag; /* lock type */
- Eterm id; /* id if possible */
+/** @brief Safely iterates through all entries in the given list.
+ *
+ * The referenced item will be valid until the next call to
+ * \c erts_lcnt_iterate_list after which point it may be destroyed; call
+ * erts_lcnt_retain_lock_info if you wish to hang on to it beyond that point.
+ *
+ * Iteration can be cancelled by calling erts_lcnt_release_lock_info on the
+ * iterator and breaking out of the loop.
+ *
+ * @param iterator The iteration variable; set the pointee to NULL to start
+ * iteration.
+ * @return 1 while the iterator is valid, 0 at the end of the list. */
+int erts_lcnt_iterate_list(erts_lcnt_lock_info_list_t *list, erts_lcnt_lock_info_t **iterator);
+
+/** @brief Clears the counter state of all locks, and releases all locks
+ * preserved through erts_lcnt_set_preserve_info (if any). */
+void erts_lcnt_clear_counters(void);
+
+/** @brief Retrieves the global lock counter state.
+ *
+ * Note that the lists may be modified while you're mucking around with them.
+ * Always use \c erts_lcnt_iterate_list to enumerate them. */
+erts_lcnt_data_t erts_lcnt_get_data(void);
+
+void erts_lcnt_retain_lock_info(erts_lcnt_lock_info_t *info);
+void erts_lcnt_release_lock_info(erts_lcnt_lock_info_t *info);
+
+/** @brief Sets whether to preserve the info of destroyed/uninstalled locks.
+ *
+ * This option makes no distinction whether the lock was destroyed or if lock
+ * counting was simply disabled, so erts_lcnt_set_category_mask must not be
+ * used while this option is active. */
+void erts_lcnt_set_preserve_info(int enable);
+
+int erts_lcnt_get_preserve_info(void);
+
+/** @brief Updates the category mask, enabling or disabling counting on the
+ * affected locks as necessary.
+ *
+ * This is not guaranteed to find all existing locks; only those that are
+ * flagged as static locks and those reachable through other means can be
+ * altered. */
+void erts_lcnt_set_category_mask(erts_lock_flags_t mask);
+
+erts_lock_flags_t erts_lcnt_get_category_mask(void);
+
+/* -- Inline implementation ------------------------------------------------ */
+
+/* The following is a hack to get the things we need from erl_thr_progress.h,
+ * which we can't #include without dependency hell breaking loose.
+ *
+ * The size of LcntThrPrgrLaterOp and value of the constant are verified at
+ * compile-time in erts_lcnt_pre_thr_init. */
+
+int lcnt_thr_progress_unmanaged_delay__(void);
+void lcnt_thr_progress_unmanaged_continue__(int handle);
+typedef struct { Uint64 _[4]; } LcntThrPrgrLaterOp;
+#define LCNT_THR_PRGR_DHANDLE_MANAGED -1
+
+struct lcnt_lock_info_carrier_ {
+ ethr_atomic_t ref_count;
+
+ LcntThrPrgrLaterOp release_entries;
+
+ unsigned char entry_count;
+ erts_lcnt_lock_info_t entries[];
+};
+
+typedef struct {
+ erts_lcnt_time_t timer; /* timer */
+ int timer_set; /* bool */
+ int lock_in_conflict; /* bool */
+} lcnt_thread_data_t__;
+
+extern const int lcnt_log2_tab64__[];
+
+extern ethr_tsd_key lcnt_thr_data_key__;
+extern erts_lock_flags_t lcnt_category_mask__;
#ifdef DEBUG
- ethr_atomic_t flowstate;
+extern int lcnt_initialization_completed__;
#endif
- /* lock states */
- ethr_atomic_t w_state; /* 0 not taken, otherwise n threads waiting */
- ethr_atomic_t r_state; /* 0 not taken, > 0 -> writes will wait */
+void lcnt_register_static_lock__(erts_lcnt_ref_t *reference, const char *name, Eterm id,
+ erts_lock_flags_t flags);
- /* statistics */
- unsigned int n_stats;
- erts_lcnt_lock_stats_t stats[ERTS_LCNT_MAX_LOCK_LOCATIONS]; /* first entry is "undefined"*/
+void lcnt_deallocate_carrier__(erts_lcnt_lock_info_carrier_t *carrier);
- /* chains for list handling */
- /* data is hold by lcnt_lock */
- struct erts_lcnt_lock_s *prev;
- struct erts_lcnt_lock_s *next;
-} erts_lcnt_lock_t;
+ERTS_GLB_INLINE
+int lcnt_log2__(Uint64 v);
-typedef struct {
- erts_lcnt_lock_t *head;
- erts_lcnt_lock_t *tail;
- unsigned long n;
-} erts_lcnt_lock_list_t;
+ERTS_GLB_INLINE
+void lcnt_update_wait_histogram__(erts_lcnt_hist_t *hist, erts_lcnt_time_t *time_waited);
-typedef struct {
- erts_lcnt_time_t duration; /* time since last clear */
- erts_lcnt_lock_list_t *current_locks;
- erts_lcnt_lock_list_t *deleted_locks;
-} erts_lcnt_data_t;
+ERTS_GLB_INLINE
+void lcnt_update_stats__(erts_lcnt_lock_stats_t *stats, int lock_in_conflict, erts_lcnt_time_t *time_waited);
-typedef struct {
- int id;
+ERTS_GLB_INLINE
+erts_lcnt_lock_stats_t *lcnt_get_lock_stats__(erts_lcnt_lock_info_t *info, char *file, unsigned int line);
- erts_lcnt_time_t timer; /* timer */
- int timer_set; /* bool */
- int lock_in_conflict; /* bool */
-} erts_lcnt_thread_data_t;
+ERTS_GLB_INLINE
+void lcnt_dec_lock_state__(ethr_atomic_t *l_state);
-/* globals */
+ERTS_GLB_INLINE
+void lcnt_time__(erts_lcnt_time_t *time);
-extern Uint16 erts_lcnt_rt_options;
+ERTS_GLB_INLINE
+void lcnt_time_add__(erts_lcnt_time_t *t, erts_lcnt_time_t *d);
-/* function declerations */
+ERTS_GLB_INLINE
+void lcnt_time_diff__(erts_lcnt_time_t *d, erts_lcnt_time_t *t1, erts_lcnt_time_t *t0);
-void erts_lcnt_init(void);
-void erts_lcnt_late_init(void);
+ERTS_GLB_INLINE
+void lcnt_retain_carrier__(erts_lcnt_lock_info_carrier_t *carrier);
-/* thread operations */
-void erts_lcnt_thread_setup(void);
-void erts_lcnt_thread_exit_handler(void);
+ERTS_GLB_INLINE
+void lcnt_release_carrier__(erts_lcnt_lock_info_carrier_t *carrier);
+
+ERTS_GLB_INLINE
+lcnt_thread_data_t__ *lcnt_get_thread_data__(void);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE
+void lcnt_time__(erts_lcnt_time_t *time) {
+ /*
+ * erts_sys_hrtime() is the highest resolution
+ * we could find, it may or may not be monotonic...
+ */
+ ErtsMonotonicTime mtime = erts_sys_hrtime();
+ time->s = (unsigned long) (mtime / 1000000000LL);
+ time->ns = (unsigned long) (mtime - 1000000000LL*time->s);
+}
+
+/* difference d must be non-negative */
+
+ERTS_GLB_INLINE
+void lcnt_time_add__(erts_lcnt_time_t *t, erts_lcnt_time_t *d) {
+ t->s += d->s;
+ t->ns += d->ns;
+
+ t->s += t->ns / 1000000000LL;
+ t->ns = t->ns % 1000000000LL;
+}
+
+ERTS_GLB_INLINE
+void lcnt_time_diff__(erts_lcnt_time_t *d, erts_lcnt_time_t *t1, erts_lcnt_time_t *t0) {
+ long ds;
+ long dns;
+
+ ds = t1->s - t0->s;
+ dns = t1->ns - t0->ns;
+
+ /* the difference should not be able to get bigger than 1 sec in ns*/
+
+ if (dns < 0) {
+ ds -= 1;
+ dns += 1000000000LL;
+ }
+
+ ASSERT(ds >= 0);
+
+ d->s = ds;
+ d->ns = dns;
+}
+
+ERTS_GLB_INLINE
+int lcnt_log2__(Uint64 v) {
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v |= v >> 32;
+
+ return lcnt_log2_tab64__[((Uint64)((v - (v >> 1))*0x07EDD5E59A4E28C2)) >> 58];
+}
+
+ERTS_GLB_INLINE
+void lcnt_update_wait_histogram__(erts_lcnt_hist_t *hist, erts_lcnt_time_t *time_waited) {
+ int idx;
+
+ if(time_waited->s > 0 || time_waited->ns > ERTS_LCNT_HISTOGRAM_MAX_NS) {
+ idx = ERTS_LCNT_HISTOGRAM_SLOT_SIZE - 1;
+ } else {
+ unsigned long r = time_waited->ns >> ERTS_LCNT_HISTOGRAM_RSHIFT;
+
+ idx = r ? lcnt_log2__(r) : 0;
+ }
+
+ hist->ns[idx]++;
+}
+
+ERTS_GLB_INLINE
+void lcnt_update_stats__(erts_lcnt_lock_stats_t *stats, int lock_in_conflict, erts_lcnt_time_t *time_waited) {
+ ethr_atomic_inc(&stats->attempts);
+
+ if(lock_in_conflict) {
+ ethr_atomic_inc(&stats->collisions);
+ }
+
+ if(time_waited) {
+ stats->times_waited++;
+
+ lcnt_time_add__(&stats->total_time_waited, time_waited);
+ lcnt_update_wait_histogram__(&stats->wait_time_histogram, time_waited);
+ }
+}
+
+/* If we were installed while the lock was held, r/w_state will be 0 and we
+ * can't tell which unlock or unacquire operation was the last. To get around
+ * this we assume that all excess operations go *towards* zero rather than down
+ * to zero, eventually becoming consistent with the actual state once the lock
+ * is fully released.
+ *
+ * Conflicts might not be counted until the recorded state is fully consistent
+ * with the actual state, but there should be no other ill effects. */
+
+ERTS_GLB_INLINE
+void lcnt_dec_lock_state__(ethr_atomic_t *l_state) {
+ ethr_sint_t state = ethr_atomic_dec_read_acqb(l_state);
+
+ /* We can not assume that state is >= -1 here; unlock and unacquire might
+ * bring it below -1 and race to increment it back. */
+
+ if(state < 0) {
+ ethr_atomic_inc_acqb(l_state);
+ }
+}
+
+ERTS_GLB_INLINE
+erts_lcnt_lock_stats_t *lcnt_get_lock_stats__(erts_lcnt_lock_info_t *info, char *file, unsigned int line) {
+ unsigned int i;
+
+ ASSERT(info->location_count >= 1 && info->location_count <= ERTS_LCNT_MAX_LOCK_LOCATIONS);
+
+ for(i = 0; i < info->location_count; i++) {
+ erts_lcnt_lock_stats_t *stats = &info->location_stats[i];
+
+ if(stats->file == file && stats->line == line) {
+ return stats;
+ }
+ }
-/* list operations (local) */
-erts_lcnt_lock_list_t *erts_lcnt_list_init(void);
+ if(info->location_count < ERTS_LCNT_MAX_LOCK_LOCATIONS) {
+ erts_lcnt_lock_stats_t *stats = &info->location_stats[info->location_count];
-void erts_lcnt_list_insert(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock);
-void erts_lcnt_list_delete(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock);
+ stats->file = file;
+ stats->line = line;
-/* lock operations (global) */
-void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag);
-void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id);
-void erts_lcnt_init_lock_empty(erts_lcnt_lock_t *lock);
-void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock);
+ info->location_count++;
-void erts_lcnt_lock(erts_lcnt_lock_t *lock);
-void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option);
-void erts_lcnt_lock_post(erts_lcnt_lock_t *lock);
-void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line);
-void erts_lcnt_lock_unaquire(erts_lcnt_lock_t *lock);
+ return stats;
+ }
-void erts_lcnt_unlock(erts_lcnt_lock_t *lock);
-void erts_lcnt_unlock_opt(erts_lcnt_lock_t *lock, Uint16 option);
+ return &info->location_stats[0];
+}
-void erts_lcnt_trylock_opt(erts_lcnt_lock_t *lock, int res, Uint16 option);
-void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res);
+ERTS_GLB_INLINE
+lcnt_thread_data_t__ *lcnt_get_thread_data__(void) {
+ lcnt_thread_data_t__ *eltd = (lcnt_thread_data_t__ *)ethr_tsd_get(lcnt_thr_data_key__);
-/* bif interface */
-Uint16 erts_lcnt_set_rt_opt(Uint16 opt);
-Uint16 erts_lcnt_clear_rt_opt(Uint16 opt);
-void erts_lcnt_clear_counters(void);
-char *erts_lcnt_lock_type(Uint16 type);
-erts_lcnt_data_t *erts_lcnt_get_data(void);
+ ASSERT(eltd);
+
+ return eltd;
+}
+
+ERTS_GLB_FORCE_INLINE
+int erts_lcnt_open_ref(erts_lcnt_ref_t *ref, int *handle, erts_lcnt_lock_info_carrier_t **result) {
+ if(ERTS_LIKELY(!erts_lcnt_check_ref_installed(ref))) {
+ return 0;
+ }
+
+ ASSERT(lcnt_initialization_completed__);
+
+ (*handle) = lcnt_thr_progress_unmanaged_delay__();
+ (*result) = (erts_lcnt_lock_info_carrier_t*)ethr_atomic_read(ref);
+
+ if(*result) {
+ if(*handle != LCNT_THR_PRGR_DHANDLE_MANAGED) {
+ lcnt_retain_carrier__(*result);
+ lcnt_thr_progress_unmanaged_continue__(*handle);
+ }
+
+ return 1;
+ } else if(*handle != LCNT_THR_PRGR_DHANDLE_MANAGED) {
+ lcnt_thr_progress_unmanaged_continue__(*handle);
+ }
+
+ return 0;
+}
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_close_ref(int handle, erts_lcnt_lock_info_carrier_t *carrier) {
+ if(handle != LCNT_THR_PRGR_DHANDLE_MANAGED) {
+ lcnt_release_carrier__(carrier);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_init_ref(erts_lcnt_ref_t *ref) {
+ ethr_atomic_init(ref, (ethr_sint_t)NULL);
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_init_ref_x(erts_lcnt_ref_t *ref, const char *name,
+ Eterm id, erts_lock_flags_t flags) {
+ erts_lcnt_init_ref(ref);
+
+ if(flags & ERTS_LOCK_FLAGS_PROPERTY_STATIC) {
+ lcnt_register_static_lock__(ref, name, id, flags);
+ }
+
+ if(erts_lcnt_check_enabled(flags)) {
+ erts_lcnt_install_new_lock_info(ref, name, id, flags);
+ }
+}
+
+ERTS_GLB_FORCE_INLINE
+int erts_lcnt_check_ref_installed(erts_lcnt_ref_t *ref) {
+ return (!!*ethr_atomic_addr(ref));
+}
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_lock(erts_lcnt_ref_t *ref) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(ref, &handle, &carrier)) {
+ erts_lcnt_lock_idx(carrier, 0);
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_lock_opt(erts_lcnt_ref_t *ref, Uint16 option) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(ref, &handle, &carrier)) {
+ erts_lcnt_lock_opt_idx(carrier, 0, option);
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_lock_post(erts_lcnt_ref_t *ref) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(ref, &handle, &carrier)) {
+ erts_lcnt_lock_post_idx(carrier, 0);
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_lock_post_x(erts_lcnt_ref_t *ref, char *file, unsigned int line) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(ref, &handle, &carrier)) {
+ erts_lcnt_lock_post_x_idx(carrier, 0, file, line);
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_lock_unacquire(erts_lcnt_ref_t *ref) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(ref, &handle, &carrier)) {
+ erts_lcnt_lock_unacquire_idx(carrier, 0);
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_unlock(erts_lcnt_ref_t *ref) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(ref, &handle, &carrier)) {
+ erts_lcnt_unlock_idx(carrier, 0);
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_unlock_opt(erts_lcnt_ref_t *ref, Uint16 option) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(ref, &handle, &carrier)) {
+ erts_lcnt_unlock_opt_idx(carrier, 0, option);
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_trylock(erts_lcnt_ref_t *ref, int result) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(ref, &handle, &carrier)) {
+ erts_lcnt_trylock_idx(carrier, 0, result);
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_FORCE_INLINE
+void erts_lcnt_trylock_opt(erts_lcnt_ref_t *ref, int result, Uint16 option) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(ref, &handle, &carrier)) {
+ erts_lcnt_trylock_opt_idx(carrier, 0, result, option);
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_lock_idx(erts_lcnt_lock_info_carrier_t *carrier, int index) {
+ erts_lcnt_lock_opt_idx(carrier, index, ERTS_LOCK_OPTION_WRITE);
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_lock_opt_idx(erts_lcnt_lock_info_carrier_t *carrier, int index, Uint16 option) {
+ erts_lcnt_lock_info_t *info = &carrier->entries[index];
+
+ lcnt_thread_data_t__ *eltd = lcnt_get_thread_data__();
+
+ ASSERT(index < carrier->entry_count);
+
+ ASSERT((option & ERTS_LOCK_OPTION_READ) || (option & ERTS_LOCK_OPTION_WRITE));
+
+ if(option & ERTS_LOCK_OPTION_WRITE) {
+ ethr_sint_t w_state, r_state;
+
+ w_state = ethr_atomic_inc_read(&info->w_state) - 1;
+ r_state = ethr_atomic_read(&info->r_state);
+
+ /* We cannot acquire w_lock if either w or r are taken */
+ eltd->lock_in_conflict = (w_state > 0) || (r_state > 0);
+ } else {
+ ethr_sint_t w_state = ethr_atomic_read(&info->w_state);
+
+ /* We cannot acquire r_lock if w_lock is taken */
+ eltd->lock_in_conflict = (w_state > 0);
+ }
+
+ if(option & ERTS_LOCK_OPTION_READ) {
+ ASSERT(info->flags & ERTS_LOCK_FLAGS_PROPERTY_READ_WRITE);
+ ethr_atomic_inc(&info->r_state);
+ }
+
+ if(eltd->lock_in_conflict) {
+ /* Only set the timer if nobody else has it. This should only happen
+ * when proc_locks acquires several locks "atomically." All other locks
+ * will block the thread when locked (w_state > 0) */
+ if(eltd->timer_set == 0) {
+ lcnt_time__(&eltd->timer);
+ }
+
+ eltd->timer_set++;
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_lock_post_idx(erts_lcnt_lock_info_carrier_t *carrier, int index) {
+ erts_lcnt_lock_post_x_idx(carrier, index, NULL, 0);
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_lock_post_x_idx(erts_lcnt_lock_info_carrier_t *carrier, int index, char *file, unsigned int line) {
+ erts_lcnt_lock_info_t *info = &carrier->entries[index];
+
+ lcnt_thread_data_t__ *eltd = lcnt_get_thread_data__();
+ erts_lcnt_lock_stats_t *stats;
+
+ ASSERT(index < carrier->entry_count);
+
+ /* If the lock was in conflict, update the time spent waiting. */
+ stats = lcnt_get_lock_stats__(info, file, line);
+ if(eltd->timer_set) {
+ erts_lcnt_time_t time_wait;
+ erts_lcnt_time_t timer;
+
+ lcnt_time__(&timer);
+
+ lcnt_time_diff__(&time_wait, &timer, &eltd->timer);
+ lcnt_update_stats__(stats, eltd->lock_in_conflict, &time_wait);
+
+ eltd->timer_set--;
+
+ ASSERT(eltd->timer_set >= 0);
+ } else {
+ lcnt_update_stats__(stats, eltd->lock_in_conflict, NULL);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_unlock_idx(erts_lcnt_lock_info_carrier_t *carrier, int index) {
+ ASSERT(index < carrier->entry_count);
+
+ erts_lcnt_unlock_opt_idx(carrier, index, ERTS_LOCK_OPTION_WRITE);
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_unlock_opt_idx(erts_lcnt_lock_info_carrier_t *carrier, int index, Uint16 option) {
+ erts_lcnt_lock_info_t *info = &carrier->entries[index];
+
+ ASSERT(index < carrier->entry_count);
+
+ ASSERT((option & ERTS_LOCK_OPTION_READ) || (option & ERTS_LOCK_OPTION_WRITE));
+
+ if(option & ERTS_LOCK_OPTION_WRITE) {
+ lcnt_dec_lock_state__(&info->w_state);
+ }
+
+ if(option & ERTS_LOCK_OPTION_READ) {
+ ASSERT(info->flags & ERTS_LOCK_FLAGS_PROPERTY_READ_WRITE);
+ lcnt_dec_lock_state__(&info->r_state);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_lock_unacquire_idx(erts_lcnt_lock_info_carrier_t *carrier, int index) {
+ erts_lcnt_lock_info_t *info = &carrier->entries[index];
+
+ ASSERT(index < carrier->entry_count);
+
+ lcnt_dec_lock_state__(&info->w_state);
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_trylock_idx(erts_lcnt_lock_info_carrier_t *carrier, int index, int result) {
+ ASSERT(index < carrier->entry_count);
+
+ erts_lcnt_trylock_opt_idx(carrier, index, result, ERTS_LOCK_OPTION_WRITE);
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_trylock_opt_idx(erts_lcnt_lock_info_carrier_t *carrier, int index, int result, Uint16 option) {
+ erts_lcnt_lock_info_t *info = &carrier->entries[index];
+
+ ASSERT(index < carrier->entry_count);
+
+ ASSERT((option & ERTS_LOCK_OPTION_READ) || (option & ERTS_LOCK_OPTION_WRITE));
+
+ if(result != EBUSY) {
+ if(option & ERTS_LOCK_OPTION_WRITE) {
+ ethr_atomic_inc(&info->w_state);
+ }
+
+ if(option & ERTS_LOCK_OPTION_READ) {
+ ASSERT(info->flags & ERTS_LOCK_FLAGS_PROPERTY_READ_WRITE);
+ ethr_atomic_inc(&info->r_state);
+ }
+
+ lcnt_update_stats__(&info->location_stats[0], 0, NULL);
+ } else {
+ ethr_atomic_inc(&info->location_stats[0].attempts);
+ ethr_atomic_inc(&info->location_stats[0].collisions);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_init_lock_info_idx(erts_lcnt_lock_info_carrier_t *carrier, int index,
+ const char *name, Eterm id, erts_lock_flags_t flags) {
+ erts_lcnt_lock_info_t *info = &carrier->entries[index];
+
+ ASSERT(is_immed(id));
+
+ ASSERT(flags & ERTS_LOCK_FLAGS_MASK_TYPE);
+ ASSERT(flags & ERTS_LOCK_FLAGS_MASK_CATEGORY);
+
+ info->flags = flags;
+ info->name = name;
+ info->id = id;
+}
+
+ERTS_GLB_INLINE
+void lcnt_retain_carrier__(erts_lcnt_lock_info_carrier_t *carrier) {
+#ifdef DEBUG
+ ASSERT(ethr_atomic_inc_read_acqb(&carrier->ref_count) >= 2);
+#else
+ ethr_atomic_inc_acqb(&carrier->ref_count);
+#endif
+}
+
+ERTS_GLB_INLINE
+void lcnt_release_carrier__(erts_lcnt_lock_info_carrier_t *carrier) {
+ ethr_sint_t count = ethr_atomic_dec_read_relb(&carrier->ref_count);
+
+ ASSERT(count >= 0);
+
+ if(count == 0) {
+ lcnt_deallocate_carrier__(carrier);
+ }
+}
+
+#endif
#endif /* ifdef ERTS_ENABLE_LOCK_COUNT */
#endif /* ifndef ERTS_LOCK_COUNT_H__ */
diff --git a/erts/emulator/beam/erl_lock_flags.c b/erts/emulator/beam/erl_lock_flags.c
new file mode 100644
index 0000000000..ba6a7217f2
--- /dev/null
+++ b/erts/emulator/beam/erl_lock_flags.c
@@ -0,0 +1,46 @@
+/*
+ * %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%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "erl_lock_flags.h"
+
+const char *erts_lock_flags_get_type_name(erts_lock_flags_t flags) {
+ switch(flags & ERTS_LOCK_FLAGS_MASK_TYPE) {
+ case ERTS_LOCK_FLAGS_TYPE_PROCLOCK:
+ return "proclock";
+ case ERTS_LOCK_FLAGS_TYPE_MUTEX:
+ if(flags & ERTS_LOCK_FLAGS_PROPERTY_READ_WRITE) {
+ return "rw_mutex";
+ }
+
+ return "mutex";
+ case ERTS_LOCK_FLAGS_TYPE_SPINLOCK:
+ if(flags & ERTS_LOCK_FLAGS_PROPERTY_READ_WRITE) {
+ return "rw_spinlock";
+ }
+
+ return "spinlock";
+ default:
+ return "garbage";
+ }
+}
diff --git a/erts/emulator/beam/erl_lock_flags.h b/erts/emulator/beam/erl_lock_flags.h
new file mode 100644
index 0000000000..b66c160af5
--- /dev/null
+++ b/erts/emulator/beam/erl_lock_flags.h
@@ -0,0 +1,74 @@
+/*
+ * %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%
+ */
+
+#ifndef ERTS_LOCK_FLAGS_H__
+#define ERTS_LOCK_FLAGS_H__
+
+#define ERTS_LOCK_OPTION_READ (1 << 1)
+#define ERTS_LOCK_OPTION_WRITE (1 << 2)
+
+#define ERTS_LOCK_OPTION_RDWR (ERTS_LOCK_OPTION_READ | ERTS_LOCK_OPTION_WRITE)
+
+/* Property/category are bitfields to simplify their use in masks. */
+#define ERTS_LOCK_FLAGS_MASK_CATEGORY (0xFFC0)
+#define ERTS_LOCK_FLAGS_MASK_PROPERTY (0x0030)
+
+/* Type is a plain number. */
+#define ERTS_LOCK_FLAGS_MASK_TYPE (0x000F)
+
+#define ERTS_LOCK_FLAGS_TYPE_SPINLOCK (1)
+#define ERTS_LOCK_FLAGS_TYPE_MUTEX (2)
+#define ERTS_LOCK_FLAGS_TYPE_PROCLOCK (3)
+
+/* "Static" guarantees that the lock will never be destroyed once created. */
+#define ERTS_LOCK_FLAGS_PROPERTY_STATIC (1 << 4)
+#define ERTS_LOCK_FLAGS_PROPERTY_READ_WRITE (1 << 5)
+
+#define ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR (1 << 6)
+#define ERTS_LOCK_FLAGS_CATEGORY_PROCESS (1 << 7)
+#define ERTS_LOCK_FLAGS_CATEGORY_IO (1 << 8)
+#define ERTS_LOCK_FLAGS_CATEGORY_DB (1 << 9)
+#define ERTS_LOCK_FLAGS_CATEGORY_DEBUG (1 << 10)
+#define ERTS_LOCK_FLAGS_CATEGORY_SCHEDULER (1 << 11)
+#define ERTS_LOCK_FLAGS_CATEGORY_GENERIC (1 << 12)
+#define ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION (1 << 13)
+
+#define ERTS_LOCK_TYPE_SPINLOCK \
+ (ERTS_LOCK_FLAGS_TYPE_SPINLOCK)
+#define ERTS_LOCK_TYPE_RWSPINLOCK \
+ (ERTS_LOCK_TYPE_SPINLOCK | \
+ ERTS_LOCK_FLAGS_PROPERTY_READ_WRITE)
+#define ERTS_LOCK_TYPE_MUTEX \
+ (ERTS_LOCK_FLAGS_TYPE_MUTEX)
+#define ERTS_LOCK_TYPE_RWMUTEX \
+ (ERTS_LOCK_TYPE_MUTEX | \
+ ERTS_LOCK_FLAGS_PROPERTY_READ_WRITE)
+#define ERTS_LOCK_TYPE_PROCLOCK \
+ (ERTS_LOCK_FLAGS_CATEGORY_PROCESS | \
+ ERTS_LOCK_FLAGS_TYPE_PROCLOCK)
+
+/* -- -- */
+
+typedef unsigned short erts_lock_flags_t;
+
+/* @brief Gets the type name of the lock, honoring the RW flag if supplied. */
+const char *erts_lock_flags_get_type_name(erts_lock_flags_t flags);
+
+#endif /* ERTS_LOCK_FLAGS_H__ */
diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c
index 2d70f0d874..6c477be615 100644
--- a/erts/emulator/beam/erl_msacc.c
+++ b/erts/emulator/beam/erl_msacc.c
@@ -76,7 +76,8 @@ void erts_msacc_early_init(void) {
#ifndef ERTS_MSACC_ALWAYS_ON
erts_msacc_enabled = 0;
#endif
- erts_rwmtx_init(&msacc_mutex,"msacc_list_mutex");
+ erts_rwmtx_init(&msacc_mutex, "msacc_list_mutex", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
#ifdef USE_THREADS
erts_tsd_key_create(&erts_msacc_key,"erts_msacc_key");
#else
@@ -109,7 +110,8 @@ void erts_msacc_init_thread(char *type, int id, int managed) {
#ifdef USE_THREADS
erts_rwmtx_rwlock(&msacc_mutex);
if (!managed) {
- erts_mtx_init(&msacc->mtx,"msacc_unmanaged_mutex");
+ erts_mtx_init(&msacc->mtx, "msacc_unmanaged_mutex", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
msacc->next = msacc_unmanaged;
msacc_unmanaged = msacc;
msacc_unmanaged_count++;
diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c
index 19bb7d5b31..f2a660f085 100644
--- a/erts/emulator/beam/erl_mtrace.c
+++ b/erts/emulator/beam/erl_mtrace.c
@@ -583,8 +583,10 @@ void erts_mtrace_init(char *receiver, char *nodename)
byte ip_addr[4];
Uint16 port;
- erts_mtx_init(&mtrace_buf_mutex, "mtrace_buf");
- erts_mtx_init(&mtrace_op_mutex, "mtrace_op");
+ erts_mtx_init(&mtrace_buf_mutex, "mtrace_buf", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
+ erts_mtx_init(&mtrace_op_mutex, "mtrace_op", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
socket_desc = erts_sock_open();
if (socket_desc == ERTS_SOCK_INVALID_SOCKET) {
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 4815e5e7bb..848e116621 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -2456,7 +2456,8 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz)
erts_refc_inc(&resource->type->refc, 2);
if (type->down) {
resource->monitors = (ErtsResourceMonitors*) (resource->data + monitors_offs);
- erts_smp_mtx_init(&resource->monitors->lock, "resource_monitors");
+ erts_smp_mtx_init(&resource->monitors->lock, "resource_monitors", NIL,
+ ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
resource->monitors->root = NULL;
resource->monitors->pending_failed_fire = 0;
resource->monitors->is_dying = 0;
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 3c5945d48d..deadf435e9 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -85,21 +85,20 @@ dist_table_cmp(void *dep1, void *dep2)
static void*
dist_table_alloc(void *dep_tmpl)
{
- Eterm chnl_nr;
Eterm sysname;
DistEntry *dep;
erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ;
sysname = ((DistEntry *) dep_tmpl)->sysname;
- chnl_nr = make_small((Uint) atom_val(sysname));
dep = (DistEntry *) erts_alloc(ERTS_ALC_T_DIST_ENTRY, sizeof(DistEntry));
dist_entries++;
dep->prev = NULL;
erts_smp_refc_init(&dep->refc, -1);
- erts_smp_rwmtx_init_opt_x(&dep->rwmtx, &rwmtx_opt, "dist_entry", chnl_nr);
+ erts_smp_rwmtx_init_opt(&dep->rwmtx, &rwmtx_opt, "dist_entry", sysname,
+ ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
dep->sysname = sysname;
dep->cid = NIL;
dep->connection_id = 0;
@@ -107,12 +106,14 @@ dist_table_alloc(void *dep_tmpl)
dep->flags = 0;
dep->version = 0;
- erts_smp_mtx_init_x(&dep->lnk_mtx, "dist_entry_links", chnl_nr);
+ erts_smp_mtx_init(&dep->lnk_mtx, "dist_entry_links", sysname,
+ ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
dep->node_links = NULL;
dep->nlinks = NULL;
dep->monitors = NULL;
- erts_smp_mtx_init_x(&dep->qlock, "dist_entry_out_queue", chnl_nr);
+ erts_smp_mtx_init(&dep->qlock, "dist_entry_out_queue", sysname,
+ ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
dep->qflgs = 0;
dep->qsize = 0;
dep->out_queue.first = NULL;
@@ -760,8 +761,10 @@ void erts_init_node_tables(int dd_sec)
rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ;
rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED;
- erts_smp_rwmtx_init_opt(&erts_node_table_rwmtx, &rwmtx_opt, "node_table");
- erts_smp_rwmtx_init_opt(&erts_dist_table_rwmtx, &rwmtx_opt, "dist_table");
+ erts_smp_rwmtx_init_opt(&erts_node_table_rwmtx, &rwmtx_opt, "node_table", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+ erts_smp_rwmtx_init_opt(&erts_dist_table_rwmtx, &rwmtx_opt, "dist_table", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
f.hash = (H_FUN) dist_table_hash;
f.cmp = (HCMP_FUN) dist_table_cmp;
@@ -818,6 +821,33 @@ int erts_lc_is_de_rlocked(DistEntry *dep)
#endif
#endif
+#ifdef ERTS_ENABLE_LOCK_COUNT
+
+static void erts_lcnt_enable_dist_lock_count(void *dep_raw, void *enable) {
+ DistEntry *dep = (DistEntry*)dep_raw;
+
+ if(enable) {
+ erts_lcnt_install_new_lock_info(&dep->rwmtx.lcnt, "dist_entry", dep->sysname,
+ ERTS_LOCK_TYPE_RWMUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+ erts_lcnt_install_new_lock_info(&dep->lnk_mtx.lcnt, "dist_entry_links", dep->sysname,
+ ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+ erts_lcnt_install_new_lock_info(&dep->qlock.lcnt, "dist_entry_out_queue", dep->sysname,
+ ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION);
+ } else {
+ erts_lcnt_uninstall(&dep->rwmtx.lcnt);
+ erts_lcnt_uninstall(&dep->lnk_mtx.lcnt);
+ erts_lcnt_uninstall(&dep->qlock.lcnt);
+ }
+}
+
+void erts_lcnt_update_distribution_locks(int enable) {
+ erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx);
+ hash_foreach(&erts_dist_table, erts_lcnt_enable_dist_lock_count,
+ (void*)(UWord)enable);
+ erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx);
+}
+#endif
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* The following is only supposed to be used for testing, and debugging. *
* *
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index 489da1ba17..91bcb4fce1 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -195,6 +195,10 @@ int erts_lc_is_de_rwlocked(DistEntry *);
int erts_lc_is_de_rlocked(DistEntry *);
#endif
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void erts_lcnt_update_distribution_locks(int enable);
+#endif
+
ERTS_GLB_INLINE void erts_deref_dist_entry(DistEntry *dep);
ERTS_GLB_INLINE void erts_deref_node_entry(ErlNode *np);
ERTS_GLB_INLINE void erts_smp_de_rlock(DistEntry *dep);
diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h
index 5c947ad1c0..6a3213ec52 100644
--- a/erts/emulator/beam/erl_port.h
+++ b/erts/emulator/beam/erl_port.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2012-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/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index 55526e1d5e..1ab1e47254 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2006-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.
@@ -852,10 +852,11 @@ schedule_port_task_handle_list_free(ErtsPortTaskHandleList *pthlp)
}
static ERTS_INLINE void
-abort_nosuspend_task(Port *pp,
- ErtsPortTaskType type,
- ErtsPortTaskTypeData *tdp,
- int bpq_data)
+abort_signal_task(Port *pp,
+ int abort_type,
+ ErtsPortTaskType type,
+ ErtsPortTaskTypeData *tdp,
+ int bpq_data)
{
ASSERT(type == ERTS_PORT_TASK_PROC_SIG);
@@ -863,18 +864,28 @@ abort_nosuspend_task(Port *pp,
if (!bpq_data)
tdp->psig.callback(NULL,
ERTS_PORT_SFLG_INVALID,
- ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND,
+ abort_type,
&tdp->psig.data);
else {
ErlDrvSizeT size = erts_proc2port_sig_command_data_size(&tdp->psig.data);
tdp->psig.callback(NULL,
ERTS_PORT_SFLG_INVALID,
- ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND,
+ abort_type,
&tdp->psig.data);
aborted_proc2port_data(pp, size);
}
}
+
+static ERTS_INLINE void
+abort_nosuspend_task(Port *pp,
+ ErtsPortTaskType type,
+ ErtsPortTaskTypeData *tdp,
+ int bpq_data)
+{
+ abort_signal_task(pp, ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND, type, tdp, bpq_data);
+}
+
static ErtsPortTaskHandleList *
get_free_nosuspend_handles(Port *pp)
{
@@ -1613,8 +1624,9 @@ abort_nosuspend:
ASSERT(ns_pthlp);
erts_free(ERTS_ALC_T_PT_HNDL_LIST, ns_pthlp);
- if (ptp)
- port_task_free(ptp);
+
+ ASSERT(ptp);
+ port_task_free(ptp);
return 0;
@@ -1625,12 +1637,15 @@ fail:
erts_port_dec_refc(pp);
#endif
+ if (ptp) {
+ abort_signal_task(pp, ERTS_PROC2PORT_SIG_ABORT,
+ ptp->type, &ptp->u.alive.td, 0);
+ port_task_free(ptp);
+ }
+
if (ns_pthlp)
erts_free(ERTS_ALC_T_PT_HNDL_LIST, ns_pthlp);
- if (ptp)
- port_task_free(ptp);
-
return -1;
}
diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h
index ab536c6f27..39f403b443 100644
--- a/erts/emulator/beam/erl_port_task.h
+++ b/erts/emulator/beam/erl_port_task.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2006-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.
@@ -188,13 +188,7 @@ erts_port_task_init_sched(ErtsPortTaskSched *ptsp, Eterm instr_id)
ptsp->taskq.in.last = NULL;
erts_smp_atomic32_init_nob(&ptsp->flags, 0);
#ifdef ERTS_SMP
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_mtx_init_x_opt(&ptsp->mtx, lock_str, instr_id,
- ((erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK)
- ? 0 : ERTS_LCNT_LT_DISABLE));
-#else
- erts_mtx_init_x(&ptsp->mtx, lock_str, instr_id);
-#endif
+ erts_mtx_init(&ptsp->mtx, lock_str, instr_id, ERTS_LOCK_FLAGS_CATEGORY_IO);
#endif
}
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index fc2b34e70f..d58203e9e1 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -841,7 +841,9 @@ erts_late_init_process(void)
{
int ix;
- erts_smp_spinlock_init(&erts_sched_stat.lock, "sched_stat");
+ erts_smp_spinlock_init(&erts_sched_stat.lock, "sched_stat", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_SCHEDULER);
+
for (ix = 0; ix < ERTS_NO_PRIO_LEVELS; ix++) {
Eterm atom;
char *atom_str;
@@ -1289,10 +1291,8 @@ reply_sched_wall_time(void *vswtrp)
ErlOffHeap *ohp = NULL;
ErtsMessage *mp = NULL;
- ASSERT(esdp);
-#ifdef ERTS_DIRTY_SCHEDULERS
- ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp));
-#endif
+ ASSERT(esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp));
+
if (swtrp->set) {
if (!swtrp->enable && esdp->sched_wall_time.enabled) {
esdp->sched_wall_time.u.need = erts_sched_balance_util;
@@ -1458,11 +1458,10 @@ erts_sched_wall_time_request(Process *c_p, int set, int enable,
ErtsSchedWallTimeReq *swtrp;
Eterm *hp;
+ ASSERT(esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp));
+
if (!set && !esdp->sched_wall_time.enabled)
return THE_NON_VALUE;
-#ifdef ERTS_DIRTY_SCHEDULERS
- ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp));
-#endif
swtrp = swtreq_alloc();
ref = erts_make_ref(c_p);
@@ -1509,10 +1508,7 @@ reply_system_check(void *vscrp)
ErlOffHeap *ohp = NULL;
ErtsMessage *mp = NULL;
- ASSERT(esdp);
-#ifdef ERTS_DIRTY_SCHEDULERS
- ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp));
-#endif
+ ASSERT(esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp));
sz = ERTS_REF_THING_SIZE;
mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
@@ -1778,9 +1774,9 @@ static ERTS_INLINE void
haw_thr_prgr_current_check_progress(ErtsAuxWorkData *awdp)
{
ErtsThrPrgrVal current = awdp->current_thr_prgr;
-#ifdef ERTS_DIRTY_SCHEDULERS
+
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
+
if (current != ERTS_THR_PRGR_INVALID
&& !erts_thr_progress_equal(current, erts_thr_progress_current())) {
/*
@@ -1797,9 +1793,7 @@ handle_delayed_aux_work_wakeup(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, in
{
int jix, max_jix;
-#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
ASSERT(awdp->delayed_wakeup.next != ERTS_DELAYED_WAKEUP_INFINITY);
@@ -1956,9 +1950,8 @@ handle_misc_aux_work_thr_prgr(ErtsAuxWorkData *awdp,
erts_aint32_t aux_work,
int waiting)
{
-#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
+
if (!erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp),
awdp->misc.thr_prgr))
return aux_work & ~ERTS_SSI_AUX_WORK_MISC_THR_PRGR;
@@ -2010,12 +2003,13 @@ erts_schedule_multi_misc_aux_work(int ignore_self,
int id, self = 0;
if (ignore_self) {
- ErtsSchedulerData *esdp = erts_get_scheduler_data();
-#ifdef ERTS_DIRTY_SCHEDULERS
- ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp));
-#endif
- if (esdp)
- self = (int) esdp->no;
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+
+ /* ignore_self is meaningless on dirty schedulers since aux work can
+ * only run on normal schedulers, and their ids do not translate. */
+ if(esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ self = (int)esdp->no;
+ }
}
ASSERT(0 < max_sched && max_sched <= erts_no_schedulers);
@@ -2043,9 +2037,9 @@ handle_async_ready(ErtsAuxWorkData *awdp,
int waiting)
{
ErtsSchedulerSleepInfo *ssi = awdp->ssi;
-#ifdef ERTS_DIRTY_SCHEDULERS
+
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
+
unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_ASYNC_READY);
if (erts_check_async_ready(awdp->async_ready.queue)) {
if (set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_ASYNC_READY)
@@ -2070,9 +2064,8 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp,
{
void *thr_prgr_p;
-#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
+
#ifdef ERTS_SMP
if (awdp->async_ready.need_thr_prgr
&& !erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp),
@@ -2110,9 +2103,8 @@ handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
ErtsSchedulerSleepInfo *ssi = awdp->ssi;
erts_aint32_t res;
-#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
+
unset_aux_work_flags(ssi, (ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM
| ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC));
aux_work &= ~(ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM
@@ -2160,9 +2152,9 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waitin
ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID;
int more_work = 0;
ERTS_MSACC_PUSH_STATE_M_X();
-#ifdef ERTS_DIRTY_SCHEDULERS
+
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
+
unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD);
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ALLOC);
erts_alloc_scheduler_handle_delayed_dealloc((void *) awdp->esdp,
@@ -2199,9 +2191,8 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, i
ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID;
ErtsThrPrgrVal current = haw_thr_prgr_current(awdp);
-#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
+
if (!erts_thr_progress_has_reached_this(current, awdp->dd.thr_prgr))
return aux_work & ~ERTS_SSI_AUX_WORK_DD_THR_PRGR;
@@ -2258,9 +2249,8 @@ handle_canceled_timers(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waitin
ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID;
int more_work = 0;
-#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
+
unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_CNCLD_TMRS);
erts_handle_canceled_timers((void *) awdp->esdp,
&need_thr_progress,
@@ -2294,9 +2284,8 @@ handle_canceled_timers_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, i
ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID;
ErtsThrPrgrVal current = haw_thr_prgr_current(awdp);
-#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
+
if (!erts_thr_progress_has_reached_this(current, awdp->cncld_tmrs.thr_prgr))
return aux_work & ~ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR;
@@ -2339,9 +2328,8 @@ handle_thr_prgr_later_op(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int wait
int lops;
ErtsThrPrgrVal current = haw_thr_prgr_current(awdp);
-#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
+
for (lops = 0; lops < ERTS_MAX_THR_PRGR_LATER_OPS; lops++) {
ErtsThrPrgrLaterOp *lop = awdp->later_op.first;
@@ -2371,7 +2359,7 @@ enqueue_later_op(ErtsSchedulerData *esdp,
ErtsThrPrgrLaterOp *lop)
{
ErtsThrPrgrVal later = erts_thr_progress_later(esdp);
- ASSERT(esdp);
+ ASSERT(esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp));
lop->func = later_func;
lop->data = later_data;
@@ -2424,9 +2412,7 @@ handle_debug_wait_completed(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int w
ErtsSchedulerSleepInfo *ssi = awdp->ssi;
erts_aint32_t saved_aux_work, flags;
-#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!awdp->esdp || !ERTS_SCHEDULER_IS_DIRTY(awdp->esdp));
-#endif
flags = awdp->debug.wait_completed.flags;
@@ -3008,9 +2994,9 @@ static ERTS_INLINE void
sched_active_sys(Uint no, ErtsRunQueue *rq)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
-#ifdef ERTS_DIRTY_SCHEDULERS
+
ASSERT(!ERTS_RUNQ_IX_IS_DIRTY(rq->ix));
-#endif
+
ASSERT(rq->waiting < 0);
rq->waiting *= -1;
rq->waiting--;
@@ -3075,9 +3061,9 @@ static ERTS_INLINE void
sched_change_waiting_sys_to_waiting(Uint no, ErtsRunQueue *rq)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
-#ifdef ERTS_DIRTY_SCHEDULERS
+
ASSERT(!ERTS_RUNQ_IX_IS_DIRTY(rq->ix));
-#endif
+
ASSERT(rq->waiting < 0);
rq->waiting *= -1;
}
@@ -3586,9 +3572,8 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
erts_smp_atomic32_set_relb(&function_calls, 0);
*fcalls = 0;
-#ifdef ERTS_DIRTY_SCHEDULERS
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(esdp));
-#endif
+
sched_waiting_sys(esdp->no, rq);
erts_smp_runq_unlock(rq);
@@ -6232,13 +6217,17 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online
* id if the esdp->no <-> ix+1 mapping change.
*/
- erts_smp_mtx_init_x(&rq->mtx, "run_queue", make_small(ix + 1));
+ erts_smp_mtx_init(&rq->mtx, "run_queue", make_small(ix + 1),
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_SCHEDULER);
erts_smp_cnd_init(&rq->cnd);
#ifdef ERTS_DIRTY_SCHEDULERS
#ifdef ERTS_SMP
- if (ERTS_RUNQ_IX_IS_DIRTY(ix))
- erts_smp_spinlock_init(&rq->sleepers.lock, "dirty_run_queue_sleep_list");
+ if (ERTS_RUNQ_IX_IS_DIRTY(ix)) {
+ erts_smp_spinlock_init(&rq->sleepers.lock, "dirty_run_queue_sleep_list",
+ make_small(ix + 1),
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_SCHEDULER);
+ }
rq->sleepers.list = NULL;
#endif
#endif
@@ -6431,7 +6420,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online
init_no_runqs(no_schedulers_online, no_schedulers_online);
balance_info.last_active_runqs = no_schedulers;
- erts_smp_mtx_init(&balance_info.update_mtx, "migration_info_update");
+ erts_smp_mtx_init(&balance_info.update_mtx, "migration_info_update", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_SCHEDULER);
balance_info.forced_check_balance = 0;
balance_info.halftime = 1;
balance_info.full_reds_history_index = 0;
@@ -7493,7 +7483,8 @@ sched_set_suspended_sleeptype(ErtsSchedulerSleepInfo *ssi)
static void
init_scheduler_suspend(void)
{
- erts_smp_mtx_init(&schdlr_sspnd.mtx, "schdlr_sspnd");
+ erts_smp_mtx_init(&schdlr_sspnd.mtx, "schdlr_sspnd", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_SCHEDULER);
schdlr_sspnd.online.normal = 1;
schdlr_sspnd.curr_online.normal = 1;
schdlr_sspnd.active.normal = 1;
diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c
index c0e7380ed0..23c7414901 100644
--- a/erts/emulator/beam/erl_process_lock.c
+++ b/erts/emulator/beam/erl_process_lock.c
@@ -112,21 +112,13 @@ static struct {
erts_pix_lock_t erts_pix_locks[ERTS_NO_OF_PIX_LOCKS];
-#ifdef ERTS_ENABLE_LOCK_COUNT
-static void lcnt_enable_proc_lock_count(Process *proc, int enable);
-#endif
-
void
erts_init_proc_lock(int cpus)
{
int i;
for (i = 0; i < ERTS_NO_OF_PIX_LOCKS; i++) {
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_mtx_init_x(&erts_pix_locks[i].u.mtx,
- "pix_lock", make_small(i));
-#else
- erts_mtx_init(&erts_pix_locks[i].u.mtx, "pix_lock");
-#endif
+ erts_mtx_init(&erts_pix_locks[i].u.mtx, "pix_lock", make_small(i),
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
}
#if ERTS_PROC_LOCK_OWN_IMPL
erts_thr_install_exit_handler(cleanup_tse);
@@ -944,7 +936,7 @@ erts_pid2proc_opt(Process *c_p,
erts_proc_inc_refc(proc);
#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
- erts_lcnt_proc_lock_unaquire(&proc->lock, lcnt_locks);
+ erts_lcnt_proc_lock_unacquire(&proc->lock, lcnt_locks);
#endif
managed = dhndl == ERTS_THR_PRGR_DHANDLE_MANAGED;
@@ -1062,32 +1054,38 @@ erts_proc_lock_init(Process *p)
#endif
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
- erts_mtx_init_x(&p->lock.main, "proc_main", p->common.id);
+ erts_mtx_init(&p->lock.main, "proc_main", p->common.id,
+ ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
ethr_mutex_lock(&p->lock.main.mtx);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.main.lc);
#endif
- erts_mtx_init_x(&p->lock.link, "proc_link", p->common.id);
+ erts_mtx_init(&p->lock.link, "proc_link", p->common.id,
+ ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
ethr_mutex_lock(&p->lock.link.mtx);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.link.lc);
#endif
- erts_mtx_init_x(&p->lock.msgq, "proc_msgq", p->common.id);
+ erts_mtx_init(&p->lock.msgq, "proc_msgq", p->common.id,
+ ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
ethr_mutex_lock(&p->lock.msgq.mtx);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.msgq.lc);
#endif
- erts_mtx_init_x(&p->lock.btm, "proc_btm", p->common.id);
+ erts_mtx_init(&p->lock.btm, "proc_btm", p->common.id,
+ ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
ethr_mutex_lock(&p->lock.btm.mtx);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.btm.lc);
#endif
- erts_mtx_init_x(&p->lock.status, "proc_status", p->common.id);
+ erts_mtx_init(&p->lock.status, "proc_status", p->common.id,
+ ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
ethr_mutex_lock(&p->lock.status.mtx);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.status.lc);
#endif
- erts_mtx_init_x(&p->lock.trace, "proc_trace", p->common.id);
+ erts_mtx_init(&p->lock.trace, "proc_trace", p->common.id,
+ ERTS_LOCK_FLAGS_CATEGORY_PROCESS);
ethr_mutex_lock(&p->lock.trace.mtx);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.trace.lc);
@@ -1124,117 +1122,70 @@ erts_proc_lock_fin(Process *p)
#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
-void erts_lcnt_enable_proc_lock_count(int enable) {
- int ix, max = erts_ptab_max(&erts_proc);
- Process *proc = NULL;
- for (ix = 0; ix < max; ++ix) {
- if ((proc = erts_pix2proc(ix)) != NULL)
- lcnt_enable_proc_lock_count(proc, enable);
- } /* for all processes */
-}
-
void erts_lcnt_proc_lock_init(Process *p) {
- if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) {
- erts_lcnt_init_lock_empty(&(p->lock.lcnt_main));
- erts_lcnt_init_lock_empty(&(p->lock.lcnt_link));
- erts_lcnt_init_lock_empty(&(p->lock.lcnt_msgq));
- erts_lcnt_init_lock_empty(&(p->lock.lcnt_btm));
- erts_lcnt_init_lock_empty(&(p->lock.lcnt_status));
- erts_lcnt_init_lock_empty(&(p->lock.lcnt_trace));
- } else { /* now the common case */
- Eterm pid = (p->common.id != ERTS_INVALID_PID) ? p->common.id : NIL;
- erts_lcnt_init_lock_x(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK, pid);
- erts_lcnt_init_lock_x(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK, pid);
- erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK, pid);
- erts_lcnt_init_lock_x(&(p->lock.lcnt_btm), "proc_btm", ERTS_LCNT_LT_PROCLOCK, pid);
- erts_lcnt_init_lock_x(&(p->lock.lcnt_status),"proc_status",ERTS_LCNT_LT_PROCLOCK, pid);
- erts_lcnt_init_lock_x(&(p->lock.lcnt_trace), "proc_trace", ERTS_LCNT_LT_PROCLOCK, pid);
- } /* the lock names should really be aligned to four characters */
+ erts_lcnt_init_ref(&p->lock.lcnt_carrier);
+
+ if(erts_lcnt_check_enabled(ERTS_LOCK_FLAGS_CATEGORY_PROCESS)) {
+ erts_lcnt_enable_proc_lock_count(p, 1);
+ }
} /* logic reversed */
void erts_lcnt_proc_lock_destroy(Process *p) {
- erts_lcnt_destroy_lock(&(p->lock.lcnt_main));
- erts_lcnt_destroy_lock(&(p->lock.lcnt_link));
- erts_lcnt_destroy_lock(&(p->lock.lcnt_msgq));
- erts_lcnt_destroy_lock(&(p->lock.lcnt_btm));
- erts_lcnt_destroy_lock(&(p->lock.lcnt_status));
- erts_lcnt_destroy_lock(&(p->lock.lcnt_trace));
+ erts_lcnt_uninstall(&p->lock.lcnt_carrier);
}
-static void lcnt_enable_proc_lock_count(Process *proc, int enable) {
- if (enable) {
- if (!ERTS_LCNT_LOCK_TYPE(&(proc->lock.lcnt_main))) {
- erts_lcnt_proc_lock_init(proc);
- }
- }
- else {
- if (ERTS_LCNT_LOCK_TYPE(&(proc->lock.lcnt_main))) {
- erts_lcnt_proc_lock_destroy(proc);
- }
+void erts_lcnt_enable_proc_lock_count(Process *proc, int enable) {
+ if(proc->common.id == ERTS_INVALID_PID) {
+ /* Locks without an id are more trouble than they're worth; there's no
+ * way to look them up and we can't track them with _STATIC since it's
+ * too early to tell whether we're a system process (proc->static_flags
+ * hasn't been not set yet). */
+ } else if(!enable) {
+ erts_lcnt_proc_lock_destroy(proc);
+ } else if(!erts_lcnt_check_ref_installed(&proc->lock.lcnt_carrier)) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+
+ carrier = erts_lcnt_create_lock_info_carrier(ERTS_LCNT_PROCLOCK_COUNT);
+
+ erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN,
+ "proc_main", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
+ erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK,
+ "proc_link", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
+ erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ,
+ "proc_msgq", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
+ erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_BTM,
+ "proc_btm", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
+ erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_STATUS,
+ "proc_status",proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
+ erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_TRACE,
+ "proc_trace", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK);
+
+ erts_lcnt_install(&proc->lock.lcnt_carrier, carrier);
}
}
-void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks) {
- if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return;
- if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock(&(lock->lcnt_main)); }
- if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_lock(&(lock->lcnt_link)); }
- if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_lock(&(lock->lcnt_msgq)); }
- if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_lock(&(lock->lcnt_btm)); }
- if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_lock(&(lock->lcnt_status)); }
- if (locks & ERTS_PROC_LOCK_TRACE) { erts_lcnt_lock(&(lock->lcnt_trace)); }
-}
+void erts_lcnt_update_process_locks(int enable) {
+ int i, max;
-void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks,
- char *file, unsigned int line) {
- if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return;
- if (locks & ERTS_PROC_LOCK_MAIN) {
- erts_lcnt_lock_post_x(&(lock->lcnt_main), file, line);
- }
- if (locks & ERTS_PROC_LOCK_LINK) {
- erts_lcnt_lock_post_x(&(lock->lcnt_link), file, line);
- }
- if (locks & ERTS_PROC_LOCK_MSGQ) {
- erts_lcnt_lock_post_x(&(lock->lcnt_msgq), file, line);
- }
- if (locks & ERTS_PROC_LOCK_BTM) {
- erts_lcnt_lock_post_x(&(lock->lcnt_btm), file, line);
- }
- if (locks & ERTS_PROC_LOCK_STATUS) {
- erts_lcnt_lock_post_x(&(lock->lcnt_status), file, line);
- }
- if (locks & ERTS_PROC_LOCK_TRACE) {
- erts_lcnt_lock_post_x(&(lock->lcnt_trace), file, line);
- }
-}
+ max = erts_ptab_max(&erts_proc);
-void erts_lcnt_proc_lock_unaquire(erts_proc_lock_t *lock, ErtsProcLocks locks) {
- if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return;
- if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock_unaquire(&(lock->lcnt_main)); }
- if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_lock_unaquire(&(lock->lcnt_link)); }
- if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_lock_unaquire(&(lock->lcnt_msgq)); }
- if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_lock_unaquire(&(lock->lcnt_btm)); }
- if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_lock_unaquire(&(lock->lcnt_status)); }
- if (locks & ERTS_PROC_LOCK_TRACE) { erts_lcnt_lock_unaquire(&(lock->lcnt_trace)); }
-}
+ for(i = 0; i < max; i++) {
+ int delay_handle;
+ Process *proc;
+
+ delay_handle = erts_thr_progress_unmanaged_delay();
+ proc = erts_pix2proc(i);
+
+ if(proc != NULL) {
+ erts_lcnt_enable_proc_lock_count(proc, enable);
+ }
-void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks) {
- if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return;
- if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_unlock(&(lock->lcnt_main)); }
- if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_unlock(&(lock->lcnt_link)); }
- if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_unlock(&(lock->lcnt_msgq)); }
- if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_unlock(&(lock->lcnt_btm)); }
- if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_unlock(&(lock->lcnt_status)); }
- if (locks & ERTS_PROC_LOCK_TRACE) { erts_lcnt_unlock(&(lock->lcnt_trace)); }
+ if(delay_handle != ERTS_THR_PRGR_DHANDLE_MANAGED) {
+ erts_thr_progress_unmanaged_continue(delay_handle);
+ }
+ }
}
-void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res) {
- if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK)) return;
- if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_trylock(&(lock->lcnt_main), res); }
- if (locks & ERTS_PROC_LOCK_LINK) { erts_lcnt_trylock(&(lock->lcnt_link), res); }
- if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_trylock(&(lock->lcnt_msgq), res); }
- if (locks & ERTS_PROC_LOCK_BTM) { erts_lcnt_trylock(&(lock->lcnt_btm), res); }
- if (locks & ERTS_PROC_LOCK_STATUS) { erts_lcnt_trylock(&(lock->lcnt_status), res); }
- if (locks & ERTS_PROC_LOCK_TRACE) { erts_lcnt_trylock(&(lock->lcnt_trace), res); }
-} /* reversed logic */
+
#endif /* ERTS_ENABLE_LOCK_COUNT */
diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h
index 6e704b185d..023ba4d4ae 100644
--- a/erts/emulator/beam/erl_process_lock.h
+++ b/erts/emulator/beam/erl_process_lock.h
@@ -78,13 +78,19 @@ typedef struct erts_proc_lock_t_ {
ErtsProcLocks flags;
#endif
erts_tse_t *queue[ERTS_PROC_LOCK_MAX_BIT+1];
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_t lcnt_main;
- erts_lcnt_lock_t lcnt_link;
- erts_lcnt_lock_t lcnt_msgq;
- erts_lcnt_lock_t lcnt_btm;
- erts_lcnt_lock_t lcnt_status;
- erts_lcnt_lock_t lcnt_trace;
+#if defined(ERTS_ENABLE_LOCK_COUNT) && !ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ /* Each erts_mtx_t has its own lock counter ^ */
+
+ #define ERTS_LCNT_PROCLOCK_IDX_MAIN 0
+ #define ERTS_LCNT_PROCLOCK_IDX_LINK 1
+ #define ERTS_LCNT_PROCLOCK_IDX_MSGQ 2
+ #define ERTS_LCNT_PROCLOCK_IDX_BTM 3
+ #define ERTS_LCNT_PROCLOCK_IDX_STATUS 4
+ #define ERTS_LCNT_PROCLOCK_IDX_TRACE 5
+
+ #define ERTS_LCNT_PROCLOCK_COUNT 6
+
+ erts_lcnt_ref_t lcnt_carrier;
#endif
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
erts_mtx_t main;
@@ -245,14 +251,170 @@ typedef struct erts_proc_lock_t_ {
void erts_lcnt_proc_lock_init(Process *p);
void erts_lcnt_proc_lock_destroy(Process *p);
+
+ERTS_GLB_INLINE
void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks);
+ERTS_GLB_INLINE
void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks, char *file, unsigned int line);
-void erts_lcnt_proc_lock_unaquire(erts_proc_lock_t *lock, ErtsProcLocks locks);
+ERTS_GLB_INLINE
+void erts_lcnt_proc_lock_unacquire(erts_proc_lock_t *lock, ErtsProcLocks locks);
+ERTS_GLB_INLINE
void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks);
+ERTS_GLB_INLINE
void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res);
-void erts_lcnt_enable_proc_lock_count(int enable);
+void erts_lcnt_enable_proc_lock_count(Process *proc, int enable);
+void erts_lcnt_update_process_locks(int enable);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE
+void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(&lock->lcnt_carrier, &handle, &carrier)) {
+ if (locks & ERTS_PROC_LOCK_MAIN) {
+ erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN);
+ }
+ if (locks & ERTS_PROC_LOCK_LINK) {
+ erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK);
+ }
+ if (locks & ERTS_PROC_LOCK_MSGQ) {
+ erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ);
+ }
+ if (locks & ERTS_PROC_LOCK_BTM) {
+ erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_BTM);
+ }
+ if (locks & ERTS_PROC_LOCK_STATUS) {
+ erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_STATUS);
+ }
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_TRACE);
+ }
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks,
+ char *file, unsigned int line) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(&lock->lcnt_carrier, &handle, &carrier)) {
+ if (locks & ERTS_PROC_LOCK_MAIN) {
+ erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN, file, line);
+ }
+ if (locks & ERTS_PROC_LOCK_LINK) {
+ erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK, file, line);
+ }
+ if (locks & ERTS_PROC_LOCK_MSGQ) {
+ erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ, file, line);
+ }
+ if (locks & ERTS_PROC_LOCK_BTM) {
+ erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_BTM, file, line);
+ }
+ if (locks & ERTS_PROC_LOCK_STATUS) {
+ erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_STATUS, file, line);
+ }
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_TRACE, file, line);
+ }
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_proc_lock_unacquire(erts_proc_lock_t *lock, ErtsProcLocks locks) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(&lock->lcnt_carrier, &handle, &carrier)) {
+ if (locks & ERTS_PROC_LOCK_MAIN) {
+ erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN);
+ }
+ if (locks & ERTS_PROC_LOCK_LINK) {
+ erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK);
+ }
+ if (locks & ERTS_PROC_LOCK_MSGQ) {
+ erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ);
+ }
+ if (locks & ERTS_PROC_LOCK_BTM) {
+ erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_BTM);
+ }
+ if (locks & ERTS_PROC_LOCK_STATUS) {
+ erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_STATUS);
+ }
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_TRACE);
+ }
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(&lock->lcnt_carrier, &handle, &carrier)) {
+ if (locks & ERTS_PROC_LOCK_MAIN) {
+ erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN);
+ }
+ if (locks & ERTS_PROC_LOCK_LINK) {
+ erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK);
+ }
+ if (locks & ERTS_PROC_LOCK_MSGQ) {
+ erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ);
+ }
+ if (locks & ERTS_PROC_LOCK_BTM) {
+ erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_BTM);
+ }
+ if (locks & ERTS_PROC_LOCK_STATUS) {
+ erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_STATUS);
+ }
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_TRACE);
+ }
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res) {
+ erts_lcnt_lock_info_carrier_t *carrier;
+ int handle;
+
+ if(erts_lcnt_open_ref(&lock->lcnt_carrier, &handle, &carrier)) {
+ if (locks & ERTS_PROC_LOCK_MAIN) {
+ erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN, res);
+ }
+ if (locks & ERTS_PROC_LOCK_LINK) {
+ erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK, res);
+ }
+ if (locks & ERTS_PROC_LOCK_MSGQ) {
+ erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ, res);
+ }
+ if (locks & ERTS_PROC_LOCK_BTM) {
+ erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_BTM, res);
+ }
+ if (locks & ERTS_PROC_LOCK_STATUS) {
+ erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_STATUS, res);
+ }
+ if (locks & ERTS_PROC_LOCK_TRACE) {
+ erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_TRACE, res);
+ }
+
+ erts_lcnt_close_ref(handle, carrier);
+ }
+} /* reversed logic */
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
#endif /* ERTS_ENABLE_LOCK_COUNT*/
diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c
index c3d59cb3a8..b3bcb3af3f 100644
--- a/erts/emulator/beam/erl_ptab.c
+++ b/erts/emulator/beam/erl_ptab.c
@@ -372,7 +372,8 @@ erts_ptab_init_table(ErtsPTab *ptab,
rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED;
- erts_smp_rwmtx_init_opt(&ptab->list.data.rwmtx, &rwmtx_opts, name);
+ erts_smp_rwmtx_init_opt(&ptab->list.data.rwmtx, &rwmtx_opts, name, NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
erts_smp_atomic32_init_nob(&ptab->vola.tile.count, 0);
last_data_init_nob(ptab, ~((Uint64) 0));
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
index cab4bd73db..96238318c9 100644
--- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c
+++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
@@ -161,7 +161,7 @@ enqueue_remote_managed_thread(erts_sspa_chunk_header_t *chdr,
if ((i & 1) == 0)
itmp = itmp2;
else {
- enq = (erts_sspa_blk_t *) itmp;
+ enq = (erts_sspa_blk_t *) itmp2;
itmp = erts_atomic_read_acqb(&enq->next_atmc);
ASSERT(itmp != ERTS_AINT_NULL);
}
diff --git a/erts/emulator/beam/erl_smp.h b/erts/emulator/beam/erl_smp.h
index 55ba943bdd..696bdbdaf1 100644
--- a/erts/emulator/beam/erl_smp.h
+++ b/erts/emulator/beam/erl_smp.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2005-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.
@@ -128,14 +128,14 @@ ERTS_GLB_INLINE int erts_smp_equal_tids(erts_smp_tid_t x, erts_smp_tid_t y);
#define ERTS_SMP_HAVE_REC_MTX_INIT 1
ERTS_GLB_INLINE void erts_smp_rec_mtx_init(erts_smp_mtx_t *mtx);
#endif
-ERTS_GLB_INLINE void erts_smp_mtx_init_x(erts_smp_mtx_t *mtx,
- char *name,
- Eterm extra);
-ERTS_GLB_INLINE void erts_smp_mtx_init_locked_x(erts_smp_mtx_t *mtx,
- char *name,
- Eterm extra);
-ERTS_GLB_INLINE void erts_smp_mtx_init(erts_smp_mtx_t *mtx, char *name);
-ERTS_GLB_INLINE void erts_smp_mtx_init_locked(erts_smp_mtx_t *mtx, char *name);
+ERTS_GLB_INLINE void erts_smp_mtx_init(erts_smp_mtx_t *mtx,
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
+ERTS_GLB_INLINE void erts_smp_mtx_init_locked(erts_smp_mtx_t *mtx,
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
ERTS_GLB_INLINE void erts_smp_mtx_destroy(erts_smp_mtx_t *mtx);
#ifdef ERTS_ENABLE_LOCK_POSITION
ERTS_GLB_INLINE int erts_smp_mtx_trylock_x(erts_smp_mtx_t *mtx, char *file, unsigned int line);
@@ -153,18 +153,15 @@ ERTS_GLB_INLINE void erts_smp_cnd_wait(erts_smp_cnd_t *cnd,
ERTS_GLB_INLINE void erts_smp_cnd_signal(erts_smp_cnd_t *cnd);
ERTS_GLB_INLINE void erts_smp_cnd_broadcast(erts_smp_cnd_t *cnd);
ERTS_GLB_INLINE void erts_smp_rwmtx_set_reader_group(int no);
-ERTS_GLB_INLINE void erts_smp_rwmtx_init_opt_x(erts_smp_rwmtx_t *rwmtx,
- erts_smp_rwmtx_opt_t *opt,
- char *name,
- Eterm extra);
-ERTS_GLB_INLINE void erts_smp_rwmtx_init_x(erts_smp_rwmtx_t *rwmtx,
- char *name,
- Eterm extra);
ERTS_GLB_INLINE void erts_smp_rwmtx_init_opt(erts_smp_rwmtx_t *rwmtx,
- erts_smp_rwmtx_opt_t *opt,
- char *name);
+ erts_smp_rwmtx_opt_t *opt,
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
ERTS_GLB_INLINE void erts_smp_rwmtx_init(erts_smp_rwmtx_t *rwmtx,
- char *name);
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
ERTS_GLB_INLINE void erts_smp_rwmtx_destroy(erts_smp_rwmtx_t *rwmtx);
#ifdef ERTS_ENABLE_LOCK_POSITION
ERTS_GLB_INLINE int erts_smp_rwmtx_tryrlock_x(erts_smp_rwmtx_t *rwmtx, char *file, unsigned int line);
@@ -181,11 +178,10 @@ ERTS_GLB_INLINE void erts_smp_rwmtx_runlock(erts_smp_rwmtx_t *rwmtx);
ERTS_GLB_INLINE void erts_smp_rwmtx_rwunlock(erts_smp_rwmtx_t *rwmtx);
ERTS_GLB_INLINE int erts_smp_lc_rwmtx_is_rlocked(erts_smp_rwmtx_t *mtx);
ERTS_GLB_INLINE int erts_smp_lc_rwmtx_is_rwlocked(erts_smp_rwmtx_t *mtx);
-ERTS_GLB_INLINE void erts_smp_spinlock_init_x(erts_smp_spinlock_t *lock,
- char *name,
- Eterm extra);
ERTS_GLB_INLINE void erts_smp_spinlock_init(erts_smp_spinlock_t *lock,
- char *name);
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
ERTS_GLB_INLINE void erts_smp_spinlock_destroy(erts_smp_spinlock_t *lock);
ERTS_GLB_INLINE void erts_smp_spin_unlock(erts_smp_spinlock_t *lock);
#ifdef ERTS_ENABLE_LOCK_POSITION
@@ -194,11 +190,10 @@ ERTS_GLB_INLINE void erts_smp_spin_lock_x(erts_smp_spinlock_t *lock, char *file,
ERTS_GLB_INLINE void erts_smp_spin_lock(erts_smp_spinlock_t *lock);
#endif
ERTS_GLB_INLINE int erts_smp_lc_spinlock_is_locked(erts_smp_spinlock_t *lock);
-ERTS_GLB_INLINE void erts_smp_rwlock_init_x(erts_smp_rwlock_t *lock,
- char *name,
- Eterm extra);
ERTS_GLB_INLINE void erts_smp_rwlock_init(erts_smp_rwlock_t *lock,
- char *name);
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
ERTS_GLB_INLINE void erts_smp_rwlock_destroy(erts_smp_rwlock_t *lock);
ERTS_GLB_INLINE void erts_smp_read_unlock(erts_smp_rwlock_t *lock);
#ifdef ERTS_ENABLE_LOCK_POSITION
@@ -1062,34 +1057,18 @@ erts_smp_rec_mtx_init(erts_smp_mtx_t *mtx)
#endif
ERTS_GLB_INLINE void
-erts_smp_mtx_init_x(erts_smp_mtx_t *mtx, char *name, Eterm extra)
+erts_smp_mtx_init(erts_smp_mtx_t *mtx, char *name, Eterm extra, erts_lock_flags_t flags)
{
#ifdef ERTS_SMP
- erts_mtx_init_x(mtx, name, extra);
+ erts_mtx_init(mtx, name, extra, flags);
#endif
}
ERTS_GLB_INLINE void
-erts_smp_mtx_init_locked_x(erts_smp_mtx_t *mtx, char *name, Eterm extra)
+erts_smp_mtx_init_locked(erts_smp_mtx_t *mtx, char *name, Eterm extra, erts_lock_flags_t flags)
{
#ifdef ERTS_SMP
- erts_mtx_init_locked_x_opt(mtx, name, extra, 0);
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_smp_mtx_init(erts_smp_mtx_t *mtx, char *name)
-{
-#ifdef ERTS_SMP
- erts_mtx_init(mtx, name);
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_smp_mtx_init_locked(erts_smp_mtx_t *mtx, char *name)
-{
-#ifdef ERTS_SMP
- erts_mtx_init_locked(mtx, name);
+ erts_mtx_init_locked(mtx, name, extra, flags);
#endif
}
@@ -1211,39 +1190,25 @@ erts_smp_rwmtx_set_reader_group(int no)
}
ERTS_GLB_INLINE void
-erts_smp_rwmtx_init_opt_x(erts_smp_rwmtx_t *rwmtx,
- erts_smp_rwmtx_opt_t *opt,
- char *name,
- Eterm extra)
-{
-#ifdef ERTS_SMP
- erts_rwmtx_init_opt_x(rwmtx, opt, name, extra);
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_smp_rwmtx_init_x(erts_smp_rwmtx_t *rwmtx, char *name, Eterm extra)
+erts_smp_rwmtx_init(erts_smp_rwmtx_t *rwmtx,
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags)
{
#ifdef ERTS_SMP
- erts_rwmtx_init_x(rwmtx, name, extra);
+ erts_smp_rwmtx_init_opt(rwmtx, NULL, name, extra, flags);
#endif
}
ERTS_GLB_INLINE void
erts_smp_rwmtx_init_opt(erts_smp_rwmtx_t *rwmtx,
- erts_smp_rwmtx_opt_t *opt,
- char *name)
-{
-#ifdef ERTS_SMP
- erts_rwmtx_init_opt(rwmtx, opt, name);
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_smp_rwmtx_init(erts_smp_rwmtx_t *rwmtx, char *name)
+ erts_smp_rwmtx_opt_t *opt,
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags)
{
#ifdef ERTS_SMP
- erts_rwmtx_init(rwmtx, name);
+ erts_rwmtx_init_opt(rwmtx, opt, name, extra, flags);
#endif
}
@@ -1379,20 +1344,10 @@ erts_smp_lc_rwmtx_is_rwlocked(erts_smp_rwmtx_t *mtx)
}
ERTS_GLB_INLINE void
-erts_smp_spinlock_init_x(erts_smp_spinlock_t *lock, char *name, Eterm extra)
-{
-#ifdef ERTS_SMP
- erts_spinlock_init_x(lock, name, extra);
-#else
- (void)lock;
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_smp_spinlock_init(erts_smp_spinlock_t *lock, char *name)
+erts_smp_spinlock_init(erts_smp_spinlock_t *lock, char *name, Eterm extra, erts_lock_flags_t flags)
{
#ifdef ERTS_SMP
- erts_spinlock_init(lock, name);
+ erts_spinlock_init(lock, name, extra, flags);
#else
(void)lock;
#endif
@@ -1445,20 +1400,10 @@ erts_smp_lc_spinlock_is_locked(erts_smp_spinlock_t *lock)
}
ERTS_GLB_INLINE void
-erts_smp_rwlock_init_x(erts_smp_rwlock_t *lock, char *name, Eterm extra)
-{
-#ifdef ERTS_SMP
- erts_rwlock_init_x(lock, name, extra);
-#else
- (void)lock;
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_smp_rwlock_init(erts_smp_rwlock_t *lock, char *name)
+erts_smp_rwlock_init(erts_smp_rwlock_t *lock, char *name, Eterm extra, erts_lock_flags_t flags)
{
#ifdef ERTS_SMP
- erts_rwlock_init(lock, name);
+ erts_rwlock_init(lock, name, extra, flags);
#else
(void)lock;
#endif
diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c
index 700ed90def..2a9f276e02 100644
--- a/erts/emulator/beam/erl_thr_progress.c
+++ b/erts/emulator/beam/erl_thr_progress.c
@@ -321,13 +321,23 @@ tmp_thr_prgr_data(ErtsSchedulerData *esdp)
ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(esdp);
if (!tpd) {
- /*
- * We only allocate the part up to the wakeup_request field
- * which is the first field only used by registered threads
- */
- tpd = erts_alloc(ERTS_ALC_T_T_THR_PRGR_DATA,
- offsetof(ErtsThrPrgrData, wakeup_request));
- init_tmp_thr_prgr_data(tpd);
+ /*
+ * We only allocate the part up to the wakeup_request field which is
+ * the first field only used by registered threads
+ */
+ size_t alloc_size = offsetof(ErtsThrPrgrData, wakeup_request);
+
+ /* We may land here as a result of unmanaged_delay being called from
+ * the lock counting module, which in turn might be called from within
+ * the allocator, so we use plain malloc to avoid deadlocks. */
+ tpd =
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ malloc(alloc_size);
+#else
+ erts_alloc(ERTS_ALC_T_T_THR_PRGR_DATA, alloc_size);
+#endif
+
+ init_tmp_thr_prgr_data(tpd);
}
return tpd;
@@ -337,8 +347,13 @@ static ERTS_INLINE void
return_tmp_thr_prgr_data(ErtsThrPrgrData *tpd)
{
if (tpd->is_temporary) {
- erts_tsd_set(erts_thr_prgr_data_key__, NULL);
- erts_free(ERTS_ALC_T_T_THR_PRGR_DATA, tpd);
+ erts_tsd_set(erts_thr_prgr_data_key__, NULL);
+
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ free(tpd);
+#else
+ erts_free(ERTS_ALC_T_T_THR_PRGR_DATA, tpd);
+#endif
}
}
diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h
index 28ff5d3a42..3fdf29d678 100644
--- a/erts/emulator/beam/erl_threads.h
+++ b/erts/emulator/beam/erl_threads.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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.
@@ -259,13 +259,16 @@
#include "sys.h"
+#include "erl_lock_flags.h"
+#include "erl_term.h"
+
#ifdef USE_THREADS
#define ETHR_TRY_INLINE_FUNCS
#include "ethread.h"
+
#include "erl_lock_check.h"
#include "erl_lock_count.h"
-#include "erl_term.h"
#if defined(__GLIBC__) && (__GLIBC__ << 16) + __GLIBC_MINOR__ < (2 << 16) + 4
/*
@@ -307,9 +310,11 @@ typedef struct {
erts_lc_lock_t lc;
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_t lcnt;
+ erts_lcnt_ref_t lcnt;
+#endif
+#ifdef DEBUG
+ erts_lock_flags_t flags;
#endif
-
} erts_mtx_t;
typedef ethr_cond erts_cnd_t;
@@ -320,7 +325,10 @@ typedef struct {
erts_lc_lock_t lc;
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_t lcnt;
+ erts_lcnt_ref_t lcnt;
+#endif
+#ifdef DEBUG
+ erts_lock_flags_t flags;
#endif
} erts_rwmtx_t;
@@ -365,7 +373,10 @@ typedef struct {
erts_lc_lock_t lc;
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_t lcnt;
+ erts_lcnt_ref_t lcnt;
+#endif
+#ifdef DEBUG
+ erts_lock_flags_t flags;
#endif
} erts_spinlock_t;
@@ -376,7 +387,10 @@ typedef struct {
erts_lc_lock_t lc;
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_t lcnt;
+ erts_lcnt_ref_t lcnt;
+#endif
+#ifdef DEBUG
+ erts_lock_flags_t flags;
#endif
} erts_rwlock_t;
@@ -479,11 +493,14 @@ ERTS_GLB_INLINE void erts_thr_install_exit_handler(void (*exit_handler)(void));
ERTS_GLB_INLINE erts_tid_t erts_thr_self(void);
ERTS_GLB_INLINE int erts_thr_getname(erts_tid_t tid, char *buf, size_t len);
ERTS_GLB_INLINE int erts_equal_tids(erts_tid_t x, erts_tid_t y);
-ERTS_GLB_INLINE void erts_mtx_init_x(erts_mtx_t *mtx, char *name, Eterm extra);
-ERTS_GLB_INLINE void erts_mtx_init_x_opt(erts_mtx_t *mtx, char *name, Eterm extra, Uint16 opt);
-ERTS_GLB_INLINE void erts_mtx_init_locked_x_opt(erts_mtx_t *mtx, char *name, Eterm extra, Uint16 opt);
-ERTS_GLB_INLINE void erts_mtx_init(erts_mtx_t *mtx, char *name);
-ERTS_GLB_INLINE void erts_mtx_init_locked(erts_mtx_t *mtx, char *name);
+ERTS_GLB_INLINE void erts_mtx_init(erts_mtx_t *mtx,
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
+ERTS_GLB_INLINE void erts_mtx_init_locked(erts_mtx_t *mtx,
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
ERTS_GLB_INLINE void erts_mtx_destroy(erts_mtx_t *mtx);
#ifdef ERTS_ENABLE_LOCK_POSITION
ERTS_GLB_INLINE int erts_mtx_trylock_x(erts_mtx_t *mtx, char *file,
@@ -502,18 +519,15 @@ ERTS_GLB_INLINE void erts_cnd_wait(erts_cnd_t *cnd, erts_mtx_t *mtx);
ERTS_GLB_INLINE void erts_cnd_signal(erts_cnd_t *cnd);
ERTS_GLB_INLINE void erts_cnd_broadcast(erts_cnd_t *cnd);
ERTS_GLB_INLINE void erts_rwmtx_set_reader_group(int no);
-ERTS_GLB_INLINE void erts_rwmtx_init_opt_x(erts_rwmtx_t *rwmtx,
- erts_rwmtx_opt_t *opt,
- char *name,
- Eterm extra);
-ERTS_GLB_INLINE void erts_rwmtx_init_x(erts_rwmtx_t *rwmtx,
- char *name,
- Eterm extra);
ERTS_GLB_INLINE void erts_rwmtx_init_opt(erts_rwmtx_t *rwmtx,
- erts_rwmtx_opt_t *opt,
- char *name);
+ erts_rwmtx_opt_t *opt,
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
ERTS_GLB_INLINE void erts_rwmtx_init(erts_rwmtx_t *rwmtx,
- char *name);
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
ERTS_GLB_INLINE void erts_rwmtx_destroy(erts_rwmtx_t *rwmtx);
#ifdef ERTS_ENABLE_LOCK_POSITION
ERTS_GLB_INLINE int erts_rwmtx_tryrlock_x(erts_rwmtx_t *rwmtx, char *file, unsigned int line);
@@ -603,16 +617,10 @@ ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_cmpxchg(erts_no_atomic64_t *xchgp
ERTS_GLB_INLINE erts_aint64_t erts_no_atomic64_read_bset(erts_no_atomic64_t *var,
erts_aint64_t mask,
erts_aint64_t set);
-
-ERTS_GLB_INLINE void erts_spinlock_init_x_opt(erts_spinlock_t *lock,
- char *name,
- Eterm extra,
- Uint16 opt);
-ERTS_GLB_INLINE void erts_spinlock_init_x(erts_spinlock_t *lock,
- char *name,
- Eterm extra);
ERTS_GLB_INLINE void erts_spinlock_init(erts_spinlock_t *lock,
- char *name);
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
ERTS_GLB_INLINE void erts_spinlock_destroy(erts_spinlock_t *lock);
ERTS_GLB_INLINE void erts_spin_unlock(erts_spinlock_t *lock);
#ifdef ERTS_ENABLE_LOCK_POSITION
@@ -621,11 +629,10 @@ ERTS_GLB_INLINE void erts_spin_lock_x(erts_spinlock_t *lock, char *file, unsigne
ERTS_GLB_INLINE void erts_spin_lock(erts_spinlock_t *lock);
#endif
ERTS_GLB_INLINE int erts_lc_spinlock_is_locked(erts_spinlock_t *lock);
-ERTS_GLB_INLINE void erts_rwlock_init_x(erts_rwlock_t *lock,
- char *name,
- Eterm extra);
ERTS_GLB_INLINE void erts_rwlock_init(erts_rwlock_t *lock,
- char *name);
+ char *name,
+ Eterm extra,
+ erts_lock_flags_t flags);
ERTS_GLB_INLINE void erts_rwlock_destroy(erts_rwlock_t *lock);
ERTS_GLB_INLINE void erts_read_unlock(erts_rwlock_t *lock);
#ifdef ERTS_ENABLE_LOCK_POSITION
@@ -2159,97 +2166,41 @@ erts_equal_tids(erts_tid_t x, erts_tid_t y)
}
ERTS_GLB_INLINE void
-erts_mtx_init_x(erts_mtx_t *mtx, char *name, Eterm extra)
+erts_mtx_init(erts_mtx_t *mtx, char *name, Eterm extra, erts_lock_flags_t flags)
{
#ifdef USE_THREADS
int res = ethr_mutex_init(&mtx->mtx);
- if (res)
- erts_thr_fatal_error(res, "initialize mutex");
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock_x(&mtx->lc, name, ERTS_LC_FLG_LT_MUTEX, extra);
-#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock_x(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX, extra);
-#endif
-#endif
-}
+ if (res) {
+ erts_thr_fatal_error(res, "initialize mutex");
+ }
-ERTS_GLB_INLINE void
-erts_mtx_init_x_opt(erts_mtx_t *mtx, char *name, Eterm extra, Uint16 opt)
-{
-#ifdef USE_THREADS
- int res = ethr_mutex_init(&mtx->mtx);
- if (res)
- erts_thr_fatal_error(res, "initialize mutex");
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock_x(&mtx->lc, name, ERTS_LC_FLG_LT_MUTEX, extra);
-#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock_x(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX | opt, extra);
+ flags |= ERTS_LOCK_TYPE_MUTEX;
+#ifdef DEBUG
+ mtx->flags = flags;
#endif
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_mtx_init_locked_x_opt(erts_mtx_t *mtx, char *name, Eterm extra, Uint16 opt)
-{
-#ifdef USE_THREADS
- int res = ethr_mutex_init(&mtx->mtx);
- if (res)
- erts_thr_fatal_error(res, "initialize mutex");
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_init_lock_x(&mtx->lc, name, ERTS_LC_FLG_LT_MUTEX, extra);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock_x(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX | opt, extra);
-#endif
- ethr_mutex_lock(&mtx->mtx);
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_trylock(1, &mtx->lc);
-#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_trylock(&mtx->lcnt, 1);
-#endif
+ erts_lcnt_init_ref_x(&mtx->lcnt, name, extra, flags);
#endif
+#endif /* USE_THREADS */
}
ERTS_GLB_INLINE void
-erts_mtx_init(erts_mtx_t *mtx, char *name)
+erts_mtx_init_locked(erts_mtx_t *mtx, char *name, Eterm extra, erts_lock_flags_t flags)
{
#ifdef USE_THREADS
- int res = ethr_mutex_init(&mtx->mtx);
- if (res)
- erts_thr_fatal_error(res, "initialize mutex");
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock(&mtx->lc, name, ERTS_LC_FLG_LT_MUTEX);
-#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX);
-#endif
-#endif
-}
+ erts_mtx_init(mtx, name, extra, flags);
-ERTS_GLB_INLINE void
-erts_mtx_init_locked(erts_mtx_t *mtx, char *name)
-{
-#ifdef USE_THREADS
- int res = ethr_mutex_init(&mtx->mtx);
- if (res)
- erts_thr_fatal_error(res, "initialize mutex");
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock(&mtx->lc, name, ERTS_LC_FLG_LT_MUTEX);
-#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock(&mtx->lcnt, name, ERTS_LCNT_LT_MUTEX);
-#endif
ethr_mutex_lock(&mtx->mtx);
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_trylock(1, &mtx->lc);
-#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_trylock(&mtx->lcnt, 1);
-#endif
+ #ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_trylock(1, &mtx->lc);
+ #endif
+ #ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_trylock(&mtx->lcnt, 1);
+ #endif
#endif
}
@@ -2258,11 +2209,14 @@ erts_mtx_destroy(erts_mtx_t *mtx)
{
#ifdef USE_THREADS
int res;
+
+ ASSERT(!(mtx->flags & ERTS_LOCK_FLAGS_PROPERTY_STATIC));
+
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_destroy_lock(&mtx->lc);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_destroy_lock(&mtx->lcnt);
+ erts_lcnt_uninstall(&mtx->lcnt);
#endif
res = ethr_mutex_destroy(&mtx->mtx);
if (res != 0) {
@@ -2468,57 +2422,32 @@ erts_rwmtx_set_reader_group(int no)
}
ERTS_GLB_INLINE void
-erts_rwmtx_init_opt_x(erts_rwmtx_t *rwmtx,
- erts_rwmtx_opt_t *opt,
- char *name,
- Eterm extra)
-{
+erts_rwmtx_init_opt(erts_rwmtx_t *rwmtx, erts_rwmtx_opt_t *opt,
+ char *name, Eterm extra, erts_lock_flags_t flags) {
#ifdef USE_THREADS
int res = ethr_rwmutex_init_opt(&rwmtx->rwmtx, opt);
- if (res != 0)
- erts_thr_fatal_error(res, "initialize rwmutex");
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock_x(&rwmtx->lc, name, ERTS_LC_FLG_LT_RWMUTEX, extra);
-#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- if (name && name[0] == '\0')
- erts_lcnt_init_lock_x(&rwmtx->lcnt, NULL, ERTS_LCNT_LT_RWMUTEX, extra);
- else
- erts_lcnt_init_lock_x(&rwmtx->lcnt, name, ERTS_LCNT_LT_RWMUTEX, extra);
-#endif
-#endif
-}
+ if (res != 0) {
+ erts_thr_fatal_error(res, "initialize rwmutex");
+ }
-ERTS_GLB_INLINE void
-erts_rwmtx_init_x(erts_rwmtx_t *rwmtx,
- char *name,
- Eterm extra)
-{
- erts_rwmtx_init_opt_x(rwmtx, NULL, name, extra);
-}
+ flags |= ERTS_LOCK_TYPE_RWMUTEX;
+#ifdef DEBUG
+ rwmtx->flags = flags;
+#endif
-ERTS_GLB_INLINE void
-erts_rwmtx_init_opt(erts_rwmtx_t *rwmtx,
- erts_rwmtx_opt_t *opt,
- char *name)
-{
-#ifdef USE_THREADS
- int res = ethr_rwmutex_init_opt(&rwmtx->rwmtx, opt);
- if (res != 0)
- erts_thr_fatal_error(res, "initialize rwmutex");
#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock(&rwmtx->lc, name, ERTS_LC_FLG_LT_RWMUTEX);
+ erts_lc_init_lock_x(&rwmtx->lc, name, ERTS_LC_FLG_LT_RWMUTEX, extra);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock(&rwmtx->lcnt, name, ERTS_LCNT_LT_RWMUTEX);
-#endif
+ erts_lcnt_init_ref_x(&rwmtx->lcnt, name, extra, flags);
#endif
+#endif /* USE_THREADS */
}
ERTS_GLB_INLINE void
-erts_rwmtx_init(erts_rwmtx_t *rwmtx, char *name)
-{
- erts_rwmtx_init_opt(rwmtx, NULL, name);
+erts_rwmtx_init(erts_rwmtx_t *rwmtx, char *name, Eterm extra,
+ erts_lock_flags_t flags) {
+ erts_rwmtx_init_opt(rwmtx, NULL, name, extra, flags);
}
ERTS_GLB_INLINE void
@@ -2526,11 +2455,14 @@ erts_rwmtx_destroy(erts_rwmtx_t *rwmtx)
{
#ifdef USE_THREADS
int res;
+
+ ASSERT(!(rwmtx->flags & ERTS_LOCK_FLAGS_PROPERTY_STATIC));
+
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_destroy_lock(&rwmtx->lc);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_destroy_lock(&rwmtx->lcnt);
+ erts_lcnt_uninstall(&rwmtx->lcnt);
#endif
res = ethr_rwmutex_destroy(&rwmtx->rwmtx);
if (res != 0) {
@@ -2573,7 +2505,7 @@ erts_rwmtx_tryrlock(erts_rwmtx_t *rwmtx)
#endif
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_trylock_opt(&rwmtx->lcnt, res, ERTS_LCNT_LO_READ);
+ erts_lcnt_trylock_opt(&rwmtx->lcnt, res, ERTS_LOCK_OPTION_READ);
#endif
return res;
@@ -2598,7 +2530,7 @@ erts_rwmtx_rlock(erts_rwmtx_t *rwmtx)
#endif
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_opt(&rwmtx->lcnt, ERTS_LCNT_LO_READ);
+ erts_lcnt_lock_opt(&rwmtx->lcnt, ERTS_LOCK_OPTION_READ);
#endif
ethr_rwmutex_rlock(&rwmtx->rwmtx);
#ifdef ERTS_ENABLE_LOCK_COUNT
@@ -2615,7 +2547,7 @@ erts_rwmtx_runlock(erts_rwmtx_t *rwmtx)
erts_lc_unlock_flg(&rwmtx->lc, ERTS_LC_FLG_LO_READ);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_unlock_opt(&rwmtx->lcnt, ERTS_LCNT_LO_READ);
+ erts_lcnt_unlock_opt(&rwmtx->lcnt, ERTS_LOCK_OPTION_READ);
#endif
ethr_rwmutex_runlock(&rwmtx->rwmtx);
#endif
@@ -2648,7 +2580,7 @@ erts_rwmtx_tryrwlock(erts_rwmtx_t *rwmtx)
#endif
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_trylock_opt(&rwmtx->lcnt, res, ERTS_LCNT_LO_READ_WRITE);
+ erts_lcnt_trylock_opt(&rwmtx->lcnt, res, ERTS_LOCK_OPTION_RDWR);
#endif
return res;
@@ -2673,7 +2605,7 @@ erts_rwmtx_rwlock(erts_rwmtx_t *rwmtx)
#endif
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_opt(&rwmtx->lcnt, ERTS_LCNT_LO_READ_WRITE);
+ erts_lcnt_lock_opt(&rwmtx->lcnt, ERTS_LOCK_OPTION_RDWR);
#endif
ethr_rwmutex_rwlock(&rwmtx->rwmtx);
#ifdef ERTS_ENABLE_LOCK_COUNT
@@ -2690,7 +2622,7 @@ erts_rwmtx_rwunlock(erts_rwmtx_t *rwmtx)
erts_lc_unlock_flg(&rwmtx->lc, ERTS_LC_FLG_LO_READ_WRITE);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_unlock_opt(&rwmtx->lcnt, ERTS_LCNT_LO_READ_WRITE);
+ erts_lcnt_unlock_opt(&rwmtx->lcnt, ERTS_LOCK_OPTION_RDWR);
#endif
ethr_rwmutex_rwunlock(&rwmtx->rwmtx);
#endif
@@ -3075,59 +3007,26 @@ erts_no_atomic64_read_bset(erts_no_atomic64_t *var,
/* spinlock */
ERTS_GLB_INLINE void
-erts_spinlock_init_x(erts_spinlock_t *lock, char *name, Eterm extra)
+erts_spinlock_init(erts_spinlock_t *lock, char *name, Eterm extra, erts_lock_flags_t flags)
{
#ifdef USE_THREADS
int res = ethr_spinlock_init(&lock->slck);
- if (res)
- erts_thr_fatal_error(res, "init spinlock");
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock_x(&lock->lc, name, ERTS_LC_FLG_LT_SPINLOCK, extra);
-#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock_x(&lock->lcnt, name, ERTS_LCNT_LT_SPINLOCK, extra);
-#endif
-#else
- (void)lock;
-#endif
-}
+ if (res) {
+ erts_thr_fatal_error(res, "init spinlock");
+ }
-ERTS_GLB_INLINE void
-erts_spinlock_init_x_opt(erts_spinlock_t *lock, char *name, Eterm extra,
- Uint16 opt)
-{
-#ifdef USE_THREADS
- int res = ethr_spinlock_init(&lock->slck);
- if (res)
- erts_thr_fatal_error(res, "init spinlock");
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock_x(&lock->lc, name, ERTS_LC_FLG_LT_SPINLOCK, extra);
-#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock_x(&lock->lcnt, name, ERTS_LCNT_LT_SPINLOCK|opt, extra);
-#endif
-#else
- (void)lock;
+ flags |= ERTS_LOCK_TYPE_SPINLOCK;
+#ifdef DEBUG
+ lock->flags = flags;
#endif
-}
-
-ERTS_GLB_INLINE void
-erts_spinlock_init(erts_spinlock_t *lock, char *name)
-{
-#ifdef USE_THREADS
- int res = ethr_spinlock_init(&lock->slck);
- if (res)
- erts_thr_fatal_error(res, "init spinlock");
#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock(&lock->lc, name, ERTS_LC_FLG_LT_SPINLOCK);
+ erts_lc_init_lock_x(&lock->lc, name, ERTS_LC_FLG_LT_SPINLOCK, extra);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock(&lock->lcnt, name, ERTS_LCNT_LT_SPINLOCK);
-#endif
-#else
- (void)lock;
+ erts_lcnt_init_ref_x(&lock->lcnt, name, extra, flags);
#endif
+#endif /* USE_THREADS */
}
ERTS_GLB_INLINE void
@@ -3135,11 +3034,14 @@ erts_spinlock_destroy(erts_spinlock_t *lock)
{
#ifdef USE_THREADS
int res;
+
+ ASSERT(!(lock->flags & ERTS_LOCK_FLAGS_PROPERTY_STATIC));
+
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_destroy_lock(&lock->lc);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_destroy_lock(&lock->lcnt);
+ erts_lcnt_uninstall(&lock->lcnt);
#endif
res = ethr_spinlock_destroy(&lock->slck);
if (res != 0) {
@@ -3218,39 +3120,26 @@ erts_lc_spinlock_is_locked(erts_spinlock_t *lock)
/* rwspinlock */
ERTS_GLB_INLINE void
-erts_rwlock_init_x(erts_rwlock_t *lock, char *name, Eterm extra)
+erts_rwlock_init(erts_rwlock_t *lock, char *name, Eterm extra, erts_lock_flags_t flags)
{
#ifdef USE_THREADS
int res = ethr_rwlock_init(&lock->rwlck);
- if (res)
- erts_thr_fatal_error(res, "init rwlock");
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock_x(&lock->lc, name, ERTS_LC_FLG_LT_RWSPINLOCK, extra);
-#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock_x(&lock->lcnt, name, ERTS_LCNT_LT_RWSPINLOCK, extra);
-#endif
-#else
- (void)lock;
+ if (res) {
+ erts_thr_fatal_error(res, "init rwlock");
+ }
+
+ flags |= ERTS_LOCK_TYPE_RWSPINLOCK;
+#ifdef DEBUG
+ lock->flags = flags;
#endif
-}
-ERTS_GLB_INLINE void
-erts_rwlock_init(erts_rwlock_t *lock, char *name)
-{
-#ifdef USE_THREADS
- int res = ethr_rwlock_init(&lock->rwlck);
- if (res)
- erts_thr_fatal_error(res, "init rwlock");
#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init_lock(&lock->lc, name, ERTS_LC_FLG_LT_RWSPINLOCK);
+ erts_lc_init_lock_x(&lock->lc, name, ERTS_LC_FLG_LT_RWSPINLOCK, extra);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init_lock(&lock->lcnt, name, ERTS_LCNT_LT_RWSPINLOCK);
-#endif
-#else
- (void)lock;
+ erts_lcnt_init_ref_x(&lock->lcnt, name, extra, flags);
#endif
+#endif /* USE_THREADS */
}
ERTS_GLB_INLINE void
@@ -3258,11 +3147,14 @@ erts_rwlock_destroy(erts_rwlock_t *lock)
{
#ifdef USE_THREADS
int res;
+
+ ASSERT(!(lock->flags & ERTS_LOCK_FLAGS_PROPERTY_STATIC));
+
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_destroy_lock(&lock->lc);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_destroy_lock(&lock->lcnt);
+ erts_lcnt_uninstall(&lock->lcnt);
#endif
res = ethr_rwlock_destroy(&lock->rwlck);
if (res != 0) {
@@ -3289,7 +3181,7 @@ erts_read_unlock(erts_rwlock_t *lock)
erts_lc_unlock_flg(&lock->lc, ERTS_LC_FLG_LO_READ);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_unlock_opt(&lock->lcnt, ERTS_LCNT_LO_READ);
+ erts_lcnt_unlock_opt(&lock->lcnt, ERTS_LOCK_OPTION_READ);
#endif
ethr_read_unlock(&lock->rwlck);
#else
@@ -3313,7 +3205,7 @@ erts_read_lock(erts_rwlock_t *lock)
#endif
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_opt(&lock->lcnt, ERTS_LCNT_LO_READ);
+ erts_lcnt_lock_opt(&lock->lcnt, ERTS_LOCK_OPTION_READ);
#endif
ethr_read_lock(&lock->rwlck);
#ifdef ERTS_ENABLE_LOCK_COUNT
@@ -3332,7 +3224,7 @@ erts_write_unlock(erts_rwlock_t *lock)
erts_lc_unlock_flg(&lock->lc, ERTS_LC_FLG_LO_READ_WRITE);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_unlock_opt(&lock->lcnt, ERTS_LCNT_LO_READ_WRITE);
+ erts_lcnt_unlock_opt(&lock->lcnt, ERTS_LOCK_OPTION_RDWR);
#endif
ethr_write_unlock(&lock->rwlck);
#else
@@ -3356,7 +3248,7 @@ erts_write_lock(erts_rwlock_t *lock)
#endif
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_lock_opt(&lock->lcnt, ERTS_LCNT_LO_READ_WRITE);
+ erts_lcnt_lock_opt(&lock->lcnt, ERTS_LOCK_OPTION_RDWR);
#endif
ethr_write_lock(&lock->rwlck);
#ifdef ERTS_ENABLE_LOCK_COUNT
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 3084a8db75..f6bb52dde1 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -954,8 +954,10 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
ASSERT(ERTS_MONOTONIC_TIME_MIN < ERTS_MONOTONIC_TIME_MAX);
- erts_smp_mtx_init(&erts_timeofday_mtx, "timeofday");
- erts_smp_mtx_init(&erts_get_time_mtx, "get_time");
+ erts_smp_mtx_init(&erts_timeofday_mtx, "timeofday", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
+ erts_smp_mtx_init(&erts_get_time_mtx, "get_time", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
time_sup.r.o.correction = time_correction;
time_sup.r.o.warp_mode = time_warp_mode;
@@ -1120,8 +1122,9 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED;
- erts_smp_rwmtx_init_opt(&time_sup.inf.c.parmon.rwmtx,
- &rwmtx_opts, "get_corrected_time");
+ erts_smp_rwmtx_init_opt(&time_sup.inf.c.parmon.rwmtx, &rwmtx_opts,
+ "get_corrected_time", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
cdatap = &time_sup.inf.c.parmon.cdata;
@@ -1286,56 +1289,62 @@ erts_finalize_time_offset(void)
/* info functions */
void
-elapsed_time_both(UWord *ms_user, UWord *ms_sys,
- UWord *ms_user_diff, UWord *ms_sys_diff)
+elapsed_time_both(ErtsMonotonicTime *ms_user, ErtsMonotonicTime *ms_sys,
+ ErtsMonotonicTime *ms_user_diff, ErtsMonotonicTime *ms_sys_diff)
{
- UWord prev_total_user, prev_total_sys;
- UWord total_user, total_sys;
+ ErtsMonotonicTime prev_total_user, prev_total_sys;
+ ErtsMonotonicTime total_user, total_sys;
SysTimes now;
sys_times(&now);
- total_user = (now.tms_utime * 1000) / SYS_CLK_TCK;
- total_sys = (now.tms_stime * 1000) / SYS_CLK_TCK;
+ total_user = (ErtsMonotonicTime) ((now.tms_utime * 1000) / SYS_CLK_TCK);
+ total_sys = (ErtsMonotonicTime) ((now.tms_stime * 1000) / SYS_CLK_TCK);
if (ms_user != NULL)
*ms_user = total_user;
if (ms_sys != NULL)
*ms_sys = total_sys;
- erts_smp_mtx_lock(&erts_timeofday_mtx);
+ if (ms_user_diff || ms_sys_diff) {
+ erts_smp_mtx_lock(&erts_timeofday_mtx);
- prev_total_user = (t_start.tms_utime * 1000) / SYS_CLK_TCK;
- prev_total_sys = (t_start.tms_stime * 1000) / SYS_CLK_TCK;
- t_start = now;
+ prev_total_user = (ErtsMonotonicTime) ((t_start.tms_utime * 1000) / SYS_CLK_TCK);
+ prev_total_sys = (ErtsMonotonicTime) ((t_start.tms_stime * 1000) / SYS_CLK_TCK);
+ t_start = now;
- erts_smp_mtx_unlock(&erts_timeofday_mtx);
+ erts_smp_mtx_unlock(&erts_timeofday_mtx);
- if (ms_user_diff != NULL)
- *ms_user_diff = total_user - prev_total_user;
+ if (ms_user_diff != NULL)
+ *ms_user_diff = total_user - prev_total_user;
- if (ms_sys_diff != NULL)
- *ms_sys_diff = total_sys - prev_total_sys;
+ if (ms_sys_diff != NULL)
+ *ms_sys_diff = total_sys - prev_total_sys;
+ }
}
/* wall clock routines */
void
-wall_clock_elapsed_time_both(UWord *ms_total, UWord *ms_diff)
+wall_clock_elapsed_time_both(ErtsMonotonicTime *ms_total, ErtsMonotonicTime *ms_diff)
{
ErtsMonotonicTime now, elapsed;
- erts_smp_mtx_lock(&erts_timeofday_mtx);
-
now = time_sup.r.o.get_time();
update_last_mtime(NULL, now);
elapsed = ERTS_MONOTONIC_TO_MSEC(now);
- *ms_total = (UWord) elapsed;
- *ms_diff = (UWord) (elapsed - prev_wall_clock_elapsed);
- prev_wall_clock_elapsed = elapsed;
- erts_smp_mtx_unlock(&erts_timeofday_mtx);
+ *ms_total = elapsed;
+
+ if (ms_diff) {
+ erts_smp_mtx_lock(&erts_timeofday_mtx);
+
+ *ms_diff = elapsed - prev_wall_clock_elapsed;
+ prev_wall_clock_elapsed = elapsed;
+
+ erts_smp_mtx_unlock(&erts_timeofday_mtx);
+ }
}
/* get current time */
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 4b06c55770..db7d0ac449 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -336,7 +336,8 @@ void erts_init_trace(void) {
rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED;
- erts_smp_rwmtx_init_opt(&sys_trace_rwmtx, &rwmtx_opts, "sys_tracers");
+ erts_smp_rwmtx_init_opt(&sys_trace_rwmtx, &rwmtx_opts, "sys_tracers", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
#ifdef HAVE_ERTS_NOW_CPU
erts_cpu_timestamp = 0;
@@ -2625,7 +2626,8 @@ init_sys_msg_dispatcher(void)
sys_message_queue = NULL;
sys_message_queue_end = NULL;
erts_smp_cnd_init(&smq_cnd);
- erts_smp_mtx_init(&smq_mtx, "sys_msg_q");
+ erts_smp_mtx_init(&smq_mtx, "sys_msg_q", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
erts_smp_thr_create(&sys_msg_dispatcher_tid,
sys_msg_dispatcher_func,
NULL,
@@ -3185,7 +3187,9 @@ static void init_tracer_nif()
erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED;
- erts_smp_rwmtx_init_opt(&tracer_mtx, &rwmtx_opt, "tracer_mtx");
+
+ erts_smp_rwmtx_init_opt(&tracer_mtx, &rwmtx_opt, "tracer_mtx", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
erts_tracer_nif_clear();
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index 07cf4f6903..3d28b05752 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -131,6 +131,7 @@ Eterm erts_bld_uint(Uint **hpp, Uint *szp, Uint ui);
Eterm erts_bld_uword(Uint **hpp, Uint *szp, UWord uw);
Eterm erts_bld_uint64(Uint **hpp, Uint *szp, Uint64 ui64);
Eterm erts_bld_sint64(Uint **hpp, Uint *szp, Sint64 si64);
+#define erts_bld_monotonic_time erts_bld_sint64
Eterm erts_bld_cons(Uint **hpp, Uint *szp, Eterm car, Eterm cdr);
Eterm erts_bld_tuple(Uint **hpp, Uint *szp, Uint arity, ...);
#define erts_bld_tuple2(H,S,E1,E2) erts_bld_tuple(H,S,2,E1,E2)
diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c
index 57f5ba5436..828c833ffc 100644
--- a/erts/emulator/beam/export.c
+++ b/erts/emulator/beam/export.c
@@ -182,7 +182,8 @@ init_export_table(void)
HashFunctions f;
int i;
- erts_smp_mtx_init(&export_staging_lock, "export_tab");
+ erts_smp_mtx_init(&export_staging_lock, "export_tab", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
erts_smp_atomic_init_nob(&total_entries_bytes, 0);
f.hash = (H_FUN) export_hash;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index fc95535ec3..2105ee7a6c 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1183,7 +1183,8 @@ void erts_ref_to_driver_monitor(Eterm ref, ErlDrvMonitor *mon);
Eterm erts_driver_monitor_to_ref(Eterm* hp, const ErlDrvMonitor *mon);
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT)
-void erts_lcnt_enable_io_lock_count(int enable);
+void erts_lcnt_update_driver_locks(int enable);
+void erts_lcnt_update_port_locks(int enable);
#endif
/* driver_tab.c */
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index d25e53ada0..b609f6de39 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -258,14 +258,7 @@ static ERTS_INLINE void port_init_instr(Port *prt
#ifdef ERTS_SMP
ASSERT(prt->drv_ptr && prt->lock);
if (!prt->drv_ptr->lock) {
- char *lock_str = "port_lock";
-#ifdef ERTS_ENABLE_LOCK_COUNT
- Uint16 opt = ((erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK)
- ? 0 : ERTS_LCNT_LT_DISABLE);
-#else
- Uint16 opt = 0;
-#endif
- erts_mtx_init_locked_x_opt(prt->lock, lock_str, id, opt);
+ erts_mtx_init_locked(prt->lock, "port_lock", id, ERTS_LOCK_FLAGS_CATEGORY_IO);
}
#endif
erts_port_task_init_sched(&prt->sched, id);
@@ -1968,7 +1961,6 @@ int
erts_port_output_async(Port *prt, Eterm from, Eterm list)
{
- ErtsPortOpResult res;
ErtsProc2PortSigData *sigdp;
erts_driver_t *drv = prt->drv_ptr;
size_t size;
@@ -2102,26 +2094,18 @@ erts_port_output_async(Port *prt, Eterm from, Eterm list)
sigdp->u.output.size = size;
port_sig_callback = port_sig_output;
}
- sigdp->flags = 0;
ns_pthp = NULL;
task_flags = 0;
- res = erts_schedule_proc2port_signal(NULL,
- prt,
- ERTS_INVALID_PID,
- NULL,
- sigdp,
- task_flags,
- ns_pthp,
- port_sig_callback);
+ erts_schedule_proc2port_signal(NULL,
+ prt,
+ ERTS_INVALID_PID,
+ NULL,
+ sigdp,
+ task_flags,
+ ns_pthp,
+ port_sig_callback);
- if (res != ERTS_PORT_OP_SCHEDULED) {
- if (drv->outputv)
- cleanup_scheduled_outputv(evp, cbin);
- else
- cleanup_scheduled_output(buf);
- return 1;
- }
return 1;
bad_value:
@@ -2554,10 +2538,6 @@ erts_port_output(Process *c_p,
port_sig_callback);
if (res != ERTS_PORT_OP_SCHEDULED) {
- if (drv->outputv)
- cleanup_scheduled_outputv(evp, cbin);
- else
- cleanup_scheduled_output(buf);
return res;
}
@@ -2736,21 +2716,14 @@ erts_port_exit(Process *c_p,
&bp->off_heap);
}
- res = erts_schedule_proc2port_signal(c_p,
- prt,
- c_p ? c_p->common.id : from,
- refp,
- sigdp,
- 0,
- NULL,
- port_sig_exit);
-
- if (res == ERTS_PORT_OP_DROPPED) {
- if (bp)
- free_message_buffer(bp);
- }
-
- return res;
+ return erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p ? c_p->common.id : from,
+ refp,
+ sigdp,
+ 0,
+ NULL,
+ port_sig_exit);
}
static ErtsPortOpResult
@@ -3419,9 +3392,8 @@ void erts_init_io(int port_tab_size,
else if (port_tab_size < ERTS_MIN_PORTS)
port_tab_size = ERTS_MIN_PORTS;
- erts_smp_rwmtx_init_opt(&erts_driver_list_lock,
- &drv_list_rwmtx_opts,
- "driver_list");
+ erts_smp_rwmtx_init_opt(&erts_driver_list_lock, &drv_list_rwmtx_opts, "driver_list", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
driver_list = NULL;
erts_smp_tsd_key_create(&driver_list_lock_status_key,
"erts_driver_list_lock_status_key");
@@ -3458,67 +3430,94 @@ void erts_init_io(int port_tab_size,
}
#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP)
-static ERTS_INLINE void lcnt_enable_drv_lock_count(erts_driver_t *dp, int enable)
+static void lcnt_enable_driver_lock_count(erts_driver_t *dp, int enable)
{
if (dp->lock) {
- if (enable)
- erts_lcnt_init_lock_x(&dp->lock->lcnt,
- "driver_lock",
- ERTS_LCNT_LT_MUTEX,
- erts_atom_put((byte*)dp->name,
- sys_strlen(dp->name),
- ERTS_ATOM_ENC_LATIN1,
- 1));
-
- else
- erts_lcnt_destroy_lock(&dp->lock->lcnt);
+ if (enable) {
+ Eterm name_as_atom = erts_atom_put((byte*)dp->name, sys_strlen(dp->name),
+ ERTS_ATOM_ENC_LATIN1, 1);
+ erts_lcnt_install_new_lock_info(&dp->lock->lcnt, "driver_lock", name_as_atom,
+ ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_IO);
+ } else {
+ erts_lcnt_uninstall(&dp->lock->lcnt);
+ }
}
}
-static ERTS_INLINE void lcnt_enable_port_lock_count(Port *prt, int enable)
+static void lcnt_enable_port_lock_count(Port *prt, int enable)
{
erts_aint32_t state = erts_atomic32_read_nob(&prt->state);
- if (!enable) {
- erts_lcnt_destroy_lock(&prt->sched.mtx.lcnt);
- if (state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK)
- erts_lcnt_destroy_lock(&prt->lock->lcnt);
+
+ if(enable) {
+ ErlDrvPDL pdl = prt->port_data_lock;
+
+ erts_lcnt_install_new_lock_info(&prt->sched.mtx.lcnt, "port_sched_lock",
+ prt->common.id, ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_IO);
+
+ if(pdl) {
+ erts_lcnt_install_new_lock_info(&pdl->mtx.lcnt, "port_data_lock",
+ prt->common.id, ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_IO);
+ }
+
+ if(state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK) {
+ erts_lcnt_install_new_lock_info(&prt->lock->lcnt, "port_lock",
+ prt->common.id, ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_IO);
+ }
+ } else {
+ erts_lcnt_uninstall(&prt->sched.mtx.lcnt);
+
+ if(prt->port_data_lock) {
+ erts_lcnt_uninstall(&prt->port_data_lock->mtx.lcnt);
+ }
+
+ if(state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK) {
+ erts_lcnt_uninstall(&prt->lock->lcnt);
+ }
}
- else {
- erts_lcnt_init_lock_x(&prt->sched.mtx.lcnt,
- "port_sched_lock",
- ERTS_LCNT_LT_MUTEX,
- prt->common.id);
- if (state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK)
- erts_lcnt_init_lock_x(&prt->lock->lcnt,
- "port_lock",
- ERTS_LCNT_LT_MUTEX,
- prt->common.id);
+}
+
+void erts_lcnt_update_driver_locks(int enable) {
+ erts_driver_t *driver;
+
+ lcnt_enable_driver_lock_count(&vanilla_driver, enable);
+ lcnt_enable_driver_lock_count(&spawn_driver, enable);
+#ifndef __WIN32__
+ lcnt_enable_driver_lock_count(&forker_driver, enable);
+#endif
+ lcnt_enable_driver_lock_count(&fd_driver, enable);
+
+ erts_rwmtx_rlock(&erts_driver_list_lock);
+
+ for (driver = driver_list; driver; driver = driver->next) {
+ lcnt_enable_driver_lock_count(driver, enable);
}
+
+ erts_rwmtx_runlock(&erts_driver_list_lock);
}
-void erts_lcnt_enable_io_lock_count(int enable) {
- erts_driver_t *dp;
- int ix, max = erts_ptab_max(&erts_port);
- Port *prt;
+void erts_lcnt_update_port_locks(int enable) {
+ int i, max;
- for (ix = 0; ix < max; ix++) {
- if ((prt = erts_pix2port(ix)) != NULL) {
- lcnt_enable_port_lock_count(prt, enable);
+ max = erts_ptab_max(&erts_port);
+
+ for(i = 0; i < max; i++) {
+ int delay_handle;
+ Port *port;
+
+ delay_handle = erts_thr_progress_unmanaged_delay();
+ port = erts_pix2port(i);
+
+ if(port != NULL) {
+ lcnt_enable_port_lock_count(port, enable);
}
- } /* for all ports */
- lcnt_enable_drv_lock_count(&vanilla_driver, enable);
- lcnt_enable_drv_lock_count(&spawn_driver, enable);
-#ifndef __WIN32__
- lcnt_enable_drv_lock_count(&forker_driver, enable);
-#endif
- lcnt_enable_drv_lock_count(&fd_driver, enable);
- /* enable lock counting in all drivers */
- for (dp = driver_list; dp; dp = dp->next) {
- lcnt_enable_drv_lock_count(dp, enable);
+ if(delay_handle != ERTS_THR_PRGR_DHANDLE_MANAGED) {
+ erts_thr_progress_unmanaged_continue(delay_handle);
+ }
}
-} /* enable/disable lock counting of ports */
+}
+
#endif /* defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP) */
/*
* Buffering of data when using line oriented I/O on ports
@@ -3701,7 +3700,7 @@ deliver_result(Port *prt, Eterm sender, Eterm pid, Eterm res)
ERTS_SMP_CHK_NO_PROC_LOCKS;
ASSERT(!prt || prt->common.id == sender);
-#ifdef ERTS_SMP
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
ASSERT(!prt || erts_lc_is_port_locked(prt));
#endif
@@ -4930,10 +4929,9 @@ erts_port_control(Process* c_p,
0,
NULL,
port_sig_control);
- if (res != ERTS_PORT_OP_SCHEDULED) {
- cleanup_scheduled_control(binp, bufp);
+ if (res != ERTS_PORT_OP_SCHEDULED)
return ERTS_PORT_OP_BADARG;
- }
+
return res;
}
@@ -5223,10 +5221,9 @@ erts_port_call(Process* c_p,
0,
NULL,
port_sig_call);
- if (res != ERTS_PORT_OP_SCHEDULED) {
- cleanup_scheduled_call(bufp);
+ if (res != ERTS_PORT_OP_SCHEDULED)
return ERTS_PORT_OP_BADARG;
- }
+
return res;
}
@@ -7093,7 +7090,7 @@ driver_pdl_create(ErlDrvPort dp)
return NULL;
pdl = erts_alloc(ERTS_ALC_T_PORT_DATA_LOCK,
sizeof(struct erl_drv_port_data_lock));
- erts_mtx_init_x(&pdl->mtx, "port_data_lock", pp->common.id);
+ erts_mtx_init(&pdl->mtx, "port_data_lock", pp->common.id, ERTS_LOCK_FLAGS_CATEGORY_IO);
pdl_init_refc(pdl);
erts_port_inc_refc(pp);
pdl->prt = pp;
@@ -8260,22 +8257,16 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle)
drv->flags = de->driver_flags;
drv->handle = handle;
#ifdef ERTS_SMP
- if (drv->flags & ERL_DRV_FLAG_USE_PORT_LOCKING)
- drv->lock = NULL;
- else {
- drv->lock = erts_alloc(ERTS_ALC_T_DRIVER_LOCK,
- sizeof(erts_mtx_t));
- erts_mtx_init_x(drv->lock,
- "driver_lock",
-#if defined(ERTS_ENABLE_LOCK_CHECK) || defined(ERTS_ENABLE_LOCK_COUNT)
- erts_atom_put((byte *) drv->name,
- sys_strlen(drv->name),
- ERTS_ATOM_ENC_LATIN1,
- 1)
-#else
- NIL
-#endif
- );
+ if (drv->flags & ERL_DRV_FLAG_USE_PORT_LOCKING) {
+ drv->lock = NULL;
+ } else {
+ Eterm driver_id = erts_atom_put((byte *) drv->name,
+ sys_strlen(drv->name),
+ ERTS_ATOM_ENC_LATIN1, 1);
+
+ drv->lock = erts_alloc(ERTS_ALC_T_DRIVER_LOCK, sizeof(erts_mtx_t));
+
+ erts_mtx_init(drv->lock, "driver_lock", driver_id, ERTS_LOCK_FLAGS_CATEGORY_IO);
}
#endif
drv->entry = de;
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index 8ab6c713d6..7987cb2eb5 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -120,7 +120,8 @@ void init_module_table(void)
}
for (i=0; i<ERTS_NUM_CODE_IX; i++) {
- erts_smp_rwmtx_init_x(&the_old_code_rwlocks[i], "old_code", make_small(i));
+ erts_smp_rwmtx_init(&the_old_code_rwlocks[i], "old_code", make_small(i),
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
}
erts_smp_atomic_init_nob(&tot_module_bytes, 0);
}
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 8abe871c14..4ce86ce949 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -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.
@@ -160,26 +160,19 @@ is_tuple Fail=f S | select_tuple_arity S=d Fail=f Size=u Rest=* => \
select_tuple_arity S=d Fail=f Size=u Rest=* => \
gen_select_tuple_arity(S, Fail, Size, Rest)
-i_select_val_bins x f I
-i_select_val_bins y f I
+i_select_val_bins xy f I
-i_select_val_lins x f I
-i_select_val_lins y f I
+i_select_val_lins xy f I
-i_select_val2 x f c c f f
-i_select_val2 y f c c f f
+i_select_val2 xy f c c f f
-i_select_tuple_arity x f I
-i_select_tuple_arity y f I
+i_select_tuple_arity xy f I
-i_select_tuple_arity2 x f A A f f
-i_select_tuple_arity2 y f A A f f
+i_select_tuple_arity2 xy f A A f f
-i_jump_on_val_zero x f I
-i_jump_on_val_zero y f I
+i_jump_on_val_zero xy f I
-i_jump_on_val x f I I
-i_jump_on_val y f I I
+i_jump_on_val xy f I I
%macro: get_list GetList -pack
get_list xy xy xy
@@ -448,19 +441,14 @@ is_ne_exact Lbl R=xy C=ian => i_is_ne_exact_immed Lbl R C
is_ne_exact Lbl R=xy C=q => i_is_ne_exact_literal Lbl R C
%macro: i_is_eq_exact_immed EqualImmed -fail_action
-i_is_eq_exact_immed f r c
-i_is_eq_exact_immed f x c
-i_is_eq_exact_immed f y c
-i_is_eq_exact_literal f x c
-i_is_eq_exact_literal f y c
+i_is_eq_exact_immed f rxy c
+i_is_eq_exact_literal f xy c
%macro: i_is_ne_exact_immed NotEqualImmed -fail_action
-i_is_ne_exact_immed f x c
-i_is_ne_exact_immed f y c
+i_is_ne_exact_immed f xy c
-i_is_ne_exact_literal f x c
-i_is_ne_exact_literal f y c
+i_is_ne_exact_literal f xy c
is_eq_exact Lbl Y=y X=x => is_eq_exact Lbl X Y
%macro: is_eq_exact EqualExact -fail_action -pack
@@ -508,8 +496,7 @@ i_put_tuple Dst Arity Puts=* | put S => \
i_put_tuple/2
%macro:i_put_tuple PutTuple -pack -goto:do_put_tuple
-i_put_tuple x I
-i_put_tuple y I
+i_put_tuple xy I
#
# The instruction "put_list Const [] Dst" were generated in rare
@@ -578,17 +565,12 @@ return_trace
move S x==0 | return => move_return S
%macro: move_return MoveReturn -nonext
-move_return x
-move_return c
-move_return n
+move_return xcn
move S x==0 | deallocate D | return => move_deallocate_return S D
%macro: move_deallocate_return MoveDeallocateReturn -pack -nonext
-move_deallocate_return x Q
-move_deallocate_return y Q
-move_deallocate_return c Q
-move_deallocate_return n Q
+move_deallocate_return xycn Q
deallocate D | return => deallocate_return D
@@ -1142,7 +1124,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 xy f I I d
+i_bs_start_match2 xy f I I x
bs_save2 Reg Index => gen_bs_save(Reg, Index)
i_bs_save2 x I
@@ -1159,12 +1141,12 @@ i_bs_match_string x f I I
bs_get_integer2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \
gen_get_integer2(Fail, Ms, Live, Sz, Unit, Flags, Dst)
-i_bs_get_integer_small_imm x I f I d
-i_bs_get_integer_imm x I I f I d
-i_bs_get_integer f I I s s d
-i_bs_get_integer_8 x f d
-i_bs_get_integer_16 x f d
-i_bs_get_integer_32 x f I d
+i_bs_get_integer_small_imm x I f I x
+i_bs_get_integer_imm x I I f I x
+i_bs_get_integer f I I s s x
+i_bs_get_integer_8 x f x
+i_bs_get_integer_16 x f x
+i_bs_get_integer_32 x f I x
# Fetching binaries from binaries.
bs_get_binary2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \
@@ -1174,9 +1156,9 @@ bs_get_binary2 Fail=f Ms=x Live=u Sz=sq Unit=u Flags=u Dst=d => \
%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
-i_bs_get_binary_all2 f x I I d
+i_bs_get_binary_imm2 f x I I I x
+i_bs_get_binary2 f x I s I x
+i_bs_get_binary_all2 f x I I x
i_bs_get_binary_all_reuse x f I
# Fetching float from binaries.
@@ -1186,7 +1168,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
-i_bs_get_float2 f x I s I d
+i_bs_get_float2 f x I s I x
# Miscellanous
@@ -1222,14 +1204,14 @@ bs_context_to_binary x
# Utf8/utf16/utf32 support. (R12B-5)
#
bs_get_utf8 Fail=f Ms=x u u Dst=d => i_bs_get_utf8 Ms Fail Dst
-i_bs_get_utf8 x f d
+i_bs_get_utf8 x f x
bs_skip_utf8 Fail=f Ms=x u u => i_bs_get_utf8 Ms Fail x
bs_get_utf16 Fail=f Ms=x u Flags=u Dst=d => i_bs_get_utf16 Ms Fail Flags Dst
bs_skip_utf16 Fail=f Ms=x u Flags=u => i_bs_get_utf16 Ms Fail Flags x
-i_bs_get_utf16 x f I d
+i_bs_get_utf16 x f I x
bs_get_utf32 Fail=f Ms=x Live=u Flags=u Dst=d => \
bs_get_integer2 Fail Ms Live i=32 u=1 Flags Dst | \
@@ -1262,15 +1244,15 @@ 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 xy j I d
+i_bs_init_fail xy j I x
-i_bs_init_fail_heap s I j I d
+i_bs_init_fail_heap s I j I x
-i_bs_init I I d
-i_bs_init_heap_bin I I d
+i_bs_init I I x
+i_bs_init_heap_bin I I x
-i_bs_init_heap I I I d
-i_bs_init_heap_bin_heap I I I d
+i_bs_init_heap I I I x
+i_bs_init_heap_bin_heap I I I x
bs_init_bits Fail Sz=o Words Regs Flags Dst => system_limit Fail
@@ -1283,16 +1265,16 @@ 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 xy j I d
+i_bs_init_bits_fail xy j I x
-i_bs_init_bits_fail_heap s I j I d
+i_bs_init_bits_fail_heap s I j I x
-i_bs_init_bits I I d
-i_bs_init_bits_heap I I I d
+i_bs_init_bits I I x
+i_bs_init_bits_heap I I I x
bs_add Fail S1=i==0 S2 Unit=u==1 D => move S2 D
-bs_add j s s I d
+bs_add j s s I x
bs_append Fail Size Extra Live Unit Bin Flags Dst => \
move Bin x | i_bs_append Fail Extra Live Unit Size Dst
@@ -1302,8 +1284,8 @@ bs_private_append Fail Size Unit Bin Flags Dst => \
bs_init_writable
-i_bs_append j I I I s d
-i_bs_private_append j I s s d
+i_bs_append j I I I s x
+i_bs_private_append j I s s x
#
# Storing integers into binaries.
@@ -1324,11 +1306,11 @@ i_new_bs_put_integer_imm j I I s
bs_utf8_size j Src=s Dst=d => i_bs_utf8_size Src Dst
-i_bs_utf8_size s d
+i_bs_utf8_size s x
bs_utf16_size j Src=s Dst=d => i_bs_utf16_size Src Dst
-i_bs_utf16_size s d
+i_bs_utf16_size s x
bs_put_utf8 Fail u Src=s => i_bs_put_utf8 Fail Src
diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c
index 7f60710124..bf3267cff1 100644
--- a/erts/emulator/beam/register.c
+++ b/erts/emulator/beam/register.c
@@ -145,7 +145,8 @@ void init_register_table(void)
rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ;
rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED;
- erts_smp_rwmtx_init_opt(&regtab_rwmtx, &rwmtx_opt, "reg_tab");
+ erts_smp_rwmtx_init_opt(&regtab_rwmtx, &rwmtx_opt, "reg_tab", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
f.hash = (H_FUN) reg_hash;
f.cmp = (HCMP_FUN) reg_cmp;
diff --git a/erts/emulator/beam/safe_hash.c b/erts/emulator/beam/safe_hash.c
index 30b26a7296..527c9efeca 100644
--- a/erts/emulator/beam/safe_hash.c
+++ b/erts/emulator/beam/safe_hash.c
@@ -155,7 +155,8 @@ int safe_hash_table_sz(SafeHash *h)
** Init a pre allocated or static hash structure
** and allocate buckets. NOT SAFE
*/
-SafeHash* safe_hash_init(ErtsAlcType_t type, SafeHash* h, char* name, int size, SafeHashFunctions fun)
+SafeHash* safe_hash_init(ErtsAlcType_t type, SafeHash* h, char* name, erts_lock_flags_t flags,
+ int size, SafeHashFunctions fun)
{
int i, bytes;
@@ -170,7 +171,8 @@ SafeHash* safe_hash_init(ErtsAlcType_t type, SafeHash* h, char* name, int size,
erts_smp_atomic_init_nob(&h->is_rehashing, 0);
erts_smp_atomic_init_nob(&h->nitems, 0);
for (i=0; i<SAFE_HASH_LOCK_CNT; i++) {
- erts_smp_mtx_init(&h->lock_vec[i].mtx,"safe_hash");
+ erts_smp_mtx_init(&h->lock_vec[i].mtx, "safe_hash", NIL,
+ flags);
}
return h;
}
@@ -273,5 +275,22 @@ void safe_hash_for_each(SafeHash* h, void (*func)(void *, void *), void *func_ar
}
}
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void erts_lcnt_enable_hash_lock_count(SafeHash *h, erts_lock_flags_t flags, int enable) {
+ int i;
+
+ for(i = 0; i < SAFE_HASH_LOCK_CNT; i++) {
+ erts_smp_mtx_t *lock = &h->lock_vec[i].mtx;
+
+ if(enable) {
+ erts_lcnt_install_new_lock_info(&lock->lcnt, "safe_hash", NIL,
+ ERTS_LOCK_TYPE_MUTEX | flags);
+ } else {
+ erts_lcnt_uninstall(&lock->lcnt);
+ }
+ }
+}
+#endif /* ERTS_ENABLE_LOCK_COUNT */
+
#endif /* !ERTS_SYS_CONTINOUS_FD_NUMBERS */
diff --git a/erts/emulator/beam/safe_hash.h b/erts/emulator/beam/safe_hash.h
index 285103cb17..dde48a6de8 100644
--- a/erts/emulator/beam/safe_hash.h
+++ b/erts/emulator/beam/safe_hash.h
@@ -28,6 +28,7 @@
#include "sys.h"
#include "erl_alloc.h"
+#include "erl_lock_flags.h"
typedef unsigned long SafeHashValue;
@@ -85,7 +86,7 @@ typedef struct
/* A: Lockless atomics */
} SafeHash;
-SafeHash* safe_hash_init(ErtsAlcType_t, SafeHash*, char*, int, SafeHashFunctions);
+SafeHash* safe_hash_init(ErtsAlcType_t, SafeHash*, char*, erts_lock_flags_t, int, SafeHashFunctions);
void safe_hash_get_info(SafeHashInfo*, SafeHash*);
int safe_hash_table_sz(SafeHash *);
@@ -96,5 +97,9 @@ void* safe_hash_erase(SafeHash*, void*);
void safe_hash_for_each(SafeHash*, void (*func)(void *, void *), void *);
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void erts_lcnt_enable_hash_lock_count(SafeHash*, erts_lock_flags_t, int);
+#endif
+
#endif /* __SAFE_HASH_H__ */
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index d752ea4330..e8615d1140 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -21,7 +21,7 @@
#ifndef __SYS_H__
#define __SYS_H__
-#if !defined(__GNUC__)
+#if !defined(__GNUC__) || defined(__e2k__)
# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0
#elif !defined(__GNUC_MINOR__)
# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \
@@ -784,10 +784,10 @@ Preload* sys_preloaded(void);
unsigned char* sys_preload_begin(Preload*);
void sys_preload_end(Preload*);
int sys_get_key(int);
-void elapsed_time_both(UWord *ms_user, UWord *ms_sys,
- UWord *ms_user_diff, UWord *ms_sys_diff);
-void wall_clock_elapsed_time_both(UWord *ms_total,
- UWord *ms_diff);
+void elapsed_time_both(ErtsMonotonicTime *ms_user, ErtsMonotonicTime *ms_sys,
+ ErtsMonotonicTime *ms_user_diff, ErtsMonotonicTime *ms_sys_diff);
+void wall_clock_elapsed_time_both(ErtsMonotonicTime *ms_total,
+ ErtsMonotonicTime *ms_diff);
void get_time(int *hour, int *minute, int *second);
void get_date(int *year, int *month, int *day);
void get_localtime(int *year, int *month, int *day,
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 457cada745..0fb25c2082 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -42,6 +42,7 @@
#include "dist.h"
#include "erl_printf.h"
#include "erl_threads.h"
+#include "erl_lock_count.h"
#include "erl_smp.h"
#include "erl_time.h"
#include "erl_thr_progress.h"
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index 1538191d67..3a7b3bb50c 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -742,7 +742,8 @@ file_init(void)
efile_init();
#ifdef USE_VM_PROBES
- erts_mtx_init(&dt_driver_mutex, "efile_drv dtrace mutex");
+ erts_mtx_init(&dt_driver_mutex, "efile_drv dtrace mutex", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
pthread_key_create(&dt_driver_key, NULL);
#endif /* USE_VM_PROBES */
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 13ee935e45..fe421bfe12 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -4334,6 +4334,12 @@ static void desc_close(inet_descriptor* desc)
desc->event = INVALID_EVENT; /* closed by stop_select callback */
desc->s = INVALID_SOCKET;
desc->event_mask = 0;
+
+ /* mark as disconnected in case when socket is left lingering due to
+ * {exit_on_close, false} option in gen_tcp socket creation. Next
+ * write to socket should produce {error, enotconn} and send a
+ * message {tcp_error,#Port<>,econnreset} */
+ desc->state &= ~INET_STATE_CONNECTED;
}
}
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 0225f17613..94bc563fda 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -1129,7 +1129,8 @@ struct hipe_ref {
static inline void hipe_mfa_info_table_init_lock(void)
{
- erts_smp_rwmtx_init(&hipe_mfa_info_table.lock, "hipe_mfait_lock");
+ erts_smp_rwmtx_init(&hipe_mfa_info_table.lock, "hipe_mfait_lock", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
}
static inline void hipe_mfa_info_table_rlock(void)
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index ad580e7d52..799f67fc45 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -2564,24 +2564,22 @@ ERTS_CIO_EXPORT(erts_init_check_io)(void)
#ifdef ERTS_SMP
init_removed_fd_alloc();
pollset.removed_list = NULL;
- erts_smp_spinlock_init(&pollset.removed_list_lock,
- "pollset_rm_list");
+ erts_smp_spinlock_init(&pollset.removed_list_lock, "pollset_rm_list", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
{
- int i;
- for (i=0; i<DRV_EV_STATE_LOCK_CNT; i++) {
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_smp_mtx_init_x(&drv_ev_state_locks[i].lck, "drv_ev_state", make_small(i));
-#else
- erts_smp_mtx_init(&drv_ev_state_locks[i].lck, "drv_ev_state");
-#endif
- }
+ int i;
+ for (i=0; i<DRV_EV_STATE_LOCK_CNT; i++) {
+ erts_smp_mtx_init(&drv_ev_state_locks[i].lck, "drv_ev_state", make_small(i),
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
+ }
}
#endif
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
max_fds = ERTS_CIO_POLL_MAX_FDS();
erts_smp_atomic_init_nob(&drv_ev_state_len, 0);
drv_ev_state = NULL;
- erts_smp_mtx_init(&drv_ev_state_grow_lock, "drv_ev_state_grow");
+ erts_smp_mtx_init(&drv_ev_state_grow_lock, "drv_ev_state_grow", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
#else
{
SafeHashFunctions hf;
@@ -2591,10 +2589,11 @@ ERTS_CIO_EXPORT(erts_init_check_io)(void)
hf.free = &drv_ev_state_free;
num_state_prealloc = 0;
state_prealloc_first = NULL;
- erts_smp_spinlock_init(&state_prealloc_lock,"state_prealloc");
+ erts_smp_spinlock_init(&state_prealloc_lock,"state_prealloc", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
- safe_hash_init(ERTS_ALC_T_DRV_EV_STATE, &drv_ev_state_tab, "drv_ev_state_tab",
- DRV_EV_STATE_HTAB_SIZE, hf);
+ safe_hash_init(ERTS_ALC_T_DRV_EV_STATE, &drv_ev_state_tab, "drv_ev_state_tab",
+ ERTS_LOCK_FLAGS_CATEGORY_IO, DRV_EV_STATE_HTAB_SIZE, hf);
}
#endif
}
@@ -3130,3 +3129,12 @@ ERTS_CIO_EXPORT(erts_check_io_debug)(ErtsCheckIoDebugInfo *ciodip)
return counters.num_errors;
}
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void ERTS_CIO_EXPORT(erts_lcnt_update_cio_locks)(int enable) {
+#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS
+ erts_lcnt_enable_hash_lock_count(&drv_ev_state_tab, ERTS_LOCK_FLAGS_CATEGORY_IO, enable);
+#else
+ (void)enable;
+#endif
+}
+#endif /* ERTS_ENABLE_LOCK_COUNT */
diff --git a/erts/emulator/sys/common/erl_check_io.h b/erts/emulator/sys/common/erl_check_io.h
index ee4abeece9..2d3bb98afa 100644
--- a/erts/emulator/sys/common/erl_check_io.h
+++ b/erts/emulator/sys/common/erl_check_io.h
@@ -59,6 +59,11 @@ void erts_init_check_io_nkp(void);
int erts_check_io_debug_kp(ErtsCheckIoDebugInfo *);
int erts_check_io_debug_nkp(ErtsCheckIoDebugInfo *);
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void erts_lcnt_update_cio_locks_kp(int enable);
+void erts_lcnt_update_cio_locks_nkp(int enable);
+#endif
+
#else /* !ERTS_ENABLE_KERNEL_POLL */
Uint erts_check_io_size(void);
@@ -72,6 +77,10 @@ void erts_check_io_interrupt_timed(int, ErtsMonotonicTime);
void erts_check_io(int);
void erts_init_check_io(void);
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void erts_lcnt_update_cio_locks(int enable);
+#endif
+
#endif
extern erts_smp_atomic_t erts_check_io_time;
diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c
index bb930ff03b..214ed01c82 100644
--- a/erts/emulator/sys/common/erl_mmap.c
+++ b/erts/emulator/sys/common/erl_mmap.c
@@ -2212,9 +2212,11 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
erts_exit(1, "erts_mmap: Failed to open /dev/zero\n");
#endif
- erts_smp_mtx_init(&mm->mtx, "erts_mmap");
+ erts_smp_mtx_init(&mm->mtx, "erts_mmap", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
if (is_first_call) {
- erts_mtx_init(&am.init_mutex, "mmap_init_atoms");
+ erts_mtx_init(&am.init_mutex, "mmap_init_atoms", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
}
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
@@ -2816,7 +2818,8 @@ static void hard_dbg_mseg_init(void)
{
ErtsFreeSegDesc_fake* p;
- erts_mtx_init(&hard_dbg_mseg_mtx, "hard_dbg_mseg");
+ erts_mtx_init(&hard_dbg_mseg_mtx, "hard_dbg_mseg", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
hard_dbg_mseg_tree.root = NULL;
hard_dbg_mseg_tree.order = ADDR_ORDER;
diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c
index b8f0bb7150..d69a79dc2a 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -1420,7 +1420,8 @@ erts_mseg_init(ErtsMsegInit_t *init)
atoms_initialized = 0;
- erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms");
+ erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
#ifdef ERTS_HAVE_EXEC_MMAPPER
/* Initialize erts_exec_mapper *FIRST*, to increase probability
@@ -1449,7 +1450,8 @@ erts_mseg_init(ErtsMsegInit_t *init)
ma->is_thread_safe = 0;
else {
ma->is_thread_safe = 1;
- erts_mtx_init(&ma->mtx, "mseg");
+ erts_mtx_init(&ma->mtx, "mseg", make_small(i),
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR);
}
ma->is_cache_check_scheduled = 0;
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index 5e7ae8953a..52a8b6a53f 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -336,7 +336,7 @@ static void fatal_error_async_signal_safe(char *error_str);
static int max_fds = -1;
static ErtsPollSet pollsets;
-static erts_smp_spinlock_t pollsets_lock;
+static erts_smp_mtx_t pollsets_lock;
#if ERTS_POLL_USE_POLL
@@ -2583,7 +2583,8 @@ ERTS_POLL_EXPORT(erts_poll_max_fds)(void)
void
ERTS_POLL_EXPORT(erts_poll_init)(void)
{
- erts_smp_spinlock_init(&pollsets_lock, "pollsets_lock");
+ erts_smp_mtx_init(&pollsets_lock, "pollsets_lock", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
pollsets = NULL;
errno = 0;
@@ -2689,7 +2690,7 @@ ERTS_POLL_EXPORT(erts_poll_create_pollset)(void)
#endif
#ifdef ERTS_SMP
erts_atomic32_init_nob(&ps->polled, 0);
- erts_smp_mtx_init(&ps->mtx, "pollset");
+ erts_smp_mtx_init(&ps->mtx, "pollset", NIL, ERTS_LOCK_FLAGS_CATEGORY_IO);
#endif
#if defined(USE_THREADS) || ERTS_POLL_ASYNC_INTERRUPT_SUPPORT
erts_atomic32_init_nob(&ps->wakeup_state, (erts_aint32_t) 0);
@@ -2731,10 +2732,10 @@ ERTS_POLL_EXPORT(erts_poll_create_pollset)(void)
#endif
erts_smp_atomic_set_nob(&ps->no_of_user_fds, 0); /* Don't count wakeup pipe and fallback fd */
- erts_smp_spin_lock(&pollsets_lock);
+ erts_smp_mtx_lock(&pollsets_lock);
ps->next = pollsets;
pollsets = ps;
- erts_smp_spin_unlock(&pollsets_lock);
+ erts_smp_mtx_unlock(&pollsets_lock);
return ps;
}
@@ -2795,7 +2796,7 @@ ERTS_POLL_EXPORT(erts_poll_destroy_pollset)(ErtsPollSet ps)
close(ps->timer_fd);
#endif
- erts_smp_spin_lock(&pollsets_lock);
+ erts_smp_mtx_lock(&pollsets_lock);
if (ps == pollsets)
pollsets = pollsets->next;
else {
@@ -2805,7 +2806,7 @@ ERTS_POLL_EXPORT(erts_poll_destroy_pollset)(ErtsPollSet ps)
ASSERT(ps == prev_ps->next);
prev_ps->next = ps->next;
}
- erts_smp_spin_unlock(&pollsets_lock);
+ erts_smp_mtx_unlock(&pollsets_lock);
erts_free(ERTS_ALC_T_POLLSET, (void *) ps);
}
@@ -3148,3 +3149,26 @@ print_misc_debug_info(void)
}
#endif
+
+#ifdef ERTS_ENABLE_LOCK_COUNT
+static void erts_lcnt_enable_pollset_lock_count(ErtsPollSet pollset, int enable) {
+ if(enable) {
+ erts_lcnt_install_new_lock_info(&pollset->mtx.lcnt, "pollset_rm", NIL,
+ ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_IO);
+ } else {
+ erts_lcnt_uninstall(&pollset->mtx.lcnt);
+ }
+}
+
+void ERTS_POLL_EXPORT(erts_lcnt_update_pollset_locks)(int enable) {
+ ErtsPollSet iterator;
+
+ erts_smp_mtx_lock(&pollsets_lock);
+
+ for(iterator = pollsets; iterator != NULL; iterator = iterator->next) {
+ erts_lcnt_enable_pollset_lock_count(iterator, enable);
+ }
+
+ erts_smp_mtx_unlock(&pollsets_lock);
+}
+#endif
diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h
index c16122610d..b3b4d79984 100644
--- a/erts/emulator/sys/common/erl_poll.h
+++ b/erts/emulator/sys/common/erl_poll.h
@@ -260,4 +260,8 @@ void ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet,
int erts_poll_new_table_len(int old_len, int need_len);
+#ifdef ERTS_ENABLE_LOCK_COUNT
+void ERTS_POLL_EXPORT(erts_lcnt_update_pollset_locks)(int enable);
+#endif
+
#endif /* #ifndef ERL_POLL_H__ */
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 5cf0a49972..50d8a35217 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -438,14 +438,18 @@ erts_sys_pre_init(void)
/* After creation in parent */
eid.thread_create_parent_func = thr_create_cleanup,
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_pre_thr_init();
+#endif
+
erts_thr_init(&eid);
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init();
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_post_thr_init();
#endif
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init();
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_init();
#endif
#endif /* USE_THREADS */
@@ -1545,7 +1549,8 @@ erl_sys_args(int* argc, char** argv)
{
int i, j;
- erts_smp_rwmtx_init(&environ_rwmtx, "environ");
+ erts_smp_rwmtx_init(&environ_rwmtx, "environ", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
i = 1;
diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c
index 4f26639703..102ef7bebf 100644
--- a/erts/emulator/sys/unix/sys_time.c
+++ b/erts/emulator/sys/unix/sys_time.c
@@ -304,8 +304,8 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
erts_sys_time_data__.r.o.os_times =
clock_gettime_times_verified;
#endif
- erts_smp_mtx_init(&internal_state.w.f.mtx,
- "os_monotonic_time");
+ erts_smp_mtx_init(&internal_state.w.f.mtx, "os_monotonic_time", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
internal_state.w.f.last_delivered
= clock_gettime_monotonic();
init_resp->os_monotonic_time_info.locked_use = 1;
diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c
index b10fc1e430..8743f83a50 100644
--- a/erts/emulator/sys/win32/erl_poll.c
+++ b/erts/emulator/sys/win32/erl_poll.c
@@ -142,7 +142,8 @@ static erts_mtx_t save_ops_mtx;
static void poll_debug_init(void)
{
- erts_mtx_init(&save_ops_mtx, "save_ops_lock");
+ erts_mtx_init(&save_ops_mtx, "save_ops_lock", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
}
void poll_debug_set_active_fd(ErtsSysFdType fd)
@@ -677,7 +678,7 @@ static void new_waiter(ErtsPollSet ps)
w->active_events = 1;
w->highwater = 1;
w->total_events = 1;
- erts_mtx_init(&w->mtx, "pollwaiter");
+ erts_mtx_init(&w->mtx, "pollwaiter", NIL, ERTS_LOCK_FLAGS_CATEGORY_IO);
/*
@@ -1359,7 +1360,7 @@ ErtsPollSet erts_poll_create_pollset(void)
erts_atomic32_init_nob(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN);
#ifdef ERTS_SMP
- erts_smp_mtx_init(&ps->mtx, "pollset");
+ erts_smp_mtx_init(&ps->mtx, "pollset", NIL, ERTS_LOCK_FLAGS_CATEGORY_IO);
#endif
init_timeout_time(ps);
@@ -1411,7 +1412,8 @@ void erts_poll_init(void)
HARDTRACEF(("In erts_poll_init"));
erts_sys_break_event = CreateManualEvent(FALSE);
- erts_mtx_init(&break_waiter_lock,"break_waiter_lock");
+ erts_mtx_init(&break_waiter_lock, "break_waiter_lock", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_IO);
break_happened_event = CreateManualEvent(FALSE);
erts_atomic32_init_nob(&break_waiter_state, 0);
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index 28019e306c..15c59109b1 100644
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -3195,15 +3195,22 @@ erts_sys_pre_init(void)
/* After creation in parent */
eid.thread_create_parent_func = thr_create_cleanup;
- erts_thr_init(&eid);
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init();
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_pre_thr_init();
#endif
+
+ erts_thr_init(&eid);
+
#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_init();
+ erts_lcnt_post_thr_init();
#endif
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_init();
#endif
+#endif /* USE_THREADS */
+
erts_init_sys_time_sup();
erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0);
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c
index 21ef71ad9a..8fcee1cbb6 100644
--- a/erts/emulator/sys/win32/sys_env.c
+++ b/erts/emulator/sys/win32/sys_env.c
@@ -37,7 +37,8 @@ static erts_smp_rwmtx_t environ_rwmtx;
void
erts_sys_env_init(void)
{
- erts_smp_rwmtx_init(&environ_rwmtx, "environ");
+ erts_smp_rwmtx_init(&environ_rwmtx, "environ", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
}
int
diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c
index e8c67b3928..88131aaa6a 100644
--- a/erts/emulator/sys/win32/sys_time.c
+++ b/erts/emulator/sys/win32/sys_time.c
@@ -300,8 +300,8 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
module = GetModuleHandle(kernel_dll_name);
if (!module) {
get_tick_count:
- erts_smp_mtx_init(&internal_state.w.f.mtime_mtx,
- "os_monotonic_time");
+ erts_smp_mtx_init(&internal_state.w.f.mtime_mtx, "os_monotonic_time", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
internal_state.w.f.wrap = 0;
internal_state.w.f.last_tick_count = 0;
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index fcd7244ae9..370fcb0f3a 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -73,6 +73,7 @@ MODULES= \
hipe_SUITE \
list_bif_SUITE \
lttng_SUITE \
+ lcnt_SUITE \
map_SUITE \
match_spec_SUITE \
module_info_SUITE \
diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl
index 9cc86014ec..5b04a15b85 100644
--- a/erts/emulator/test/a_SUITE.erl
+++ b/erts/emulator/test/a_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl
index 6409f0b336..8a34195e8d 100644
--- a/erts/emulator/test/after_SUITE.erl
+++ b/erts/emulator/test/after_SUITE.erl
@@ -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/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl
index 779d81daa5..b79f4b995d 100644
--- a/erts/emulator/test/bs_construct_SUITE.erl
+++ b/erts/emulator/test/bs_construct_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl
index 644848daf5..4e7004a424 100644
--- a/erts/emulator/test/busy_port_SUITE.erl
+++ b/erts/emulator/test/busy_port_SUITE.erl
@@ -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/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index ab0fc0d42c..77321aa50f 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
index 1ab39466db..0321b9898f 100644
--- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
+++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2009-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/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index 954f3b137f..6810729285 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -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/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl
index eaf6b58c33..f0e1bcf04b 100644
--- a/erts/emulator/test/efile_SUITE.erl
+++ b/erts/emulator/test/efile_SUITE.erl
@@ -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/erts/emulator/test/evil_SUITE.erl b/erts/emulator/test/evil_SUITE.erl
index fb1954ce37..fc4ac037ac 100644
--- a/erts/emulator/test/evil_SUITE.erl
+++ b/erts/emulator/test/evil_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index ad6d8c890f..aaca522da6 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -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/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl
index e4640909aa..7d29ebec52 100644
--- a/erts/emulator/test/fun_SUITE.erl
+++ b/erts/emulator/test/fun_SUITE.erl
@@ -22,6 +22,7 @@
-export([all/0, suite/0,
bad_apply/1,bad_fun_call/1,badarity/1,ext_badarity/1,
+ bad_arglist/1,
equality/1,ordering/1,
fun_to_port/1,t_phash/1,t_phash2/1,md5/1,
refc/1,refc_ets/1,refc_dist/1,
@@ -39,6 +40,7 @@ suite() ->
all() ->
[bad_apply, bad_fun_call, badarity, ext_badarity,
+ bad_arglist,
equality, ordering, fun_to_port, t_phash,
t_phash2, md5, refc, refc_ets, refc_dist,
const_propagation, t_arity, t_is_function2, t_fun_info,
@@ -107,6 +109,18 @@ bad_call_fc(Fun) ->
ct:fail({bad_result,Other})
end.
+% Test erlang:apply with non-proper arg-list
+bad_arglist(Config) when is_list(Config) ->
+ Fun = fun(A,B) -> A+B end,
+ {'EXIT', {badarg,_}} = (catch apply(Fun, 17)),
+ {'EXIT', {badarg,_}} = (catch apply(Fun, [17|18])),
+ {'EXIT', {badarg,_}} = (catch apply(Fun, [17,18|19])),
+ {'EXIT', {badarg,_}} = (catch apply(lists,seq, 17)),
+ {'EXIT', {badarg,_}} = (catch apply(lists,seq, [17|18])),
+ {'EXIT', {badarg,_}} = (catch apply(lists,seq, [17,18|19])),
+ ok.
+
+
%% Call and apply valid funs with wrong number of arguments.
badarity(Config) when is_list(Config) ->
diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl
index 2c2cb9c32d..f3942ef416 100644
--- a/erts/emulator/test/gc_SUITE.erl
+++ b/erts/emulator/test/gc_SUITE.erl
@@ -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/erts/emulator/test/hipe_SUITE.erl b/erts/emulator/test/hipe_SUITE.erl
index 5083f01f34..e62d4260f6 100644
--- a/erts/emulator/test/hipe_SUITE.erl
+++ b/erts/emulator/test/hipe_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2016. All Rights Reserved.
+%% Copyright Ericsson AB 2016-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/erts/emulator/test/lcnt_SUITE.erl b/erts/emulator/test/lcnt_SUITE.erl
new file mode 100644
index 0000000000..504b9b54cf
--- /dev/null
+++ b/erts/emulator/test/lcnt_SUITE.erl
@@ -0,0 +1,156 @@
+%%
+%% %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(lcnt_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+
+-export(
+ [all/0, suite/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
+
+-export(
+ [toggle_lock_counting/1, error_on_invalid_category/1, preserve_locks/1]).
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 10}}].
+
+all() ->
+ [toggle_lock_counting, error_on_invalid_category, preserve_locks].
+
+init_per_suite(Config) ->
+ case erlang:system_info(lock_counting) of
+ true ->
+ %% The tests will run straight over these properties, so we have to
+ %% preserve them to avoid tainting the other tests.
+ OldCopySave = erts_debug:lcnt_control(copy_save),
+ OldMask = erts_debug:lcnt_control(mask),
+ [{lcnt_SUITE, {OldCopySave, OldMask}} | Config];
+ _ ->
+ {skip, "Lock counting is not enabled"}
+ end.
+
+end_per_suite(Config) ->
+ {OldCopySave, OldMask} = proplists:get_value(lcnt_SUITE, Config),
+
+ erts_debug:lcnt_control(copy_save, OldCopySave),
+ OldCopySave = erts_debug:lcnt_control(copy_save),
+
+ erts_debug:lcnt_control(mask, OldMask),
+ OldMask = erts_debug:lcnt_control(mask),
+
+ erts_debug:lcnt_clear(),
+ ok.
+
+init_per_testcase(_Case, Config) ->
+ disable_lock_counting(),
+ Config.
+
+end_per_testcase(_Case, _Config) ->
+ ok.
+
+disable_lock_counting() ->
+ ok = erts_debug:lcnt_control(copy_save, false),
+ ok = erts_debug:lcnt_control(mask, []),
+ ok = erts_debug:lcnt_clear(),
+
+ %% Sanity check.
+ false = erts_debug:lcnt_control(copy_save),
+ [] = erts_debug:lcnt_control(mask),
+
+ %% The above commands rely on some lazy operations, so we'll have to wait
+ %% for the list to clear.
+ ok = wait_for_empty_lock_list().
+
+wait_for_empty_lock_list() ->
+ wait_for_empty_lock_list(10).
+wait_for_empty_lock_list(Tries) when Tries > 0 ->
+ try_flush_cleanup_ops(),
+ case erts_debug:lcnt_collect() of
+ [{duration, _}, {locks, []}] ->
+ ok;
+ _ ->
+ timer:sleep(50),
+ wait_for_empty_lock_list(Tries - 1)
+ end;
+wait_for_empty_lock_list(0) ->
+ ct:fail("Lock list failed to clear after disabling lock counting.").
+
+%% Queue up a lot of thread progress cleanup ops in a vain attempt to
+%% flush the lock list.
+try_flush_cleanup_ops() ->
+ false = lists:member(process, erts_debug:lcnt_control(mask)),
+ [spawn(fun() -> ok end) || _ <- lists:seq(1, 1000)].
+
+%%
+%% Test cases
+%%
+
+toggle_lock_counting(Config) when is_list(Config) ->
+ Categories =
+ [allocator, db, debug, distribution, generic, io, process, scheduler],
+ lists:foreach(
+ fun(Category) ->
+ Locks = get_lock_info_for(Category),
+ if
+ Locks =/= [] ->
+ disable_lock_counting();
+ Locks =:= [] ->
+ ct:fail("Failed to toggle ~p locks.", [Category])
+ end
+ end, Categories).
+
+get_lock_info_for(Categories) when is_list(Categories) ->
+ ok = erts_debug:lcnt_control(mask, Categories),
+ [{duration, _}, {locks, Locks}] = erts_debug:lcnt_collect(),
+ Locks;
+
+get_lock_info_for(Category) when is_atom(Category) ->
+ get_lock_info_for([Category]).
+
+preserve_locks(Config) when is_list(Config) ->
+ erts_debug:lcnt_control(mask, [process]),
+
+ erts_debug:lcnt_control(copy_save, true),
+ [spawn(fun() -> ok end) || _ <- lists:seq(1, 1000)],
+
+ %% Wait for the processes to be fully destroyed before disabling copy_save,
+ %% then remove all active locks from the list. (There's no foolproof method
+ %% to do this; sleeping before/after is the best way we have)
+ timer:sleep(500),
+
+ erts_debug:lcnt_control(copy_save, false),
+ erts_debug:lcnt_control(mask, []),
+
+ try_flush_cleanup_ops(),
+ timer:sleep(500),
+
+ case erts_debug:lcnt_collect() of
+ [{duration, _}, {locks, Locks}] when length(Locks) > 0 ->
+ ct:pal("Preserved ~p locks.", [length(Locks)]);
+ [{duration, _}, {locks, []}] ->
+ ct:fail("copy_save didn't preserve any locks.")
+ end.
+
+error_on_invalid_category(Config) when is_list(Config) ->
+ {error, badarg, q_invalid} = erts_debug:lcnt_control(mask, [q_invalid]),
+ ok.
diff --git a/erts/emulator/test/long_timers_test.erl b/erts/emulator/test/long_timers_test.erl
index 896ac5e1f6..de1a6e6d32 100644
--- a/erts/emulator/test/long_timers_test.erl
+++ b/erts/emulator/test/long_timers_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl
index 1c1952f912..a012fa1da2 100644
--- a/erts/emulator/test/lttng_SUITE.erl
+++ b/erts/emulator/test/lttng_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl
index 0d5f4aa0af..7f0cbdd885 100644
--- a/erts/emulator/test/message_queue_data_SUITE.erl
+++ b/erts/emulator/test/message_queue_data_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2014-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/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl
index ed3ab23738..9d772480d9 100644
--- a/erts/emulator/test/monitor_SUITE.erl
+++ b/erts/emulator/test/monitor_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl
index 0d6ab5cdb2..843e917dfc 100644
--- a/erts/emulator/test/mtx_SUITE.erl
+++ b/erts/emulator/test/mtx_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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/erts/emulator/test/nested_SUITE.erl b/erts/emulator/test/nested_SUITE.erl
index 5059317172..f1b4c03ae4 100644
--- a/erts/emulator/test/nested_SUITE.erl
+++ b/erts/emulator/test/nested_SUITE.erl
@@ -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/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index f512fa3a57..ab0b1a82bd 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -86,6 +86,7 @@
cd_relative/1,
close_deaf_port/1,
count_fds/1,
+ dropped_commands/1,
dying_port/1,
env/1,
eof/1,
@@ -548,6 +549,45 @@ make_dying_port(Config) when is_list(Config) ->
Command = lists:concat([PortTest, " -h0 -d -q"]),
open_port({spawn, Command}, [stream]).
+%% Test that dropped port_commands work correctly.
+%% This used to cause a segfault.
+%%
+%% This testcase creates a port and then lets many processes
+%% do parallel commands to it. After a while it closes the
+%% port and we are trying to catch the race when doing a
+%% command while the port is closing.
+dropped_commands(Config) ->
+ %% Test with output callback
+ dropped_commands(Config, false, {self(), {command, "1"}}),
+ %% Test with outputv callback
+ dropped_commands(Config, true, {self(), {command, "1"}}).
+
+dropped_commands(Config, Outputv, Cmd) ->
+ Path = proplists:get_value(data_dir, Config),
+ os:putenv("ECHO_DRV_USE_OUTPUTV", atom_to_list(Outputv)),
+ ok = load_driver(Path, "echo_drv"),
+ [dropped_commands_test(Cmd) || _ <- lists:seq(1, 100)],
+ timer:sleep(100),
+ erl_ddll:unload_driver("echo_drv"),
+ ok.
+
+dropped_commands_test(Cmd) ->
+ Port = erlang:open_port({spawn_driver, "echo_drv"}, [{parallelism, true}]),
+ spawn_monitor(
+ fun() ->
+ [spawn_link(fun() -> spin(Port, Cmd) end) || _ <- lists:seq(1,8)],
+ timer:sleep(5),
+ port_close(Port),
+ timer:sleep(5),
+ exit(nok)
+ end),
+ receive _M -> timer:sleep(5) end.
+
+spin(P, Cmd) ->
+ P ! Cmd,
+ spin(P, Cmd).
+
+
%% Tests that port program with complete path (but without any
%% .exe extension) can be started, even if there is a file with
%% the same name but without the extension in the same directory.
diff --git a/erts/emulator/test/port_SUITE_data/echo_drv.c b/erts/emulator/test/port_SUITE_data/echo_drv.c
index 1d39c6a00c..b4370f6455 100644
--- a/erts/emulator/test/port_SUITE_data/echo_drv.c
+++ b/erts/emulator/test/port_SUITE_data/echo_drv.c
@@ -18,8 +18,11 @@ typedef struct _erl_drv_data EchoDrvData;
static EchoDrvData *echo_drv_start(ErlDrvPort port, char *command);
static void echo_drv_stop(EchoDrvData *data_p);
-static void echo_drv_output(ErlDrvData drv_data, char *buf,
- ErlDrvSizeT len);
+static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len);
+static ErlDrvSSizeT echo_control(ErlDrvData drv_data,
+ unsigned int command, char *buf,
+ ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen);
+static void echo_outputv(ErlDrvData drv_data, ErlIOVec *ev);
static void echo_drv_finish(void);
static ErlDrvEntry echo_drv_entry = {
@@ -32,9 +35,9 @@ static ErlDrvEntry echo_drv_entry = {
"echo_drv",
echo_drv_finish,
NULL, /* handle */
- NULL, /* control */
+ echo_control, /* control */
NULL, /* timeout */
- NULL, /* outputv */
+ echo_outputv, /* outputv */
NULL, /* ready_async */
NULL,
NULL,
@@ -56,6 +59,14 @@ static ErlDrvEntry echo_drv_entry = {
DRIVER_INIT(echo_drv)
{
+ char buf[10];
+ size_t bufsz = sizeof(buf);
+ char *use_outputv;
+ use_outputv = (erl_drv_getenv("ECHO_DRV_USE_OUTPUTV", buf, &bufsz) == 0
+ ? buf
+ : "false");
+ if (strcmp(use_outputv, "true") != 0)
+ echo_drv_entry.outputv = NULL;
return &echo_drv_entry;
}
@@ -87,3 +98,15 @@ static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
static void echo_drv_finish() {
}
+
+static ErlDrvSSizeT echo_control(ErlDrvData drv_data,
+ unsigned int command, char *buf,
+ ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)
+{
+ return 0;
+}
+
+static void echo_outputv(ErlDrvData drv_data, ErlIOVec *ev)
+{
+ return;
+}
diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl
index bfc3c8cb51..c78dc754a9 100644
--- a/erts/emulator/test/port_trace_SUITE.erl
+++ b/erts/emulator/test/port_trace_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index b993bf5c69..6ded7ff1c9 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -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/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl
index 26eddfd03c..a12019ec83 100644
--- a/erts/emulator/test/receive_SUITE.erl
+++ b/erts/emulator/test/receive_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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/erts/emulator/test/register_SUITE.erl b/erts/emulator/test/register_SUITE.erl
index 43ae749498..49da94a775 100644
--- a/erts/emulator/test/register_SUITE.erl
+++ b/erts/emulator/test/register_SUITE.erl
@@ -44,14 +44,7 @@ all() ->
-define(OTP_8099_NAME, otp_8099_reg_proc).
otp_8099(Config) when is_list(Config) ->
- case catch erlang:system_info(lock_counting) of
- true -> {skipped,
- "Lock counting enabled. Current lock counting "
- "implementation cannot handle this many "
- "processes."};
- _ ->
- otp_8099_test(1000000)
- end.
+ otp_8099_test(1000000).
otp_8099_test(0) ->
ok;
diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl
index d788360812..f1d11d1814 100644
--- a/erts/emulator/test/signal_SUITE.erl
+++ b/erts/emulator/test/signal_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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/erts/emulator/test/system_profile_SUITE.erl b/erts/emulator/test/system_profile_SUITE.erl
index 3a8f862b05..9b678fcff9 100644
--- a/erts/emulator/test/system_profile_SUITE.erl
+++ b/erts/emulator/test/system_profile_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl
index a977eb41c4..fc11a04a31 100644
--- a/erts/emulator/test/timer_bif_SUITE.erl
+++ b/erts/emulator/test/timer_bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1998-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/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index 9ddc8628b6..72acd33033 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -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/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl
index ce9c60d9d9..f12c359874 100644
--- a/erts/emulator/test/trace_bif_SUITE.erl
+++ b/erts/emulator/test/trace_bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1998-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/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl
index 1cbe6201c3..253d5fed23 100644
--- a/erts/emulator/test/trace_local_SUITE.erl
+++ b/erts/emulator/test/trace_local_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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/erts/emulator/test/trace_meta_SUITE.erl b/erts/emulator/test/trace_meta_SUITE.erl
index ef58978749..f157a6c9eb 100644
--- a/erts/emulator/test/trace_meta_SUITE.erl
+++ b/erts/emulator/test/trace_meta_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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/erts/emulator/test/trace_nif_SUITE.erl b/erts/emulator/test/trace_nif_SUITE.erl
index 5c35a8a83f..f796b9d667 100644
--- a/erts/emulator/test/trace_nif_SUITE.erl
+++ b/erts/emulator/test/trace_nif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl
index fa6460073a..c85a77536e 100644
--- a/erts/emulator/test/trace_port_SUITE.erl
+++ b/erts/emulator/test/trace_port_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl
index c2fa3afd9b..ab7d047bc3 100644
--- a/erts/emulator/test/tracer_SUITE.erl
+++ b/erts/emulator/test/tracer_SUITE.erl
@@ -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/erts/emulator/test/unique_SUITE.erl b/erts/emulator/test/unique_SUITE.erl
index ab02531de3..cfc37bd44f 100644
--- a/erts/emulator/test/unique_SUITE.erl
+++ b/erts/emulator/test/unique_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2014-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/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 05cd48d434..0a30553f71 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2016. All Rights Reserved.
+# Copyright Ericsson AB 1998-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/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
index 583426460e..4bcbb288b3 100644
--- a/erts/etc/common/Makefile.in
+++ b/erts/etc/common/Makefile.in
@@ -57,7 +57,7 @@ ERTS_INCL = -I$(ERL_TOP)/erts/include \
CC = @CC@
WFLAGS = @WFLAGS@
CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSDIR) -I$(EMUDIR) \
- $(ERTS_INCL) -DOTP_SYSTEM_VERSION=\"$(SYSTEM_VSN)\"
+ -I$(COMSYSDIR) $(ERTS_INCL) -DOTP_SYSTEM_VERSION=\"$(SYSTEM_VSN)\"
LD = @LD@
LIBS = @LIBS@
LDFLAGS = @LDFLAGS@
@@ -77,6 +77,7 @@ endif
BINDIR = $(ERL_TOP)/bin/$(TARGET)
OBJDIR = $(ERL_TOP)/erts/obj$(TYPEMARKER)/$(TARGET)
EMUDIR = $(ERL_TOP)/erts/emulator/beam
+COMSYSDIR = $(ERL_TOP)/erts/emulator/sys/common
EMUOSDIR = $(ERL_TOP)/erts/emulator/@ERLANG_OSTYPE@
SYSDIR = $(ERL_TOP)/erts/emulator/sys/@ERLANG_OSTYPE@
DRVDIR = $(ERL_TOP)/erts/emulator/drivers/@ERLANG_OSTYPE@
diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c
index 7f0af77a4c..9cd5dd3fab 100644
--- a/erts/etc/common/escript.c
+++ b/erts/etc/common/escript.c
@@ -74,7 +74,6 @@ static void error(char* format, ...);
static void* emalloc(size_t size);
static void efree(void *p);
static char* strsave(char* string);
-static void push_words(char* src);
static int run_erlang(char* name, char** argv);
static char* get_default_emulator(char* progname);
#ifdef __WIN32__
@@ -583,26 +582,6 @@ main(int argc, char** argv)
return run_erlang(eargv[0], eargv);
}
-static void
-push_words(char* src)
-{
- char sbuf[PMAX];
- char* dst;
-
- dst = sbuf;
- while ((*dst++ = *src++) != '\0') {
- if (isspace((int)*src)) {
- *dst = '\0';
- PUSH(strsave(sbuf));
- dst = sbuf;
- do {
- src++;
- } while (isspace((int)*src));
- }
- }
- if (sbuf[0])
- PUSH(strsave(sbuf));
-}
#ifdef __WIN32__
wchar_t *make_commandline(char **argv)
{
diff --git a/erts/etc/unix/Makefile b/erts/etc/unix/Makefile
index 17de4d8878..83c64d35fd 100644
--- a/erts/etc/unix/Makefile
+++ b/erts/etc/unix/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2013-2016. All Rights Reserved.
+# Copyright Ericsson AB 2013-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/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 5095367fea..af6facb5f2 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam
index 9cb0ee6119..7ca25803be 100644
--- a/erts/preloaded/ebin/erl_tracer.beam
+++ b/erts/preloaded/ebin/erl_tracer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 2efc6816e8..58c17dc416 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam
index 203e08c512..b7c061d9a0 100644
--- a/erts/preloaded/ebin/erts_code_purger.beam
+++ b/erts/preloaded/ebin/erts_code_purger.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_dirty_process_code_checker.beam b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
index 39b6ae9c4a..6467b1c016 100644
--- a/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
+++ b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index e53ddea4c3..6691749dcb 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam
index 96d8be2d69..fbd3249d70 100644
--- a/erts/preloaded/ebin/erts_literal_area_collector.beam
+++ b/erts/preloaded/ebin/erts_literal_area_collector.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index d20e76ba9c..2acb1f1211 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam
index cc98612499..73a017d981 100644
--- a/erts/preloaded/ebin/otp_ring0.beam
+++ b/erts/preloaded/ebin/otp_ring0.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam
index 0e429a93d5..7e46b79671 100644
--- a/erts/preloaded/ebin/prim_eval.beam
+++ b/erts/preloaded/ebin/prim_eval.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index ed2a8767c1..32755d5c28 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index ef64e3fcbf..c03415c758 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam
index 9203fe632b..77d0d2edb0 100644
--- a/erts/preloaded/ebin/prim_zip.beam
+++ b/erts/preloaded/ebin/prim_zip.beam
Binary files differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index c29359aa34..a959ebaaf2 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile
index 047e42170a..20fea99016 100644
--- a/erts/start_scripts/Makefile
+++ b/erts/start_scripts/Makefile
@@ -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/erts/vsn.mk b/erts/vsn.mk
index 05f3b4364e..c4bef03e94 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 9.0
+VSN = 9.0.1
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index 499a7e40c3..26640acabc 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2016</year>
+ <year>2004</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,55 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 5.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add compile option <c>-compile(no_native)</c> in modules
+ with <c>on_load</c> directive which is not yet supported
+ by HiPE.</p>
+ <p>
+ Own Id: OTP-14316 Aux Id: PR-1390 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>The <c>error</c> tuple returned from the <c>encode</c>
+ and <c>decode</c> functions will now include the stack
+ backtrace to make it easier to understand what went
+ wrong.</p>
+ <p>
+ Own Id: OTP-13961</p>
+ </item>
+ <item>
+ <p>The deprecated module <c>asn1rt</c> has been removed.
+ The deprecated functions <c>asn1ct:encode/3</c> and
+ <c>asn1ct:decode/3</c> have been removed. The
+ undocumented function <c>asn1ct:encode/2</c> has been
+ removed.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14146</p>
+ </item>
+ <item>
+ <p>The new '<c>maps</c>' option changes the
+ representation of the types <c>SEQUENCE</c> and
+ <c>SET</c> to be maps (instead of records).</p>
+ <p>
+ Own Id: OTP-14219</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 4.0.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/asn1/doc/src/ref_man.xml b/lib/asn1/doc/src/ref_man.xml
index 8f380b788c..14f6818cae 100644
--- a/lib/asn1/doc/src/ref_man.xml
+++ b/lib/asn1/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index 838d59a512..806f8420ec 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -145,27 +145,37 @@ pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATE
pgen_n2nconversion(_Erules,_) ->
true.
-pgen_name2numfunc(_TypeName,[], _) ->
+pgen_name2numfunc(TypeNameAsAtom,Mapping,Ext) when is_atom(TypeNameAsAtom) ->
+ FuncName = list_to_atom("name2num_"++atom_to_list(TypeNameAsAtom)),
+ pgen_name2numfunc1(FuncName,Mapping,Ext).
+
+pgen_name2numfunc1(_FuncName,[], _) ->
true;
-pgen_name2numfunc(TypeName,[{Atom,Number}], extension_marker) ->
- emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
- emit(["name2num_",TypeName,"({asn1_enum, Num}) -> Num.",nl,nl]);
-pgen_name2numfunc(TypeName,[{Atom,Number}], _) ->
- emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,".",nl,nl]);
-pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest], EM) ->
- emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
- pgen_name2numfunc(TypeName,NNRest, EM).
-
-pgen_num2namefunc(_TypeName,[], _) ->
+pgen_name2numfunc1(FuncName,[{Atom,Number}], extension_marker) ->
+ emit([{asis,FuncName},"(",{asis,Atom},") ->",Number,";",nl]),
+ emit([{asis,FuncName},"({asn1_enum, Num}) -> Num.",nl,nl]);
+pgen_name2numfunc1(FuncName,[{Atom,Number}], _) ->
+ emit([{asis,FuncName},"(",{asis,Atom},") ->",Number,".",nl,nl]);
+pgen_name2numfunc1(FuncName,[{Atom,Number}|NNRest], EM) ->
+ emit([{asis,FuncName},"(",{asis,Atom},") ->",Number,";",nl]),
+ pgen_name2numfunc1(FuncName,NNRest, EM).
+
+pgen_num2namefunc(TypeNameAsAtom,Mapping,Ext) when is_atom(TypeNameAsAtom) ->
+ FuncName = list_to_atom("num2name_"++atom_to_list(TypeNameAsAtom)),
+ pgen_num2namefunc1(FuncName,Mapping,Ext).
+
+pgen_num2namefunc1(_FuncName,[], _) ->
true;
-pgen_num2namefunc(TypeName,[{Atom,Number}], extension_marker) ->
- emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
- emit(["num2name_",TypeName,"(ExtensionNum) -> {asn1_enum, ExtensionNum}.",nl,nl]);
-pgen_num2namefunc(TypeName,[{Atom,Number}], _) ->
- emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},".",nl,nl]);
-pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest], EM) ->
- emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
- pgen_num2namefunc(TypeName,NNRest, EM).
+pgen_num2namefunc1(FuncName,[{Atom,Number}], extension_marker) ->
+ emit([{asis,FuncName},"(",Number,") ->",{asis,Atom},";",nl]),
+ emit([{asis,FuncName},"(ExtensionNum) -> {asn1_enum, ExtensionNum}.",nl,nl]);
+pgen_num2namefunc1(FuncName,[{Atom,Number}], _) ->
+ emit([{asis,FuncName},"(",Number,") ->",{asis,Atom},".",nl,nl]);
+pgen_num2namefunc1(FuncName,[{Atom,Number}|NNRest], EM) ->
+ emit([{asis,FuncName},"(",Number,") ->",{asis,Atom},";",nl]),
+ pgen_num2namefunc1(FuncName,NNRest, EM).
+
+
pgen_objects(_,_,_,[]) ->
true;
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index 5fe6945ff2..f94b4278bf 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -1198,14 +1198,14 @@ testComment(Config) ->
testName2Number(Config) ->
N2NOptions0 = [{n2n,Type} ||
- Type <- ['CauseMisc', 'CauseProtocol',
- 'CauseRadioNetwork',
- 'CauseTransport','CauseNas']],
+ Type <- ['Cause-Misc', 'CauseProtocol']],
N2NOptions = [?NO_MAPS_MODULE|N2NOptions0],
- asn1_test_lib:compile("S1AP-IEs", Config, N2NOptions),
+ asn1_test_lib:compile("EnumN2N", Config, N2NOptions),
- 0 = 'S1AP-IEs':name2num_CauseMisc('control-processing-overload'),
- 'unknown-PLMN' = 'S1AP-IEs':num2name_CauseMisc(5),
+ 0 = 'EnumN2N':'name2num_Cause-Misc'('control-processing-overload'),
+ 'unknown-PLMN' = 'EnumN2N':'num2name_Cause-Misc'(5),
+ 4 = 'EnumN2N':name2num_CauseProtocol('semantic-error'),
+ 'transfer-syntax-error' = 'EnumN2N':num2name_CauseProtocol(0),
%% OTP-10144
%% Test that n2n option generates name2num and num2name functions supporting
diff --git a/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1 b/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1
index a724f2f3f5..a610eb6230 100644
--- a/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1
@@ -1,6 +1,28 @@
EnumN2N DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
+Cause-Misc ::= ENUMERATED {
+ control-processing-overload,
+ not-enough-user-plane-processing-resources,
+ hardware-failure,
+ om-intervention,
+ unspecified,
+ unknown-PLMN,
+...
+}
+
+CauseProtocol ::= ENUMERATED {
+ transfer-syntax-error,
+ abstract-syntax-error-reject,
+ abstract-syntax-error-ignore-and-notify,
+ message-not-compatible-with-receiver-state,
+ semantic-error,
+ abstract-syntax-error-falsely-constructed-message,
+ unspecified,
+ ...
+}
+
+
NoExt ::= ENUMERATED {
blue(0),
red(1),
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index e4bf3e2236..7329a9f879 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1 +1 @@
-ASN1_VSN = 4.0.4
+ASN1_VSN = 5.0
diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile
index e3e478ab7f..faa2d58a06 100644
--- a/lib/common_test/doc/src/Makefile
+++ b/lib/common_test/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2016. All Rights Reserved.
+# Copyright Ericsson AB 2003-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/lib/common_test/doc/src/basics_chapter.xml b/lib/common_test/doc/src/basics_chapter.xml
index 9be71ae5df..95599ca1f1 100644
--- a/lib/common_test/doc/src/basics_chapter.xml
+++ b/lib/common_test/doc/src/basics_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2016</year>
+ <year>2003</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/common_test/doc/src/ct_netconfc.xml b/lib/common_test/doc/src/ct_netconfc.xml
index ab83f50733..7ec8f23073 100644
--- a/lib/common_test/doc/src/ct_netconfc.xml
+++ b/lib/common_test/doc/src/ct_netconfc.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2010</year><year>2016</year>
+ <year>2010</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/common_test/doc/src/ct_ssh.xml b/lib/common_test/doc/src/ct_ssh.xml
index 63627d8840..0c7efed154 100644
--- a/lib/common_test/doc/src/ct_ssh.xml
+++ b/lib/common_test/doc/src/ct_ssh.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2010</year><year>2016</year>
+ <year>2010</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index a0079fd0c0..37a1846160 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,100 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.15.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ In OTP-20.0, the behavior of c, make, and ct_make was
+ changed so that in some cases the beam files by default
+ would be written to the directory where the source files
+ were found. This is now changed back to the old behavior
+ so beam files are by default written to current
+ directory.</p>
+ <p>
+ Own Id: OTP-14489 Aux Id: ERL-438 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.15</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Errors in the documentation for user HTML stylesheets
+ have been corrected.</p>
+ <p>
+ Own Id: OTP-14332 Aux Id: seq13299 </p>
+ </item>
+ <item>
+ <p>Internal code change: Calls to <c>catch</c> followed
+ by a call to <c>erlang:get_stacktrace/0</c> has been
+ rewritten to use <c>try</c> instead of <c>catch</c> to
+ make the code future-proof.</p>
+ <p>
+ Own Id: OTP-14400</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>The <c>ct_slave</c> modules now handle nodenames in
+ the same way as nodenames passed to <c>-sname</c>. That
+ means <c>ct_slave:start('[email protected]').</c> will now
+ work.</p>
+ <p>
+ Own Id: OTP-13806</p>
+ </item>
+ <item>
+ <p>
+ Added the new option, <c>keep_logs</c>. If setting the
+ value for this option to an integer, N, common_test will
+ remove all ct_run.* directories in the current log
+ directory, except the N newest.</p>
+ <p>
+ Own Id: OTP-14179</p>
+ </item>
+ <item>
+ <p>
+ The existing <c>ct_netconfc:open/1,2</c> opens an SSH
+ connection with one SSH channel realizing one Netconf
+ session. To allow testing of multiple sessions over the
+ same SSH connection, the following functions are added to
+ <c>ct_netconfc</c>:</p>
+ <p>
+ * <c>connect/1,2</c> - establish an SSH connection *
+ <c>disconnect/1</c> - close the given SSH connection *
+ <c>session/1,2,3</c> - open an ssh channel on the given
+ connection and send 'hello' to start a Netconf session</p>
+ <p>
+ Own Id: OTP-14284</p>
+ </item>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ <item>
+ <p>
+ The function ct_ssh:shell/2,3 is added.</p>
+ <p>
+ Own Id: OTP-14415 Aux Id: seq13315 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.14</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 662732d475..19b0ee20fe 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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.
@@ -917,13 +917,13 @@ comment(Comment) when is_list(Comment) ->
Formatted =
case (catch io_lib:format("~ts",[Comment])) of
{'EXIT',_} -> % it's a list not a string
- io_lib:format("~p",[Comment]);
+ io_lib:format("~tp",[Comment]);
String ->
String
end,
send_html_comment(lists:flatten(Formatted));
comment(Comment) ->
- Formatted = io_lib:format("~p",[Comment]),
+ Formatted = io_lib:format("~tp",[Comment]),
send_html_comment(lists:flatten(Formatted)).
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl
index 99de311570..d48ae830bb 100644
--- a/lib/common_test/src/ct_config.erl
+++ b/lib/common_test/src/ct_config.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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.
@@ -171,8 +171,8 @@ reload_config(KeyOrName) ->
process_default_configs(Opts) ->
lists:flatmap(fun({config,[_|_] = FileOrFiles}) ->
- case {io_lib:printable_list(FileOrFiles),
- io_lib:printable_list(hd(FileOrFiles))} of
+ case {io_lib:printable_unicode_list(FileOrFiles),
+ io_lib:printable_unicode_list(hd(FileOrFiles))} of
{false,true} ->
FileOrFiles;
{true,false} ->
diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl
index 6c1e46925f..cf0a228e1b 100644
--- a/lib/common_test/src/ct_conn_log_h.erl
+++ b/lib/common_test/src/ct_conn_log_h.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-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.
@@ -186,7 +186,7 @@ format_head(ConnMod,_,Time,Text) ->
io_lib:format("~n~ts",[Head]).
format_title(raw,#conn_log{client=Client}=Info) ->
- io_lib:format("Client ~w ~s ~ts",[Client,actionstr(Info),serverstr(Info)]);
+ io_lib:format("Client ~tw ~s ~ts",[Client,actionstr(Info),serverstr(Info)]);
format_title(_,Info) ->
Title = pad_char_end(?WIDTH,pretty_title(Info),$=),
io_lib:format("~n~ts", [Title]).
@@ -197,9 +197,9 @@ format_data(ConnMod,LogType,Data) ->
ConnMod:format_data(LogType,Data).
format_error(raw,Report) ->
- io_lib:format("~n~p~n",[Report]);
+ io_lib:format("~n~tp~n",[Report]);
format_error(pretty,Report) ->
- [io_lib:format("~n ~p: ~p",[K,V]) || {K,V} <- Report].
+ [io_lib:format("~n ~tp: ~tp",[K,V]) || {K,V} <- Report].
%%%-----------------------------------------------------------------
@@ -230,7 +230,7 @@ pretty_head({{{Y,Mo,D},{H,Mi,S}},MicroS},ConnMod,Text0) ->
micro2milli(MicroS)]).
pretty_title(#conn_log{client=Client}=Info) ->
- io_lib:format("= Client ~w ~s ~ts ",
+ io_lib:format("= Client ~tw ~s ~ts ",
[Client,actionstr(Info),serverstr(Info)]).
actionstr(#conn_log{action=send}) -> "----->";
@@ -245,11 +245,11 @@ actionstr(_) -> "<---->".
serverstr(#conn_log{name=undefined,address={undefined,_}}) ->
io_lib:format("server",[]);
serverstr(#conn_log{name=undefined,address=Address}) ->
- io_lib:format("~p",[Address]);
+ io_lib:format("~tp",[Address]);
serverstr(#conn_log{name=Alias,address={undefined,_}}) ->
- io_lib:format("~w",[Alias]);
+ io_lib:format("~tw",[Alias]);
serverstr(#conn_log{name=Alias,address=Address}) ->
- io_lib:format("~w(~p)",[Alias,Address]).
+ io_lib:format("~tw(~tp)",[Alias,Address]).
month(1) -> "Jan";
month(2) -> "Feb";
diff --git a/lib/common_test/src/ct_event.erl b/lib/common_test/src/ct_event.erl
index 5fa9f410bf..1a0ee4f3cd 100644
--- a/lib/common_test/src/ct_event.erl
+++ b/lib/common_test/src/ct_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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.
@@ -151,7 +151,7 @@ init(RecvPids) ->
%%--------------------------------------------------------------------
handle_event(Event,State=#state{receivers=RecvPids}) ->
print("~n=== ~w ===~n", [?MODULE]),
- print("~w: ~w~n", [Event#event.name,Event#event.data]),
+ print("~tw: ~tw~n", [Event#event.name,Event#event.data]),
lists:foreach(fun(Recv) -> report_event(Recv,Event) end, RecvPids),
{ok,State}.
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 141c7f5b0a..6066470233 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -312,7 +312,7 @@ add_defaults(Mod,Func, GroupPath) ->
end;
{'EXIT',Reason} ->
ErrStr = io_lib:format("~n*** ERROR *** "
- "~w:suite/0 failed: ~p~n",
+ "~w:suite/0 failed: ~tp~n",
[Suite,Reason]),
io:format(ErrStr, []),
io:format(?def_gl, ErrStr, []),
@@ -335,7 +335,7 @@ add_defaults(Mod,Func, GroupPath) ->
false ->
ErrStr = io_lib:format("~n*** ERROR *** "
"Invalid return value from "
- "~w:suite/0: ~p~n",
+ "~w:suite/0: ~tp~n",
[Suite,SuiteInfo]),
io:format(ErrStr, []),
io:format(?def_gl, ErrStr, []),
@@ -344,7 +344,7 @@ add_defaults(Mod,Func, GroupPath) ->
SuiteInfo ->
ErrStr = io_lib:format("~n*** ERROR *** "
"Invalid return value from "
- "~w:suite/0: ~p~n", [Suite,SuiteInfo]),
+ "~w:suite/0: ~tp~n", [Suite,SuiteInfo]),
io:format(ErrStr, []),
io:format(?def_gl, ErrStr, []),
{suite0_failed,bad_return_value}
@@ -371,7 +371,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo) ->
{value,{error,BadGr0Val,GrName}} ->
Gr0ErrStr = io_lib:format("~n*** ERROR *** "
"Invalid return value from "
- "~w:group(~w): ~p~n",
+ "~w:group(~tw): ~tp~n",
[Mod,GrName,BadGr0Val]),
io:format(Gr0ErrStr, []),
io:format(?def_gl, Gr0ErrStr, []),
@@ -393,7 +393,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo) ->
{error,BadTC0Val} ->
TC0ErrStr = io_lib:format("~n*** ERROR *** "
"Invalid return value from "
- "~w:~w/0: ~p~n",
+ "~w:~tw/0: ~tp~n",
[Mod,Func,BadTC0Val]),
io:format(TC0ErrStr, []),
io:format(?def_gl, TC0ErrStr, []),
@@ -921,7 +921,7 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
end,
ErrorStr = case ErrorSpec of
{badmatch,Descr} ->
- Descr1 = lists:flatten(io_lib:format("~P",[Descr,10])),
+ Descr1 = lists:flatten(io_lib:format("~tP",[Descr,10])),
if length(Descr1) > 50 ->
Descr2 = string:substr(Descr1,1,50),
io_lib:format("{badmatch,~ts...}",[Descr2]);
@@ -931,15 +931,15 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
{test_case_failed,Reason} ->
case (catch io_lib:format("{test_case_failed,~ts}", [Reason])) of
{'EXIT',_} ->
- io_lib:format("{test_case_failed,~p}", [Reason]);
+ io_lib:format("{test_case_failed,~tp}", [Reason]);
Result -> Result
end;
{'EXIT',_Reason} = EXIT ->
- io_lib:format("~P", [EXIT,5]);
+ io_lib:format("~tP", [EXIT,5]);
{Spec,_Reason} when is_atom(Spec) ->
- io_lib:format("~w", [Spec]);
+ io_lib:format("~tw", [Spec]);
Other ->
- io_lib:format("~P", [Other,5])
+ io_lib:format("~tP", [Other,5])
end,
ErrorHtml =
"<font color=\"brown\">" ++ ct_logs:escape_chars(ErrorStr) ++ "</font>",
@@ -996,16 +996,16 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
%% if a function specified by all/0 does not exist, we
%% pick up undef here
[{LastMod,LastFunc}|_] when ErrorStr == "undef" ->
- PrintError("~w:~w could not be executed~nReason: ~ts",
+ PrintError("~w:~tw could not be executed~nReason: ~ts",
[LastMod,LastFunc,ErrorStr]);
[{LastMod,LastFunc}|_] ->
- PrintError("~w:~w failed~nReason: ~ts", [LastMod,LastFunc,ErrorStr]);
+ PrintError("~w:~tw failed~nReason: ~ts", [LastMod,LastFunc,ErrorStr]);
[{LastMod,LastFunc,LastLine}|_] ->
%% print error to console, we are only
%% interested in the last executed expression
- PrintError("~w:~w failed on line ~w~nReason: ~ts",
+ PrintError("~w:~tw failed on line ~w~nReason: ~ts",
[LastMod,LastFunc,LastLine,ErrorStr]),
case ct_util:read_suite_data({seq,Mod,Func}) of
@@ -1178,7 +1178,7 @@ get_all(Mod, ConfTests) ->
case ct_util:get_testdata({error_in_suite,Mod}) of
undefined ->
ErrStr = io_lib:format("~n*** ERROR *** "
- "~w:all/0 failed: ~p~n",
+ "~w:all/0 failed: ~tp~n",
[Mod,ExitReason]),
io:format(?def_gl, ErrStr, []),
%% save the error info so it doesn't get printed twice
@@ -1294,8 +1294,8 @@ save_seq(Mod,Seq,SeqTCs,All) ->
check_private(Seq,TCs,All) ->
Bad = lists:filter(fun(TC) -> lists:member(TC,All) end, TCs),
if Bad /= [] ->
- Reason = io_lib:format("regular test cases not allowed in sequence ~p: "
- "~p",[Seq,Bad]),
+ Reason = io_lib:format("regular test cases not allowed in sequence ~tp: "
+ "~tp",[Seq,Bad]),
throw({error,list_to_atom(lists:flatten(Reason))});
true ->
ok
@@ -1312,7 +1312,7 @@ check_multiple(Mod,Seq,TCs) ->
end,TCs),
if Bad /= [] ->
Reason = io_lib:format("test cases found in multiple sequences: "
- "~p",[Bad]),
+ "~tp",[Bad]),
throw({error,list_to_atom(lists:flatten(Reason))});
true ->
ok
@@ -1340,15 +1340,15 @@ end_per_suite(_Config) ->
%% if the group config functions are missing in the suite,
%% use these instead
init_per_group(GroupName, Config) ->
- ct:comment(io_lib:format("start of ~p", [GroupName])),
- ct_logs:log("TEST INFO", "init_per_group/2 for ~w missing "
+ ct:comment(io_lib:format("start of ~tp", [GroupName])),
+ ct_logs:log("TEST INFO", "init_per_group/2 for ~tw missing "
"in suite, using default.",
[GroupName]),
Config.
end_per_group(GroupName, _) ->
- ct:comment(io_lib:format("end of ~p", [GroupName])),
- ct_logs:log("TEST INFO", "end_per_group/2 for ~w missing "
+ ct:comment(io_lib:format("end of ~tp", [GroupName])),
+ ct_logs:log("TEST INFO", "end_per_group/2 for ~tw missing "
"in suite, using default.",
[GroupName]),
ok.
diff --git a/lib/common_test/src/ct_ftp.erl b/lib/common_test/src/ct_ftp.erl
index 84e664b387..8effb06e7e 100644
--- a/lib/common_test/src/ct_ftp.erl
+++ b/lib/common_test/src/ct_ftp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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.
@@ -119,19 +119,19 @@ open(KeyOrName) ->
_ ->
case ct:get_config(KeyOrName) of
undefined ->
- log(heading(open,KeyOrName),"Failed: ~p\n",
+ log(heading(open,KeyOrName),"Failed: ~tp\n",
[{not_available,KeyOrName}]),
{error,{not_available,KeyOrName}};
_ ->
case ct:get_config({KeyOrName,username}) of
undefined ->
- log(heading(open,KeyOrName),"Failed: ~p\n",
+ log(heading(open,KeyOrName),"Failed: ~tp\n",
[{not_available,{KeyOrName,username}}]),
{error,{not_available,{KeyOrName,username}}};
Username ->
case ct:get_config({KeyOrName,password}) of
undefined ->
- log(heading(open,KeyOrName),"Failed: ~p\n",
+ log(heading(open,KeyOrName),"Failed: ~tp\n",
[{not_available,{KeyOrName,password}}]),
{error,{not_available,{KeyOrName,password}}};
Password ->
@@ -145,7 +145,7 @@ open(KeyOrName,Username,Password) ->
log(heading(open,KeyOrName),"",[]),
case ct:get_config({KeyOrName,ftp}) of
undefined ->
- log(heading(open,KeyOrName),"Failed: ~p\n",
+ log(heading(open,KeyOrName),"Failed: ~tp\n",
[{not_available,{KeyOrName,ftp}}]),
{error,{not_available,{KeyOrName,ftp}}};
Addr ->
@@ -284,7 +284,7 @@ init(KeyOrName,{IP,Port},{Username,Password}) ->
case ftp_connect(IP,Port,Username,Password) of
{ok,FtpPid} ->
log(heading(init,KeyOrName),
- "Opened ftp connection:\nIP: ~p\nUsername: ~p\nPassword: ~p\n",
+ "Opened ftp connection:\nIP: ~tp\nUsername: ~tp\nPassword: ~p\n",
[IP,Username,lists:duplicate(length(Password),$*)]),
{ok,FtpPid,#state{ftp_pid=FtpPid,target_name=KeyOrName}};
Error ->
@@ -308,28 +308,28 @@ ftp_connect(IP,Port,Username,Password) ->
%% @hidden
handle_msg({send,LocalFile,RemoteFile},State) ->
log(heading(send,State#state.target_name),
- "LocalFile: ~p\nRemoteFile: ~p\n",[LocalFile,RemoteFile]),
+ "LocalFile: ~tp\nRemoteFile: ~tp\n",[LocalFile,RemoteFile]),
Result = ftp:send(State#state.ftp_pid,LocalFile,RemoteFile),
{Result,State};
handle_msg({recv,RemoteFile,LocalFile},State) ->
log(heading(recv,State#state.target_name),
- "RemoteFile: ~p\nLocalFile: ~p\n",[RemoteFile,LocalFile]),
+ "RemoteFile: ~tp\nLocalFile: ~tp\n",[RemoteFile,LocalFile]),
Result = ftp:recv(State#state.ftp_pid,RemoteFile,LocalFile),
{Result,State};
handle_msg({cd,Dir},State) ->
- log(heading(cd,State#state.target_name),"Dir: ~p\n",[Dir]),
+ log(heading(cd,State#state.target_name),"Dir: ~tp\n",[Dir]),
Result = ftp:cd(State#state.ftp_pid,Dir),
{Result,State};
handle_msg({ls,Dir},State) ->
- log(heading(ls,State#state.target_name),"Dir: ~p\n",[Dir]),
+ log(heading(ls,State#state.target_name),"Dir: ~tp\n",[Dir]),
Result = ftp:ls(State#state.ftp_pid,Dir),
{Result,State};
handle_msg({type,Type},State) ->
- log(heading(type,State#state.target_name),"Type: ~p\n",[Type]),
+ log(heading(type,State#state.target_name),"Type: ~tp\n",[Type]),
Result = ftp:type(State#state.ftp_pid,Type),
{Result,State};
handle_msg({delete,File},State) ->
- log(heading(delete,State#state.target_name),"Delete file: ~p\n",[File]),
+ log(heading(delete,State#state.target_name),"Delete file: ~tp\n",[File]),
Result = ftp:delete(State#state.ftp_pid,File),
{Result,State}.
@@ -368,7 +368,7 @@ call(Pid,Msg) ->
heading(Function,Name) ->
- io_lib:format("ct_ftp:~w ~p",[Function,Name]).
+ io_lib:format("ct_ftp:~tw ~tp",[Function,Name]).
log(Heading,Str,Args) ->
ct_gen_conn:log(Heading,Str,Args).
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index 88b05cf955..badb7c52ae 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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.
@@ -247,7 +247,7 @@ do_start(Opts) ->
Error;
{'DOWN',MRef,process,_,Reason} ->
log("ct_gen_conn:start",
- "Connection process died: ~p\n",
+ "Connection process died: ~tp\n",
[Reason]),
{error,{connection_process_died,Reason}}
end.
@@ -327,7 +327,7 @@ loop(Opts) ->
case Opts#gen_opts.reconnect of
true ->
log("Connection down!\nOpening new!",
- "Reason: ~p\nAddress: ~p\n",
+ "Reason: ~tp\nAddress: ~tp\n",
[Reason,Opts#gen_opts.address]),
case reconnect(Opts) of
{ok, NewPid, NewState} ->
@@ -338,12 +338,12 @@ loop(Opts) ->
Error ->
ct_util:unregister_connection(self()),
log("Reconnect failed. Giving up!",
- "Reason: ~p\n",
+ "Reason: ~tp\n",
[Error])
end;
false ->
ct_util:unregister_connection(self()),
- log("Connection closed!","Reason: ~p\n",[Reason])
+ log("Connection closed!","Reason: ~tp\n",[Reason])
end;
{'EXIT',Pid,Reason} ->
case Opts#gen_opts.ct_util_server of
diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl
index 333151ee1b..2235365a0e 100644
--- a/lib/common_test/src/ct_groups.erl
+++ b/lib/common_test/src/ct_groups.erl
@@ -210,7 +210,7 @@ find(Mod, _GrNames, _TCs, [BadTerm | _Gs], Known, _Defs, _FindAll) ->
"group "++atom_to_list(lists:last(Known))++
" in "++atom_to_list(Mod)++":groups/0"
end,
- Term = io_lib:format("~p", [BadTerm]),
+ Term = io_lib:format("~tp", [BadTerm]),
E = "Bad term "++lists:flatten(Term)++" in "++Where,
throw({error,list_to_atom(E)});
@@ -447,7 +447,7 @@ make_conf(Mod, Name, Props, TestSpec) ->
{false,false} ->
ct_logs:log("TEST INFO", "init_per_group/2 and "
"end_per_group/2 missing for group "
- "~w in ~w, using default.",
+ "~tw in ~w, using default.",
[Name,Mod]),
{{ct_framework,init_per_group},
{ct_framework,end_per_group},
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index 8cdc6d8c75..f0592a40be 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -235,7 +235,7 @@ call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) ->
call(resort(NewRest,NewHooks,Meta), Config, Meta, NewHooks)
catch Error:Reason ->
Trace = erlang:get_stacktrace(),
- ct_logs:log("Suite Hook","Failed to start a CTH: ~p:~p",
+ ct_logs:log("Suite Hook","Failed to start a CTH: ~tp:~tp",
[Error,{Reason,Trace}]),
call([], {fail,"Failed to start CTH"
", see the CT Log for details"}, Meta, Hooks)
@@ -424,11 +424,11 @@ catch_apply(M,F,A) ->
erlang:apply(M,F,A)
catch _:Reason ->
Trace = erlang:get_stacktrace(),
- ct_logs:log("Suite Hook","Call to CTH failed: ~w:~p",
+ ct_logs:log("Suite Hook","Call to CTH failed: ~w:~tp",
[error,{Reason,Trace}]),
throw({error_in_cth_call,
lists:flatten(
- io_lib:format("~w:~w/~w CTH call failed",
+ io_lib:format("~w:~tw/~w CTH call failed",
[M,F,length(A)]))})
end.
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 978af0f149..ba7660fe6a 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -139,7 +139,7 @@ close(Info, StartDir) ->
LogCacheBin =
case make_last_run_index() of
{error, Reason} -> % log server not responding
- io:format("Warning! ct_logs not responding: ~p~n", [Reason]),
+ io:format("Warning! ct_logs not responding: ~tp~n", [Reason]),
undefined;
LCB ->
LCB
@@ -175,7 +175,7 @@ close(Info, StartDir) ->
ok ->
ok;
Error ->
- io:format("Warning! Cleanup failed: ~p~n", [Error])
+ io:format("Warning! Cleanup failed: ~tp~n", [Error])
end,
_ = make_all_suites_index(stop),
make_all_runs_index(stop),
@@ -425,7 +425,7 @@ add_external_logs(Logs) ->
%%% @doc Print a link to a given file stored in the priv_dir of the
%%% calling test suite.
add_link(Heading,File,Type) ->
- log(Heading,"<a href=\"~ts\" type=~p>~ts</a>\n",
+ log(Heading,"<a href=\"~ts\" type=~tp>~ts</a>\n",
[uri(filename:join("log_private",File)),Type,File]).
@@ -567,7 +567,7 @@ get_header("default") ->
[log_timestamp(?now)]);
get_header(Heading) ->
io_lib:format("\n-----------------------------"
- "-----------------------\n~s ~s\n",
+ "-----------------------\n~ts ~s\n",
[Heading,log_timestamp(?now)]).
@@ -704,8 +704,8 @@ logger(Parent, Mode, Verbosity) ->
case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of
{error,Src1,Dest1,Reason1} ->
io:format(?def_gl, "ERROR! "++
- "Priv file ~p could not be copied to ~p. "++
- "Reason: ~p~n",
+ "Priv file ~tp could not be copied to ~tp. "++
+ "Reason: ~tp~n",
[Src1,Dest1,Reason1]),
exit({priv_file_error,Dest1});
ok ->
@@ -713,8 +713,8 @@ logger(Parent, Mode, Verbosity) ->
{error,Src2,Dest2,Reason2} ->
io:format(?def_gl,
"ERROR! "++
- "Priv file ~p could not be copied to ~p. "
- ++"Reason: ~p~n",
+ "Priv file ~tp could not be copied to ~tp. "
+ ++"Reason: ~tp~n",
[Src2,Dest2,Reason2]),
exit({priv_file_error,Dest2});
ok ->
@@ -891,7 +891,7 @@ logger_loop(State) ->
logger_loop(State);
{set_stylesheet,TC,SSFile} ->
Fd = State#logger_state.ct_log_fd,
- io:format(Fd, "~p loading external style sheet: ~ts~n",
+ io:format(Fd, "~tp loading external style sheet: ~ts~n",
[TC,SSFile]),
logger_loop(State#logger_state{stylesheet = SSFile});
{clear_stylesheet,_} when State#logger_state.stylesheet == undefined ->
@@ -952,7 +952,7 @@ create_io_fun(FromPid, CtLogFd, EscChars) ->
[IoList,"\n",IoStr]
catch
_:_Reason ->
- io:format(CtLogFd, "Logging fails! Str: ~p, Args: ~p~n",
+ io:format(CtLogFd, "Logging fails! Str: ~tp, Args: ~tp~n",
[Str,Args]),
%% stop the testcase, we need to see the fault
exit(FromPid, {log_printout_error,Str,Args}),
@@ -1151,7 +1151,7 @@ open_ctlog(MiscIoName) ->
Dir = filename:dirname(Cwd),
Variables = ct_run:variables_file_name(Dir),
io:format(Fd,
- "Can not read the file \'~ts\' Reason: ~w\n"
+ "Can not read the file \'~ts\' Reason: ~tw\n"
"No configuration found for test!!\n",
[Variables,Reason])
end,
@@ -1213,7 +1213,7 @@ print_style(Fd, IoFormat, StyleSheet) ->
end.
print_style_error(Fd, IoFormat, StyleSheet, Reason) ->
- IO = io_lib:format("\n<!-- Failed to load stylesheet ~ts: ~p -->\n",
+ IO = io_lib:format("\n<!-- Failed to load stylesheet ~ts: ~tp -->\n",
[StyleSheet,Reason]),
IoFormat(Fd, IO, []),
print_style(Fd, IoFormat, undefined).
@@ -1256,11 +1256,11 @@ make_last_run_index(StartTime) ->
case catch make_last_run_index1(StartTime,IndexName) of
{'EXIT', Reason} ->
io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"),
- io:format("~p~n", [Reason]),
+ io:format("~tp~n", [Reason]),
{error, Reason};
{error, Reason} ->
io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"),
- io:format("~p~n", [Reason]),
+ io:format("~tp~n", [Reason]),
{error, Reason};
ok ->
ok;
@@ -1561,7 +1561,7 @@ get_missing_suites(_,_) ->
[].
term_to_text(Term) ->
- lists:flatten(io_lib:format("~p.\n", [Term])).
+ lists:flatten(io_lib:format("~tp.\n", [Term])).
%%% Headers and footers.
@@ -1829,7 +1829,7 @@ count_cases(Dir) ->
Summary
end;
{error, Reason} ->
- io:format("\nFailed to read ~p: ~p (skipped)\n",
+ io:format("\nFailed to read ~tp: ~tp (skipped)\n",
[LogFile,Reason]),
error
end
@@ -1911,10 +1911,10 @@ config_table_header() ->
config_table1([{Key,Value}|Vars]) ->
[xhtml(["<tr><td>", atom_to_list(Key), "</td>\n",
- "<td><pre>",io_lib:format("~p",[Value]),"</pre></td></tr>\n"],
+ "<td><pre>",io_lib:format("~tp",[Value]),"</pre></td></tr>\n"],
["<tr class=\"", odd_or_even(), "\">\n",
"<td>", atom_to_list(Key), "</td>\n",
- "<td>", io_lib:format("~p",[Value]), "</td>\n</tr>\n"]) |
+ "<td>", io_lib:format("~tp",[Value]), "</td>\n</tr>\n"]) |
config_table1(Vars)];
config_table1([]) ->
[xhtml("","</tbody>\n"),"</table>\n"].
@@ -2474,17 +2474,17 @@ make_all_suites_index(NewTestData = {_TestName,DirName}) ->
LogDirData) of
{'EXIT',Reason} ->
io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"),
- io:format("~p~n", [Reason]),
+ io:format("~tp~n", [Reason]),
{error,Reason};
{error,Reason} ->
io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"),
- io:format("~p~n", [Reason]),
+ io:format("~tp~n", [Reason]),
{error,Reason};
ok ->
ok;
Err ->
io:format("Unknown internal error while updating ~ts. "
- "Please report.\n(Err: ~p, ID: 1)",
+ "Please report.\n(Err: ~tp, ID: 1)",
[AbsIndexName,Err]),
{error, Err}
end,
@@ -2703,11 +2703,11 @@ make_all_suites_index1(When, AbsIndexName, AllTestLogDirs) ->
case catch make_all_suites_index2(IndexName, AllTestLogDirs) of
{'EXIT', Reason} ->
io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"),
- io:format("~p~n", [Reason]),
+ io:format("~tp~n", [Reason]),
{error, Reason};
{error, Reason} ->
io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"),
- io:format("~p~n", [Reason]),
+ io:format("~tp~n", [Reason]),
{error, Reason};
{ok,TempData} ->
case When of
@@ -2721,7 +2721,7 @@ make_all_suites_index1(When, AbsIndexName, AllTestLogDirs) ->
end;
Err ->
io:format("Unknown internal error while updating ~ts. "
- "Please report.\n(Err: ~p, ID: 1)",
+ "Please report.\n(Err: ~tp, ID: 1)",
[AbsIndexName,Err]),
{error, Err}
end.
@@ -3200,7 +3200,7 @@ get_ts_html_wrapper(TestName, Logdir, PrintLabel, Cwd, TableCols, Encoding) ->
TestName1 = if is_list(TestName) ->
lists:flatten(TestName);
true ->
- lists:flatten(io_lib:format("~p", [TestName]))
+ lists:flatten(io_lib:format("~tp", [TestName]))
end,
Basic = basic_html(),
LabelStr =
diff --git a/lib/common_test/src/ct_make.erl b/lib/common_test/src/ct_make.erl
index f22959d457..220cb0473d 100644
--- a/lib/common_test/src/ct_make.erl
+++ b/lib/common_test/src/ct_make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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.
@@ -96,7 +96,7 @@ read_emakefile(Emakefile,Opts) ->
Mods = [filename:rootname(F) || F <- filelib:wildcard("*.erl")],
[{Mods, Opts}];
{error,Other} ->
- io:format("make: Trouble reading 'Emakefile':~n~p~n",[Other]),
+ io:format("make: Trouble reading 'Emakefile':~n~tp~n",[Other]),
error
end.
@@ -151,7 +151,7 @@ get_opts_from_emakefile(Mods,Emakefile,Opts) ->
{error,enoent} ->
[{Mods, Opts}];
{error,Other} ->
- io:format("make: Trouble reading 'Emakefile':~n~p~n",[Other]),
+ io:format("make: Trouble reading 'Emakefile':~n~tp~n",[Other]),
error
end.
@@ -280,15 +280,47 @@ recompile(File, NoExec, Load, Opts) ->
do_recompile(_File, true, _Load, _Opts) ->
out_of_date;
-do_recompile(File, false, noload, Opts) ->
+do_recompile(File, false, Load, Opts) ->
io:format("Recompile: ~ts\n",[File]),
- compile:file(File, [report_errors, report_warnings, error_summary |Opts]);
-do_recompile(File, false, load, Opts) ->
- io:format("Recompile: ~ts\n",[File]),
- c:c(File, Opts);
-do_recompile(File, false, netload, Opts) ->
- io:format("Recompile: ~ts\n",[File]),
- c:nc(File, Opts).
+ case compile:file(File, [report_errors, report_warnings |Opts]) of
+ Ok when is_tuple(Ok), element(1,Ok)==ok ->
+ maybe_load(element(2,Ok), Load, Opts);
+ _Error ->
+ error
+ end.
+
+maybe_load(_Mod, noload, _Opts) ->
+ ok;
+maybe_load(Mod, Load, Opts) ->
+ %% We have compiled File with options Opts. Find out where the
+ %% output file went to, and load it.
+ case compile:output_generated(Opts) of
+ true ->
+ Dir = proplists:get_value(outdir,Opts,"."),
+ do_load(Dir, Mod, Load);
+ false ->
+ io:format("** Warning: No object file created - nothing loaded **~n"),
+ ok
+ end.
+
+do_load(Dir, Mod, load) ->
+ code:purge(Mod),
+ case code:load_abs(filename:join(Dir, Mod),Mod) of
+ {module,Mod} ->
+ {ok,Mod};
+ Other ->
+ Other
+ end;
+do_load(Dir, Mod, netload) ->
+ Obj = atom_to_list(Mod) ++ code:objfile_extension(),
+ Fname = filename:join(Dir, Obj),
+ case file:read_file(Fname) of
+ {ok,Bin} ->
+ rpc:eval_everywhere(code,load_binary,[Mod,Fname,Bin]),
+ {ok,Mod};
+ Other ->
+ Other
+ end.
exists(File) ->
case file:read_file_info(File) of
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 4eef27d2a5..6e6d1879c2 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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.
@@ -434,7 +434,7 @@ init_master1(Parent,NodeOptsList,InitOptions,LogDirs) ->
init_master2(Parent,NodeOptsList,LogDirs) ->
process_flag(trap_exit,true),
Cookie = erlang:get_cookie(),
- log(all,"Cookie","~w",[Cookie]),
+ log(all,"Cookie","~tw",[Cookie]),
log(all,"Starting Tests",
"Tests starting on: ~p",[[N || {N,_} <- NodeOptsList]]),
SpawnAndMon =
@@ -454,7 +454,7 @@ master_loop(#state{node_ctrl_pids=[],
results=Finished}) ->
Str =
lists:map(fun({Node,Result}) ->
- io_lib:format("~-40.40.*ts~p\n",
+ io_lib:format("~-40.40.*ts~tp\n",
[$_,atom_to_list(Node),Result])
end,lists:reverse(Finished)),
log(all,"TEST RESULTS",Str,[]),
@@ -488,7 +488,7 @@ master_loop(State=#state{node_ctrl_pids=NodeCtrlPids,
Bad
end,
log(all,"Test Info",
- "Test on node ~w failed! Reason: ~p",
+ "Test on node ~w failed! Reason: ~tp",
[Node,Error]),
{Locks1,Blocked1} =
update_queue(exit,Node,Locks,Blocked),
@@ -501,7 +501,7 @@ master_loop(State=#state{node_ctrl_pids=NodeCtrlPids,
undefined ->
%% ignore (but report) exit from master_logger etc
log(all,"Test Info",
- "Warning! Process ~w has terminated. Reason: ~p",
+ "Warning! Process ~w has terminated. Reason: ~tp",
[Pid,Reason]),
master_loop(State)
end;
@@ -584,7 +584,7 @@ update_queue(take,Node,From,Lock={Op,Resource},Locks,Blocked) ->
%% Blocked: [{{Operation,Resource},Node,WaitingPid},...]
case lists:keysearch(Lock,1,Locks) of
{value,{_Lock,Owner}} -> % other node has lock
- log(html,"Lock Info","Node ~w blocked on ~w by ~w. Resource: ~p",
+ log(html,"Lock Info","Node ~w blocked on ~w by ~w. Resource: ~tp",
[Node,Op,Owner,Resource]),
Blocked1 = Blocked ++ [{Lock,Node,From}],
{Locks,Blocked1};
@@ -599,7 +599,7 @@ update_queue(release,Node,_From,Lock={Op,Resource},Locks,Blocked) ->
case lists:keysearch(Lock,1,Blocked) of
{value,E={Lock,SomeNode,WaitingPid}} ->
Blocked1 = lists:delete(E,Blocked),
- log(html,"Lock Info","Node ~w proceeds with ~w. Resource: ~p",
+ log(html,"Lock Info","Node ~w proceeds with ~w. Resource: ~tp",
[SomeNode,Op,Resource]),
reply(ok,WaitingPid), % waiting process may start
{Locks1,Blocked1};
@@ -678,7 +678,7 @@ refresh_logs([D|Dirs],Refreshed) ->
refresh_logs([],Refreshed) ->
Str =
lists:map(fun({D,Result}) ->
- io_lib:format("Refreshing logs in ~p... ~p",
+ io_lib:format("Refreshing logs in ~tp... ~tp",
[D,Result])
end,Refreshed),
log(all,"Info",Str,[]).
@@ -712,7 +712,7 @@ init_node_ctrl(MasterPid,Cookie,Opts) ->
{ok, _} = start_ct_event(),
ct_event:add_handler([{master,MasterPid}]),
- %% log("Running test with options: ~p~n", [Opts]),
+ %% log("Running test with options: ~tp~n", [Opts]),
Result = case (catch ct:run_test(Opts)) of
ok -> finished_ok;
Other -> Other
@@ -828,7 +828,7 @@ start_nodes(InitOptions)->
"with callback ~w~n", [NodeName,Callback]);
{error, Reason, _NodeName} ->
io:format("Failed to start node ~w with callback ~w! "
- "Reason: ~p~n", [NodeName, Callback, Reason])
+ "Reason: ~tp~n", [NodeName, Callback, Reason])
end;
{true, true}->
io:format("WARNING: Node ~w is alive but has node_start "
@@ -857,10 +857,10 @@ eval_on_nodes(InitOptions)->
evaluate(Node, [{M,F,A}|MFAs])->
case rpc:call(Node, M, F, A) of
{badrpc,Reason}->
- io:format("WARNING: Failed to call ~w:~w/~w on node ~w "
- "due to ~p~n", [M,F,length(A),Node,Reason]);
+ io:format("WARNING: Failed to call ~w:~tw/~w on node ~w "
+ "due to ~tp~n", [M,F,length(A),Node,Reason]);
Result->
- io:format("Called ~w:~w/~w on node ~w, result: ~p~n",
+ io:format("Called ~w:~tw/~w on node ~w, result: ~tp~n",
[M,F,length(A),Node,Result])
end,
evaluate(Node, MFAs);
diff --git a/lib/common_test/src/ct_master_event.erl b/lib/common_test/src/ct_master_event.erl
index d28ef42c20..d535d1274e 100644
--- a/lib/common_test/src/ct_master_event.erl
+++ b/lib/common_test/src/ct_master_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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.
@@ -71,7 +71,7 @@ stop() ->
{error,Reason} ->
ct_master_logs:log("Error",
"No response from CT Master Event.\n"
- "Reason = ~p\n"
+ "Reason = ~tp\n"
"Terminating now!\n",[Reason]),
%% communication with event manager fails, kill it
catch exit(whereis(?CT_MEVMGR_REF), kill);
@@ -135,7 +135,7 @@ handle_event(#event{name=start_logging,node=Node,data=RunDir},State) ->
handle_event(#event{name=Name,node=Node,data=Data},State) ->
print("~n=== ~w ===~n", [?MODULE]),
- print("~w on ~w: ~p~n", [Name,Node,Data]),
+ print("~tw on ~w: ~tp~n", [Name,Node,Data]),
{ok,State}.
%%--------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl
index 52003f752d..d8ecd641ed 100644
--- a/lib/common_test/src/ct_master_logs.erl
+++ b/lib/common_test/src/ct_master_logs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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.
@@ -110,16 +110,16 @@ init(Parent,LogDir,Nodes) ->
case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of
{error,Src1,Dest1,Reason1} ->
io:format(user, "ERROR! "++
- "Priv file ~p could not be copied to ~p. "++
- "Reason: ~p~n",
+ "Priv file ~tp could not be copied to ~tp. "++
+ "Reason: ~tp~n",
[Src1,Dest1,Reason1]),
exit({priv_file_error,Dest1});
ok ->
case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of
{error,Src2,Dest2,Reason2} ->
io:format(user, "ERROR! "++
- "Priv file ~p could not be copied to ~p. "++
- "Reason: ~p~n",
+ "Priv file ~tp could not be copied to ~tp. "++
+ "Reason: ~tp~n",
[Src2,Dest2,Reason2]),
exit({priv_file_error,Dest2});
ok ->
@@ -170,7 +170,7 @@ loop(State) ->
case catch io:format(Fd,Str++"\n",Args) of
{'EXIT',Reason} ->
io:format(Fd,
- "Logging fails! Str: ~p, Args: ~p~n",
+ "Logging fails! Str: ~tp, Args: ~tp~n",
[Str,Args]),
exit({logging_failed,Reason}),
ok;
diff --git a/lib/common_test/src/ct_master_status.erl b/lib/common_test/src/ct_master_status.erl
index 7d3e54e645..b27fdd341e 100644
--- a/lib/common_test/src/ct_master_status.erl
+++ b/lib/common_test/src/ct_master_status.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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.
@@ -71,7 +71,7 @@ init(_) ->
%%
handle_event(#event{name=Name,node=Node,data=Data},State) ->
print("~n=== ~w ===~n", [?MODULE]),
- print("~w on ~w: ~p~n", [Name,Node,Data]),
+ print("~tw on ~w: ~tp~n", [Name,Node,Data]),
{ok,State}.
%%--------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
index b77570c121..2c4b97df20 100644
--- a/lib/common_test/src/ct_netconfc.erl
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -1,7 +1,7 @@
%%----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-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/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl
index 12c3d726d3..94ccb59af9 100644
--- a/lib/common_test/src/ct_property_test.erl
+++ b/lib/common_test/src/ct_property_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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.
@@ -134,7 +134,7 @@ mk_ct_return(Other, Tool) ->
try lists:last(hd(Tool:counterexample()))
of
{set,{var,_},{call,M,F,Args}} ->
- {fail, io_lib:format("~p:~p/~p returned bad result",[M,F,length(Args)])}
+ {fail, io_lib:format("~p:~tp/~p returned bad result",[M,F,length(Args)])}
catch
_:_ ->
{fail, Other}
@@ -174,7 +174,7 @@ compile_tests(Path, ToolModule) ->
BeamFiles = [F || F<-FileNames,
filename:extension(F) == ".beam"],
_ = [file:delete(F) || F<-BeamFiles],
- ct:pal("Compiling in ~p:~n Deleted ~p~n MacroDefs=~p",[Path,BeamFiles,MacroDefs]),
+ ct:pal("Compiling in ~tp:~n Deleted ~p~n MacroDefs=~p",[Path,BeamFiles,MacroDefs]),
Result = make:all([load|MacroDefs]),
ok = file:set_cwd(Cwd),
Result.
diff --git a/lib/common_test/src/ct_release_test.erl b/lib/common_test/src/ct_release_test.erl
index 6e3cad0a50..551a7e06d7 100644
--- a/lib/common_test/src/ct_release_test.erl
+++ b/lib/common_test/src/ct_release_test.erl
@@ -445,7 +445,7 @@ init_upgrade_test(Level) ->
end,
case OldRel of
false ->
- ct:log("Release ~p is not available."
+ ct:log("Release ~tp is not available."
" Upgrade on '~p' level can not be tested.",
[FromVsn,Level]),
undefined;
@@ -522,8 +522,8 @@ upgrade(Apps,Level,Callback,CreateDir,InstallDir,Config) ->
{ToVsn,ToRel,ToAppsVsns} =
upgrade_system(Apps, FromRel, CreateDir,
InstallDir, Data),
- ct:log("Upgrade from: OTP-~ts, ~p",[FromVsn, FromAppsVsns]),
- ct:log("Upgrade to: OTP-~ts, ~p",[ToVsn, ToAppsVsns]),
+ ct:log("Upgrade from: OTP-~ts, ~tp",[FromVsn, FromAppsVsns]),
+ ct:log("Upgrade to: OTP-~ts, ~tp",[ToVsn, ToAppsVsns]),
do_upgrade(Callback, FromVsn, FromAppsVsns, ToRel,
ToAppsVsns, InstallDir)
end
@@ -727,9 +727,9 @@ do_upgrade({Cb,InitState},FromVsn,FromAppsVsns,ToRel,ToAppsVsns,InstallDir) ->
do_callback(Node,Mod,Func,Args) ->
Dir = filename:dirname(code:which(Mod)),
_ = rpc:call(Node,code,add_path,[Dir]),
- ct:log("Calling ~p:~p/1",[Mod,Func]),
+ ct:log("Calling ~p:~tp/1",[Mod,Func]),
R = rpc:call(Node,Mod,Func,Args),
- ct:log("~p:~p/~w returned: ~p",[Mod,Func,length(Args),R]),
+ ct:log("~p:~tp/~w returned: ~tp",[Mod,Func,length(Args),R]),
case R of
{badrpc,Error} ->
throw({fail,{test_upgrade_callback,Mod,Func,Args,Error}});
@@ -860,10 +860,7 @@ copy_file(Src, Dest, Opts) ->
end.
write_file(FName, Conts) ->
- Enc = file:native_name_encoding(),
- {ok, Fd} = file:open(FName, [write]),
- ok = file:write(Fd, unicode:characters_to_binary(Conts,Enc,Enc)),
- ok = file:close(Fd).
+ file:write_file(FName, unicode:characters_to_binary(Conts)).
%% Substitute all occurrences of %Var% for Val in the given scripts
subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) ->
@@ -879,7 +876,7 @@ subst_src_script(Script, SrcDir, DestDir, Vars, Opts) ->
subst_file(Src, Dest, Vars, Opts) ->
{ok, Bin} = file:read_file(Src),
- Conts = binary_to_list(Bin),
+ Conts = unicode:characters_to_list(Bin),
NConts = subst(Conts, Vars),
write_file(Dest, NConts),
case lists:member(preserve, Opts) of
@@ -891,7 +888,7 @@ subst_file(Src, Dest, Vars, Opts) ->
end.
subst(Str, [{Var,Val}|Vars]) ->
- subst(re:replace(Str,"%"++Var++"%",Val,[{return,list}]),Vars);
+ subst(re:replace(Str,"%"++Var++"%",Val,[{return,list},unicode]),Vars);
subst(Str, []) ->
Str.
diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl
index dac596a135..c043c9846c 100644
--- a/lib/common_test/src/ct_repeat.erl
+++ b/lib/common_test/src/ct_repeat.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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.
@@ -43,7 +43,7 @@ loop_test(If,Args) when is_list(Args) ->
no_loop ->
false;
E = {error,_} ->
- io:format("Common Test error: ~p\n\n",[E]),
+ io:format("Common Test error: ~tp\n\n",[E]),
ok = file:set_cwd(Cwd),
E;
{repeat,N} ->
@@ -89,18 +89,18 @@ loop(If,Type,N,Data0,Data1,Args,TPid,AccResult) ->
{'EXIT',Pid,Reason} ->
case Reason of
{user_error,What} ->
- io:format("\nTest run failed!\nReason: ~p\n\n\n", [What]),
+ io:format("\nTest run failed!\nReason: ~tp\n\n\n", [What]),
cancel(TPid),
{error,What};
_ ->
io:format("Test run crashed! This could be an internal error "
"- please report!\n\n"
- "~p\n\n\n",[Reason]),
+ "~tp\n\n\n",[Reason]),
cancel(TPid),
{error,Reason}
end;
{Pid,{error,Reason}} ->
- io:format("\nTest run failed!\nReason: ~p\n\n\n",[Reason]),
+ io:format("\nTest run failed!\nReason: ~tp\n\n\n",[Reason]),
cancel(TPid),
{error,Reason};
{Pid,Result} ->
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index ce30babc0d..14f28f9ca3 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -121,13 +121,13 @@ script_start() ->
%% used for purpose of testing the run_test interface
io:format(user, "~n-------------------- START ARGS "
"--------------------~n", []),
- io:format(user, "--- Init args:~n~p~n", [FlagFilter(Init)]),
- io:format(user, "--- CT args:~n~p~n", [FlagFilter(CtArgs)]),
+ io:format(user, "--- Init args:~n~tp~n", [FlagFilter(Init)]),
+ io:format(user, "--- CT args:~n~tp~n", [FlagFilter(CtArgs)]),
EnvArgs = opts2args(EnvStartOpts),
- io:format(user, "--- Env opts -> args:~n~p~n =>~n~p~n",
+ io:format(user, "--- Env opts -> args:~n~tp~n =>~n~tp~n",
[EnvStartOpts,EnvArgs]),
Merged = merge_arguments(CtArgs ++ EnvArgs),
- io:format(user, "--- Merged args:~n~p~n", [FlagFilter(Merged)]),
+ io:format(user, "--- Merged args:~n~tp~n", [FlagFilter(Merged)]),
io:format(user, "-----------------------------------"
"-----------------~n~n", []),
Merged;
@@ -160,18 +160,18 @@ script_start(Args) ->
{'EXIT',Pid,Reason} ->
case Reason of
{user_error,What} ->
- io:format("\nTest run failed!\nReason: ~p\n\n\n",
+ io:format("\nTest run failed!\nReason: ~tp\n\n\n",
[What]),
finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args);
_ ->
io:format("Test run crashed! "
"This could be an internal error "
"- please report!\n\n"
- "~p\n\n\n", [Reason]),
+ "~tp\n\n\n", [Reason]),
finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args)
end;
{Pid,{error,Reason}} ->
- io:format("\nTest run failed! Reason:\n~p\n\n\n",[Reason]),
+ io:format("\nTest run failed! Reason:\n~tp\n\n\n",[Reason]),
finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args);
{Pid,Result} ->
io:nl(),
@@ -219,7 +219,7 @@ analyze_test_result([], _) ->
analyze_test_result(interactive_mode, _) ->
interactive_mode;
analyze_test_result(Unknown, _) ->
- io:format("\nTest run failed! Reason:\n~p\n\n\n",[Unknown]),
+ io:format("\nTest run failed! Reason:\n~tp\n\n\n",[Unknown]),
?EXIT_STATUS_TEST_RUN_FAILED.
finish(Tracing, ExitStatus, Args) ->
@@ -760,7 +760,7 @@ script_start4(#opts{label = Label, profile = Profile,
if Config == [] ->
ok;
true ->
- io:format("\nInstalling: ~p\n\n", [Config])
+ io:format("\nInstalling: ~tp\n\n", [Config])
end,
case install([{config,Config},{event_handler,EvHandlers},
{ct_hooks, CTHooks},
@@ -909,9 +909,9 @@ install(Opts, LogDir) ->
case whereis(ct_util_server) of
undefined ->
VarFile = variables_file_name(LogDir),
- case file:open(VarFile, [write]) of
+ case file:open(VarFile, [write, {encoding,utf8}]) of
{ok,Fd} ->
- _ = [io:format(Fd, "~p.\n", [Opt]) || Opt <- ConfOpts],
+ _ = [io:format(Fd, "~tp.\n", [Opt]) || Opt <- ConfOpts],
ok = file:close(Fd);
{error,Reason} ->
io:format("CT failed to install configuration data. Please "
@@ -1828,10 +1828,10 @@ compile_and_run(Tests, Skip, Opts, Args) ->
case lists:member(all, Conns) of
true ->
Conns1 = ct_util:override_silence_all_connections(),
- ct_logs:log("Silent connections", "~p", [Conns1]);
+ ct_logs:log("Silent connections", "~tp", [Conns1]);
false ->
ct_util:override_silence_connections(Conns),
- ct_logs:log("Silent connections", "~p", [Conns])
+ ct_logs:log("Silent connections", "~tp", [Conns])
end
end,
log_ts_names(Opts#opts.testspec_files),
@@ -1924,7 +1924,7 @@ possibly_spawn(true, Tests, Skip, Opts) ->
TestRunPid = spawn_link(TestRun),
receive
{'EXIT',TestRunPid,{ok,TestResult}} ->
- io:format(user, "~nCommon Test returned ~p~n~n",
+ io:format(user, "~nCommon Test returned ~tp~n~n",
[TestResult]);
{'EXIT',TestRunPid,Error} ->
exit(Error)
@@ -1943,7 +1943,7 @@ auto_compile(TestSuites) ->
case application:get_env(common_test, include) of
{ok,UserInclDirs} when length(UserInclDirs) > 0 ->
io:format("Including the following directories:~n"),
- [begin io:format("~p~n",[UserInclDir]), {i,UserInclDir} end ||
+ [begin io:format("~tp~n",[UserInclDir]), {i,UserInclDir} end ||
UserInclDir <- UserInclDirs];
_ ->
[]
@@ -2284,7 +2284,7 @@ do_run_test(Tests, Skip, Opts0) ->
NoOfSuites = length(Suites1),
ct_util:warn_duplicates(Suites1),
{ok,Cwd} = file:get_cwd(),
- io:format("~nCWD set to: ~p~n", [Cwd]),
+ io:format("~nCWD set to: ~tp~n", [Cwd]),
if NoOfCases == unknown ->
io:format("~nTEST INFO: ~w test(s), ~w suite(s)~n~n",
[NoOfTests,NoOfSuites]),
@@ -2354,7 +2354,7 @@ do_run_test(Tests, Skip, Opts0) ->
case ct_util:get_testdata(severe_error) of
undefined -> ok;
SevereError ->
- ct_logs:log("SEVERE ERROR", "~p\n", [SevereError]),
+ ct_logs:log("SEVERE ERROR", "~tp\n", [SevereError]),
exit(SevereError)
end,
@@ -2425,7 +2425,7 @@ start_cover(Opts=#opts{coverspec=CovData,cover_stop=CovStop},LogDir) ->
if (CovNodes /= []) and (CovNodes /= undefined) ->
ct_logs:log("COVER INFO",
"Nodes included in cover "
- "session: ~w",
+ "session: ~tw",
[CovNodes]),
cover:start(CovNodes);
true ->
@@ -2439,7 +2439,7 @@ start_cover(Opts=#opts{coverspec=CovData,cover_stop=CovStop},LogDir) ->
{error,Reason} ->
ct_logs:log("COVER INFO",
"Importing cover data from: ~ts fails! "
- "Reason: ~p", [Imp,Reason])
+ "Reason: ~tp", [Imp,Reason])
end
end, CovImport),
{TsCoverInfo,Opts}.
@@ -2773,7 +2773,7 @@ run_make(Targets, TestDir0, Mod, UserInclude) ->
{up_to_date,_} ->
ok;
{'EXIT',Reason} ->
- io:format("{error,{make_crashed,~p}\n", [Reason]),
+ io:format("{error,{make_crashed,~tp}\n", [Reason]),
{error,{make_crashed,TestDir,Reason}};
{error,ModInfo} ->
io:format("{error,make_failed}\n", []),
@@ -2782,7 +2782,7 @@ run_make(Targets, TestDir0, Mod, UserInclude) ->
{error,{make_failed,Bad}}
end;
{error,_} ->
- io:format("{error,{invalid_directory,~p}}\n", [TestDir0]),
+ io:format("{error,{invalid_directory,~tp}}\n", [TestDir0]),
{error,{invalid_directory,TestDir0}}
end.
@@ -2832,7 +2832,7 @@ maybe_interpret2(Suite, Cases, StepOpts) ->
_ -> ok
catch
_:_Error ->
- io:format(user, "Invalid breakpoint: ~w:~w/1~n",
+ io:format(user, "Invalid breakpoint: ~w:~tw/1~n",
[Suite,Case])
end
end || Case <- Cases, is_atom(Case)],
@@ -2963,7 +2963,7 @@ ct_hooks_args2opts([],Acc) ->
parse_cth_args(String) ->
try
- true = io_lib:printable_list(String),
+ true = io_lib:printable_unicode_list(String),
{ok,Toks,_} = erl_scan:string(String++"."),
{ok, Args} = erl_parse:parse_term(Toks),
Args
@@ -3042,7 +3042,7 @@ rel_to_abs(CtArgs) ->
_ = if Dir /= Abs ->
_ = code:del_path(Dir),
_ = code:del_path(Abs),
- io:format(user, "Converting ~p to ~p and re-inserting "
+ io:format(user, "Converting ~tp to ~tp and re-inserting "
"with add_pathz/1~n",
[Dir, Abs]);
true ->
@@ -3056,7 +3056,7 @@ rel_to_abs(CtArgs) ->
_ = if Dir /= Abs ->
_ = code:del_path(Dir),
_ = code:del_path(Abs),
- io:format(user, "Converting ~p to ~p and re-inserting "
+ io:format(user, "Converting ~tp to ~tp and re-inserting "
"with add_patha/1~n",
[Dir, Abs]);
true ->
@@ -3126,7 +3126,7 @@ opts2args(EnvStartOpts) ->
({group,G}) when is_atom(G) ->
[{group,[atom_to_list(G)]}];
({group,Gs}) when is_list(Gs) ->
- LOfGStrs = [lists:flatten(io_lib:format("~w",[G])) ||
+ LOfGStrs = [lists:flatten(io_lib:format("~tw",[G])) ||
G <- Gs],
[{group,LOfGStrs}];
({testcase,Case}) when is_atom(Case) ->
@@ -3178,10 +3178,10 @@ opts2args(EnvStartOpts) ->
({event_handler,EHs}) when is_list(EHs) ->
[{event_handler,[atom_to_list(EH) || EH <- EHs]}];
({event_handler,{EH,Arg}}) when is_atom(EH) ->
- ArgStr = lists:flatten(io_lib:format("~p", [Arg])),
+ ArgStr = lists:flatten(io_lib:format("~tp", [Arg])),
[{event_handler_init,[atom_to_list(EH),ArgStr]}];
({event_handler,{EHs,Arg}}) when is_list(EHs) ->
- ArgStr = lists:flatten(io_lib:format("~p", [Arg])),
+ ArgStr = lists:flatten(io_lib:format("~tp", [Arg])),
Strs = lists:flatmap(fun(EH) ->
[atom_to_list(EH),
ArgStr,"and"]
@@ -3212,25 +3212,25 @@ opts2args(EnvStartOpts) ->
({ct_hooks,[]}) ->
[];
({ct_hooks,CTHs}) when is_list(CTHs) ->
- io:format(user,"ct_hooks: ~p",[CTHs]),
+ io:format(user,"ct_hooks: ~tp",[CTHs]),
Strs = lists:flatmap(
fun({CTH,Arg,Prio}) ->
[atom_to_list(CTH),
lists:flatten(
- io_lib:format("~p",[Arg])),
+ io_lib:format("~tp",[Arg])),
lists:flatten(
- io_lib:format("~p",[Prio])),
+ io_lib:format("~tp",[Prio])),
"and"];
({CTH,Arg}) ->
[atom_to_list(CTH),
lists:flatten(
- io_lib:format("~p",[Arg])),
+ io_lib:format("~tp",[Arg])),
"and"];
(CTH) when is_atom(CTH) ->
[atom_to_list(CTH),"and"]
end,CTHs),
[_LastAnd|StrsR] = lists:reverse(Strs),
- io:format(user,"return: ~p",[lists:reverse(StrsR)]),
+ io:format(user,"return: ~tp",[lists:reverse(StrsR)]),
[{ct_hooks,lists:reverse(StrsR)}];
({Opt,As=[A|_]}) when is_atom(A) ->
[{Opt,[atom_to_list(Atom) || Atom <- As]}];
@@ -3312,7 +3312,7 @@ start_trace(Args) ->
ok ->
true;
{_,Error} ->
- io:format("Warning! Tracing not started. Reason: ~p~n~n",
+ io:format("Warning! Tracing not started. Reason: ~tp~n~n",
[Error]),
false
end;
diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl
index 5ac91f396b..ca62357e1c 100644
--- a/lib/common_test/src/ct_ssh.erl
+++ b/lib/common_test/src/ct_ssh.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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.
@@ -161,7 +161,7 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) ->
connect(KeyOrName, ConnType, ExtraOpts) ->
case ct:get_config(KeyOrName) of
undefined ->
- log(heading(connect,KeyOrName), "Failed: ~p\n",
+ log(heading(connect,KeyOrName), "Failed: ~tp\n",
[{not_available,KeyOrName}]),
{error,{not_available,KeyOrName}};
SSHData ->
@@ -214,18 +214,18 @@ connect(KeyOrName, ConnType, ExtraOpts) ->
end,
case {Addr,proplists:get_value(port, AllOpts1)} of
{undefined,_} ->
- log(heading(connect,KeyOrName), "Failed: ~p\n",
+ log(heading(connect,KeyOrName), "Failed: ~tp\n",
[{not_available,{KeyOrName,ConnType1}}]),
{error,{not_available,{KeyOrName,ConnType1}}};
{_,undefined} ->
try_log(heading(connect,KeyOrName),
- "Opening ~w connection to ~p:22\n",
+ "Opening ~w connection to ~tp:22\n",
[ConnType1,Addr]),
ct_gen_conn:start(KeyOrName, {ConnType1,Addr,22},
AllOpts1, ?MODULE);
{_,Port} ->
try_log(heading(connect,KeyOrName),
- "Opening ~w connection to ~p:~w\n",
+ "Opening ~w connection to ~tp:~w\n",
[ConnType1,Addr,Port]),
ct_gen_conn:start(KeyOrName, {ConnType1,Addr,Port},
AllOpts1, ?MODULE)
@@ -995,7 +995,7 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) ->
SSHRef = element(2, Ok),
try_log(heading(init,KeyOrName),
"Opened ~w connection:\n"
- "Host: ~p (~p)\nUser: ~p\nPassword: ~p\n",
+ "Host: ~tp (~p)\nUser: ~tp\nPassword: ~p\n",
[ConnType,Addr,Port,User,lists:duplicate(length(Password),$*)]),
{ok,SSHRef,#state{ssh_ref=SSHRef, conn_type=ConnType,
target=KeyOrName}}
@@ -1033,11 +1033,11 @@ handle_msg({exec,Chn,Command,TO}, State) ->
end,
case Chn1 of
{error,_} = ChnError ->
- log(heading(exec,Target), "Opening channel failed: ~p", [ChnError]),
+ log(heading(exec,Target), "Opening channel failed: ~tp", [ChnError]),
{ChnError,State};
_ ->
try_log(heading(exec,Target),
- "SSH Ref: ~p, Chn: ~p, Command: ~p, Timeout: ~p",
+ "SSH Ref: ~p, Chn: ~p, Command: ~tp, Timeout: ~p",
[SSHRef,Chn1,Command,TO]),
case ssh_connection:exec(SSHRef, Chn1, Command, TO) of
success ->
@@ -1060,7 +1060,7 @@ handle_msg({send,Chn,Type,Data,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
try_log(heading(send,Target),
"SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n"
- "Data: ~p", [SSHRef,Chn,Type,TO,Data]),
+ "Data: ~tp", [SSHRef,Chn,Type,TO,Data]),
Result = ssh_connection:send(SSHRef, Chn, Type, Data, TO),
{Result,State};
@@ -1068,7 +1068,7 @@ handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
try_log(heading(send_and_receive,Target),
"SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n"
- "Data: ~p", [SSHRef,Chn,Type,TO,Data]),
+ "Data: ~tp", [SSHRef,Chn,Type,TO,Data]),
case ssh_connection:send(SSHRef, Chn, Type, Data, TO) of
ok ->
Result = do_recv_response(SSHRef, Chn, [], End, TO),
@@ -1080,7 +1080,7 @@ handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) ->
handle_msg({subsystem,Chn,Subsystem,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
try_log(heading(subsystem,Target),
- "SSH Ref: ~p, Chn: ~p, Subsys: ~p, Timeout: ~p",
+ "SSH Ref: ~p, Chn: ~p, Subsys: ~tp, Timeout: ~p",
[SSHRef,Chn,Subsystem,TO]),
Result = ssh_connection:subsystem(SSHRef, Chn, Subsystem, TO),
{Result,State};
@@ -1097,151 +1097,151 @@ handle_msg({shell,Chn,TO}, State) ->
handle_msg({read_file,Srv,File}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_file(ref(Srv,SSHRef), File),S};
handle_msg({write_file,Srv,File,Iolist}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:write_file(ref(Srv,SSHRef), File, Iolist),S};
handle_msg({list_dir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:list_dir(ref(Srv,SSHRef), Path),S};
handle_msg({open,Srv,File,Mode}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:open(ref(Srv,SSHRef), File, Mode),S};
handle_msg({opendir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:opendir(ref(Srv,SSHRef), Path),S};
handle_msg({close,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:close(ref(Srv,SSHRef), Handle),S};
handle_msg({read,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read(ref(Srv,SSHRef), Handle, Len),S};
handle_msg({pread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:pread(ref(Srv,SSHRef),Handle,Position,Length),S};
handle_msg({aread,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:aread(ref(Srv,SSHRef), Handle, Len),S};
handle_msg({apread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:apread(ref(Srv,SSHRef), Handle, Position, Length),S};
handle_msg({write,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:write(ref(Srv,SSHRef), Handle, Data),S};
handle_msg({pwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:pwrite(ref(Srv,SSHRef), Handle, Position, Data),S};
handle_msg({awrite,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:awrite(ref(Srv,SSHRef), Handle, Data),S};
handle_msg({apwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:apwrite(ref(Srv,SSHRef), Handle, Position, Data),S};
handle_msg({position,Srv,Handle,Location}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:position(ref(Srv,SSHRef), Handle, Location),S};
handle_msg({read_file_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_file_info(ref(Srv,SSHRef), Name),S};
handle_msg({get_file_info,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:get_file_info(ref(Srv,SSHRef), Handle),S};
handle_msg({read_link_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_link_info(ref(Srv,SSHRef), Name),S};
handle_msg({write_file_info,Srv,Name,Info}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:write_file_info(ref(Srv,SSHRef), Name, Info),S};
handle_msg({read_link,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_link(ref(Srv,SSHRef), Name),S};
handle_msg({make_symlink,Srv,Name,Target}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:make_symlink(ref(Srv,SSHRef), Name, Target),S};
handle_msg({rename,Srv,OldName,NewName}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:rename(ref(Srv,SSHRef), OldName, NewName),S};
handle_msg({delete,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:delete(ref(Srv,SSHRef), Name),S};
handle_msg({make_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:make_dir(ref(Srv,SSHRef), Name),S};
handle_msg({del_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
try_log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ "SSH Ref: ~p, Server: ~p~nCmd: ~tp",
[SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:del_dir(ref(Srv,SSHRef), Name),S}.
@@ -1285,7 +1285,7 @@ do_recv_response(SSH, Chn, Data, End, Timeout) ->
{ssh_cm, SSH, {data,Chn,_,NewData}} ->
ssh_connection:adjust_window(SSH, Chn, size(NewData)),
- debug("RECVD~n~p", [binary_to_list(NewData)]),
+ debug("RECVD~n~tp", [binary_to_list(NewData)]),
DataAcc = Data ++ binary_to_list(NewData),
if is_function(End) ->
case End(DataAcc) of
@@ -1338,7 +1338,7 @@ do_recv_response(SSH, Chn, Data, End, Timeout) ->
%% {ok,WCh};
Other ->
- debug("UNEXPECTED MESSAGE~n~p ~p~n~p", [SSH,Chn,Other]),
+ debug("UNEXPECTED MESSAGE~n~p ~p~n~tp", [SSH,Chn,Other]),
do_recv_response(SSH, Chn, Data, End, Timeout)
after Timeout ->
@@ -1391,7 +1391,7 @@ mod(Cmd) ->
%%%-----------------------------------------------------------------
%%%
heading(Function, Ref) ->
- io_lib:format("ct_ssh:~w ~p",[Function,Ref]).
+ io_lib:format("ct_ssh:~tw ~tp",[Function,Ref]).
%%%-----------------------------------------------------------------
%%%
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index bff1112ab9..14d9d381da 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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.
@@ -251,7 +251,7 @@ open(KeyOrName,ConnType,TargetMod) ->
open(KeyOrName,ConnType,TargetMod,Extra) ->
case ct:get_config({KeyOrName,ConnType}) of
undefined ->
- log(undefined,open,"Failed: ~p",[{not_available,KeyOrName}]),
+ log(undefined,open,"Failed: ~tp",[{not_available,KeyOrName}]),
{error,{not_available,KeyOrName,ConnType}};
Addr ->
Addr1 =
@@ -273,7 +273,7 @@ open(KeyOrName,ConnType,TargetMod,Extra) ->
end;
Bool -> Bool
end,
- log(undefined,open,"Connecting to ~p(~p)",
+ log(undefined,open,"Connecting to ~tp(~tp)",
[KeyOrName,Addr1]),
Reconnect =
case ct:get_config({telnet_settings,reconnection_attempts}) of
@@ -672,7 +672,7 @@ set_telnet_defaults([{tcp_nodelay,NoDelay}|Ss],S) ->
set_telnet_defaults(Ss,S#state{tcp_nodelay=NoDelay});
set_telnet_defaults([Unknown|Ss],S) ->
force_log(S,error,
- "Bad element in telnet_settings: ~p",[Unknown]),
+ "Bad element in telnet_settings: ~tp",[Unknown]),
set_telnet_defaults(Ss,S);
set_telnet_defaults([],S) ->
S.
@@ -680,7 +680,7 @@ set_telnet_defaults([],S) ->
%% @hidden
handle_msg({cmd,Cmd,Opts},State) ->
start_gen_log(heading(cmd,State#state.name)),
- log(State,cmd,"Cmd: ~p",[Cmd]),
+ log(State,cmd,"Cmd: ~tp",[Cmd]),
%% whatever is in the buffer from previous operations
%% will be ignored as we go ahead with this telnet cmd
@@ -715,7 +715,7 @@ handle_msg({cmd,Cmd,Opts},State) ->
case teln_cmd(State#state.teln_pid, Cmd, State#state.prx,
Newline, TO) of
{ok,Data,_PromptType,Rest} ->
- log(State,recv,"Return: ~p",[{ok,Data}]),
+ log(State,recv,"Return: ~tp",[{ok,Data}]),
{{ok,Data},Rest,true};
Error ->
Retry = {retry,{Error,
@@ -723,14 +723,14 @@ handle_msg({cmd,Cmd,Opts},State) ->
State#state.type},
State#state.teln_pid,
{cmd,Cmd,Opts}}},
- log(State,recv,"Return: ~p",[Error]),
+ log(State,recv,"Return: ~tp",[Error]),
{Retry,[],false}
end,
end_gen_log(),
{Return,State#state{buffer=NewBuffer,prompt=Prompt}};
handle_msg({send,Cmd,Opts},State) ->
start_gen_log(heading(send,State#state.name)),
- log(State,send,"Sending: ~p",[Cmd]),
+ log(State,send,"Sending: ~tp",[Cmd]),
debug_cont_gen_log("Throwing Buffer:",[]),
debug_log_lines(State#state.buffer),
@@ -762,12 +762,12 @@ handle_msg(get_data,State) ->
log(State,cmd,"Reading data...",[]),
{ok,Data,Buffer} = teln_get_all_data(State,State#state.buffer,[],[],
State#state.poll_limit),
- log(State,recv,"Return: ~p",[{ok,Data}]),
+ log(State,recv,"Return: ~tp",[{ok,Data}]),
end_gen_log(),
{{ok,Data},State#state{buffer=Buffer}};
handle_msg({expect,Pattern,Opts},State) ->
start_gen_log(heading(expect,State#state.name)),
- log(State,expect,"Expect: ~p\nOpts = ~p\n",[Pattern,Opts]),
+ log(State,expect,"Expect: ~tp\nOpts = ~tp\n",[Pattern,Opts]),
{Return,NewBuffer,Prompt} =
case teln_expect(State#state.name,
State#state.teln_pid,
@@ -779,15 +779,15 @@ handle_msg({expect,Pattern,Opts},State) ->
P = check_if_prompt_was_reached(Data,[]),
{{ok,Data},Rest,P};
{ok,Data,HaltReason,Rest} ->
- force_log(State,expect,"HaltReason: ~p",[HaltReason]),
+ force_log(State,expect,"HaltReason: ~tp",[HaltReason]),
P = check_if_prompt_was_reached(Data,HaltReason),
{{ok,Data,HaltReason},Rest,P};
{error,Reason,Rest} ->
- force_log(State,expect,"Expect failed\n~p",[{error,Reason}]),
+ force_log(State,expect,"Expect failed\n~tp",[{error,Reason}]),
P = check_if_prompt_was_reached([],Reason),
{{error,Reason},Rest,P};
{error,Reason} ->
- force_log(State,expect,"Expect failed\n~p",[{error,Reason}]),
+ force_log(State,expect,"Expect failed\n~tp",[{error,Reason}]),
P = check_if_prompt_was_reached([],Reason),
{{error,Reason},[],P}
end,
@@ -896,7 +896,7 @@ check_if_prompt_was_reached(_,_) ->
heading(Action,undefined) ->
io_lib:format("~w ~w",[?MODULE,Action]);
heading(Action,Name) ->
- io_lib:format("~w ~w for ~p",[?MODULE,Action,Name]).
+ io_lib:format("~w ~w for ~tp",[?MODULE,Action,Name]).
force_log(State,Action,String,Args) ->
log(State,Action,String,Args,true).
@@ -1294,7 +1294,7 @@ get_data1(Pid) ->
%% one_expect: split data chunk at prompts
one_expect(Name,Pid,Data,Pattern,EO) when EO#eo.prompt_check==false ->
-% io:format("Raw Data ~p Pattern ~p EO ~p ",[Data,Pattern,EO]),
+% io:format("Raw Data ~tp Pattern ~tp EO ~tp ",[Data,Pattern,EO]),
one_expect1(Name,Pid,Data,Pattern,[],EO#eo{found_prompt=false});
one_expect(Name,Pid,Data,Pattern,EO) ->
case match_prompt(Data,EO#eo.prx) of
diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl
index 5df7e279ac..c8d217cd2a 100644
--- a/lib/common_test/src/ct_telnet_client.erl
+++ b/lib/common_test/src/ct_telnet_client.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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.
@@ -120,7 +120,7 @@ get_data(Pid) ->
init(Parent, Server, Port, Timeout, KeepAlive, NoDelay, ConnName) ->
case gen_tcp:connect(Server, Port, [list,{packet,0},{nodelay,NoDelay}], Timeout) of
{ok,Sock} ->
- dbg("~p connected to: ~p (port: ~w, keep_alive: ~w)\n",
+ dbg("~tp connected to: ~tp (port: ~w, keep_alive: ~w)\n",
[ConnName,Server,Port,KeepAlive]),
send([?IAC,?DO,?SUPPRESS_GO_AHEAD], Sock, ConnName),
Parent ! {open,self()},
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 180786273d..09839bd35d 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -344,7 +344,7 @@ create_spec_tree([Spec|Specs],TS,JoinWithNext,Known) ->
create_spec_tree(Specs,TS,JoinWithNext,Known)};
{error,Reason} ->
ReasonStr =
- lists:flatten(io_lib:format("~s",
+ lists:flatten(io_lib:format("~ts",
[file:format_error(Reason)])),
throw({error,{SpecAbsName,ReasonStr}})
end
@@ -1101,7 +1101,7 @@ check_term(Term) when is_tuple(Term) ->
true ->
io:format("~nSuspicious term, "
"please check:~n"
- "~p~n", [Term]),
+ "~tp~n", [Term]),
invalid;
false ->
invalid
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 802e9be97c..abf131f4df 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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.
@@ -207,7 +207,7 @@ do_start(Parent, Mode, LogDir, Verbosity) ->
catch
_:CTHReason ->
ErrorInfo = if is_atom(CTHReason) ->
- io_lib:format("{~p,~p}",
+ io_lib:format("{~tp,~tp}",
[CTHReason,
erlang:get_stacktrace()]);
true ->
@@ -490,7 +490,7 @@ loop(Mode,TestData,StartDir) ->
?MAX_IMPORTANCE,
"CT Error Notification",
"Connection process died: "
- "Pid: ~w, Address: ~p, "
+ "Pid: ~w, Address: ~tp, "
"Callback: ~w\n"
"Reason: ~ts\n\n",
[Pid,A,CB,ErrorHtml]),
@@ -501,7 +501,7 @@ loop(Mode,TestData,StartDir) ->
_ ->
%% Let process crash in case of error, this shouldn't happen!
io:format("\n\nct_util_server got EXIT "
- "from ~w: ~p\n\n", [Pid,Reason]),
+ "from ~w: ~tp\n\n", [Pid,Reason]),
ok = file:set_cwd(StartDir),
exit(Reason)
end
@@ -977,12 +977,12 @@ get_profile_data(Profile, Key, StartDir) ->
end,
case Result of
{error,enoent} when Profile /= default ->
- io:format(?def_gl, "~nERROR! Missing profile file ~p~n", [File]),
+ io:format(?def_gl, "~nERROR! Missing profile file ~tp~n", [File]),
undefined;
{error,enoent} when Profile == default ->
undefined;
{error,Reason} ->
- io:format(?def_gl,"~nERROR! Error in profile file ~p: ~p~n",
+ io:format(?def_gl,"~nERROR! Error in profile file ~tp: ~tp~n",
[WhichFile,Reason]),
undefined;
{ok,Data} ->
@@ -993,7 +993,7 @@ get_profile_data(Profile, Key, StartDir) ->
Data;
_ ->
io:format(?def_gl,
- "~nERROR! Invalid profile data in ~p~n",
+ "~nERROR! Invalid profile data in ~tp~n",
[WhichFile]),
[]
end,
diff --git a/lib/common_test/src/ct_webtool.erl b/lib/common_test/src/ct_webtool.erl
index 87af442fd3..9016aca899 100644
--- a/lib/common_test/src/ct_webtool.erl
+++ b/lib/common_test/src/ct_webtool.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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.
@@ -120,10 +120,10 @@ debug_app(Mod) ->
out(_,{trace_ts,Pid,call,MFA={M,F,A},{W,_,_},TS},_,S)
when W==webtool;W==mod_esi->
- io:format("~w: (~p)~ncall ~s~n", [TS,Pid,ffunc(MFA)]),
+ io:format("~w: (~p)~ncall ~ts~n", [TS,Pid,ffunc(MFA)]),
[{M,F,length(A)}|S];
out(_,{trace_ts,Pid,return_from,MFA,R,TS},_,[MFA|S]) ->
- io:format("~w: (~p)~nreturned from ~s -> ~p~n", [TS,Pid,ffunc(MFA),R]),
+ io:format("~w: (~p)~nreturned from ~ts -> ~tp~n", [TS,Pid,ffunc(MFA),R]),
S;
out(_,_,_,_) ->
ok.
@@ -171,7 +171,7 @@ script_start([App,Browser]) ->
IExplore=filename:join(win32reg:expand(Val),"iexplore.exe"),
os:cmd("\"" ++ IExplore ++ "\" " ++ Url);
_ when OSType == win32 ->
- io:format("Starting ~w...\n",[Browser]),
+ io:format("Starting ~tw...\n",[Browser]),
os:cmd("\"" ++ atom_to_list(Browser) ++ "\" " ++ Url);
B when B==firefox; B==mozilla ->
io:format("Sending URL to ~w...",[Browser]),
@@ -194,7 +194,7 @@ script_start([App,Browser]) ->
os:cmd(BStr ++ " " ++ Url)
end;
_ ->
- io:format("Starting ~w...\n",[Browser]),
+ io:format("Starting ~tw...\n",[Browser]),
os:cmd(atom_to_list(Browser) ++ " " ++ Url)
end,
ok;
@@ -379,7 +379,7 @@ print_url(ConfigData)->
Server=proplists:get_value(server_name,ConfigData,"undefined"),
Port=proplists:get_value(port,ConfigData,"undefined"),
{A,B,C,D}=proplists:get_value(bind_address,ConfigData,"undefined"),
- io:format("WebTool is available at http://~s:~w/~n",[Server,Port]),
+ io:format("WebTool is available at http://~ts:~w/~n",[Server,Port]),
io:format("Or http://~w.~w.~w.~w:~w/~n",[A,B,C,D,Port]).
@@ -859,8 +859,8 @@ handle_app({Name,{start,{func,Start,Stop}}},Data,_Pid,Cmd)->
%%! Here the tool disappears from the webtool interface!!
io:format("\n=======ERROR (webtool, line ~w) =======\n"
"Could not start application \'~p\'\n\n"
- "~w:~w(~s) ->\n"
- "~p\n\n",
+ "~w:~tw(~ts) ->\n"
+ "~tp\n\n",
[?LINE,Name,M,F,format_args(A),Exit]),
ets:delete(Data,Name);
_OK->
@@ -883,16 +883,16 @@ handle_app({Name,{start,{child,ChildSpec}}},Data,Pid,Cmd)->
%%! Here the tool disappears from the webtool interface!!
io:format("\n=======ERROR (webtool, line ~w) =======\n"
"Could not start application \'~p\'\n\n"
- "supervisor:start_child(~p,~p) ->\n"
- "~p\n\n",
+ "supervisor:start_child(~p,~tp) ->\n"
+ "~tp\n\n",
[?LINE,Name,Pid,ChildSpec,{error,Reason}]),
ets:delete(Data,Name);
Error ->
%%! Here the tool disappears from the webtool interface!!
io:format("\n=======ERROR (webtool, line ~w) =======\n"
"Could not start application \'~p\'\n\n"
- "supervisor:start_child(~p,~p) ->\n"
- "~p\n\n",
+ "supervisor:start_child(~p,~tp) ->\n"
+ "~tp\n\n",
[?LINE,Name,Pid,ChildSpec,Error]),
ets:delete(Data,Name)
end;
@@ -924,7 +924,7 @@ handle_app({Name,{start,{app,Real_name}}},Data,_Pid,Cmd)->
io:format("\n=======ERROR (webtool, line ~w) =======\n"
"Could not start application \'~p\'\n\n"
"application:start(~p,~p) ->\n"
- "~p\n\n",
+ "~tp\n\n",
[?LINE,Name,Real_name,temporary,Error]),
ets:delete(Data,Name)
end;
@@ -940,7 +940,7 @@ handle_app({Name,Incorrect},Data,_Pid,Cmd)->
%%! Here the tool disappears from the webtool interface!!
io:format("\n=======ERROR (webtool, line ~w) =======\n"
"Could not ~w application \'~p\'\n\n"
- "Incorrect data: ~p\n\n",
+ "Incorrect data: ~tp\n\n",
[?LINE,Cmd,Name,Incorrect]),
ets:delete(Data,Name).
@@ -1202,12 +1202,12 @@ filter_tool_files(Dir,[File|Rest]) ->
%%%-----------------------------------------------------------------
%%% format functions
ffunc({M,F,A}) when is_list(A) ->
- io_lib:format("~w:~w(~s)\n",[M,F,format_args(A)]);
+ io_lib:format("~w:~tw(~ts)\n",[M,F,format_args(A)]);
ffunc({M,F,A}) when is_integer(A) ->
- io_lib:format("~w:~w/~w\n",[M,F,A]).
+ io_lib:format("~w:~tw/~w\n",[M,F,A]).
format_args([]) ->
"";
format_args(Args) ->
- Str = lists:append(["~p"|lists:duplicate(length(Args)-1,",~p")]),
+ Str = lists:append(["~tp"|lists:duplicate(length(Args)-1,",~tp")]),
io_lib:format(Str,Args).
diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl
index faff150ac9..7b6f03311a 100644
--- a/lib/common_test/src/cth_conn_log.erl
+++ b/lib/common_test/src/cth_conn_log.erl
@@ -116,7 +116,7 @@ pre_init_per_testcase(_Suite,TestCase,Config,CthState) ->
"<table borders=1>"
"<b>" ++ ConnModStr ++ " logs:</b>\n" ++
[io_lib:format(
- "<tr><td>~p</td><td><a href=\"~ts\">~ts</a>"
+ "<tr><td>~tp</td><td><a href=\"~ts\">~ts</a>"
"</td></tr>",
[S,ct_logs:uri(L),filename:basename(L)])
|| {S,L} <- Ls] ++
diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl
index 1bc9b10d41..8b29d0f96d 100644
--- a/lib/common_test/src/cth_log_redirect.erl
+++ b/lib/common_test/src/cth_log_redirect.erl
@@ -250,26 +250,26 @@ format_header(#eh_state{curr_suite = Suite,
format_header(#eh_state{curr_suite = Suite,
curr_group = undefined,
curr_func = TcOrConf}) ->
- io_lib:format("System report during ~w:~w/1",
+ io_lib:format("System report during ~w:~tw/1",
[Suite,TcOrConf]);
format_header(#eh_state{curr_suite = Suite,
curr_group = Group,
curr_func = Conf}) when Conf == init_per_group;
Conf == end_per_group ->
- io_lib:format("System report during ~w:~w/2 for ~w",
+ io_lib:format("System report during ~w:~w/2 for ~tw",
[Suite,Conf,Group]);
format_header(#eh_state{curr_suite = Suite,
curr_group = Group,
parallel_tcs = true}) ->
- io_lib:format("System report during ~w in ~w",
+ io_lib:format("System report during ~tw in ~w",
[Group,Suite]);
format_header(#eh_state{curr_suite = Suite,
curr_group = Group,
curr_func = TC}) ->
- io_lib:format("System report during ~w:~w/1 in ~w",
+ io_lib:format("System report during ~w:~tw/1 in ~tw",
[Suite,TC,Group]).
code_change(_OldVsn, State, _Extra) ->
diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl
index 0bbb217275..da68bd105e 100644
--- a/lib/common_test/src/cth_surefire.erl
+++ b/lib/common_test/src/cth_surefire.erl
@@ -143,7 +143,7 @@ on_tc_fail(_Suite,_TC, Res, State) ->
TC = hd(TCs),
NewTC = TC#testcase{
result =
- {fail,lists:flatten(io_lib:format("~p",[Res]))} },
+ {fail,lists:flatten(io_lib:format("~tp",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
on_tc_skip(Suite,{ConfigFunc,_GrName}, Res, State) ->
@@ -164,7 +164,7 @@ do_tc_skip(Res, State) ->
TC = hd(TCs),
NewTC = TC#testcase{
result =
- {skipped,lists:flatten(io_lib:format("~p",[Res]))} },
+ {skipped,lists:flatten(io_lib:format("~tp",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
init_tc(State, Config) when is_list(Config) == false ->
diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl
index a4a84393c1..ee3a5e4bba 100644
--- a/lib/common_test/src/test_server.erl
+++ b/lib/common_test/src/test_server.erl
@@ -175,7 +175,7 @@ do_cover_compile(Modules) ->
ok.
warn_compile({error,{Reason,Module}}) ->
- io:fwrite("\nWARNING: Could not cover compile ~ts: ~p\n",
+ io:fwrite("\nWARNING: Could not cover compile ~ts: ~tp\n",
[Module,{error,Reason}]).
%% Make sure all modules are loaded and unstick if sticky
@@ -189,7 +189,7 @@ prepare_cover_compile([M|Ms],Sticky) ->
{module,_} ->
prepare_cover_compile([M|Ms],Sticky);
Error ->
- io:fwrite("\nWARNING: Could not load ~w: ~p\n",[M,Error]),
+ io:fwrite("\nWARNING: Could not load ~w: ~tp\n",[M,Error]),
prepare_cover_compile(Ms,Sticky)
end;
{false,_} ->
@@ -450,7 +450,7 @@ run_test_case_msgloop(#st{ref=Ref,pid=Pid,end_conf_pid=EndConfPid0}=St0) ->
exit(Pid, kill),
%% here's the only place we know Reason, so we save
%% it as a comment, potentially replacing user data
- Error = lists:flatten(io_lib:format("Aborted: ~p",
+ Error = lists:flatten(io_lib:format("Aborted: ~tp",
[Reason])),
Error1 = lists:flatten([string:strip(S,left) ||
S <- string:tokens(Error,
@@ -756,13 +756,13 @@ print_end_conf_result(Mod,Func,Conf,Cause,Error) ->
Str2Print =
fun(NoHTML) when NoHTML == stdout; NoHTML == major ->
io_lib:format("WARNING! "
- "~w:end_per_testcase(~w, ~tp)"
+ "~w:end_per_testcase(~tw, ~tp)"
" ~s!\n\tReason: ~tp\n",
[Mod,Func,Conf,Cause,Error]);
(minor) ->
ErrorStr = test_server_ctrl:escape_chars(Error),
io_lib:format("WARNING! "
- "~w:end_per_testcase(~w, ~tp)"
+ "~w:end_per_testcase(~tw, ~tp)"
" ~s!\n\tReason: ~ts\n",
[Mod,Func,Conf,Cause,ErrorStr])
end,
@@ -792,7 +792,7 @@ spawn_fw_call(Mod,IPTC={init_per_testcase,Func},CurrConf,Pid,
_ -> died
end,
group_leader() ! {printout,12,
- "ERROR! ~w:init_per_testcase(~w, ~p)"
+ "ERROR! ~w:init_per_testcase(~tw, ~tp)"
" failed!\n\tReason: ~tp\n",
[Mod,Func,CurrConf,Why]},
%% finished, report back
@@ -820,7 +820,7 @@ spawn_fw_call(Mod,EPTC={end_per_testcase,Func},EndConf,Pid,
{timetrap_timeout,TVal} ->
group_leader() !
{printout,12,
- "WARNING! ~w:end_per_testcase(~w, ~p)"
+ "WARNING! ~w:end_per_testcase(~tw, ~tp)"
" failed!\n\tReason: timetrap timeout"
" after ~w ms!\n", [Mod,Func,EndConf,TVal]},
W = "<font color=\"red\">"
@@ -829,7 +829,7 @@ spawn_fw_call(Mod,EPTC={end_per_testcase,Func},EndConf,Pid,
_ ->
group_leader() !
{printout,12,
- "WARNING! ~w:end_per_testcase(~w, ~p)"
+ "WARNING! ~w:end_per_testcase(~tw, ~tp)"
" failed!\n\tReason: ~tp\n",
[Mod,Func,EndConf,Why]},
W = "<font color=\"red\">"
@@ -859,7 +859,7 @@ spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) ->
Comment =
lists:flatten(
io_lib:format("<font color=\"red\">"
- "WARNING! ~w:~w failed!</font>",
+ "WARNING! ~w:~tw failed!</font>",
[FwMod,FwFunc])),
%% finished, report back
SendTo ! {self(),fw_notify_done,
@@ -1341,7 +1341,7 @@ print_init_conf_result(Line,Cause,Reason) ->
Str2Print =
fun(NoHTML) when NoHTML == stdout; NoHTML == major ->
io_lib:format("ERROR! init_per_testcase ~s!\n"
- "\tLocation: ~p\n\tReason: ~tp\n",
+ "\tLocation: ~tp\n\tReason: ~tp\n",
[Cause,Line,Reason]);
(minor) ->
ReasonStr = test_server_ctrl:escape_chars(Reason),
@@ -1413,7 +1413,7 @@ print_end_tc_warning(EndFunc,Reason,Cause,Loc) ->
Str2Print =
fun(NoHTML) when NoHTML == stdout; NoHTML == major ->
io_lib:format("WARNING: ~w ~s!\n"
- "Reason: ~tp\nLine: ~p\n",
+ "Reason: ~tp\nLine: ~tp\n",
[EndFunc,Cause,Reason,Loc]);
(minor) ->
ReasonStr = test_server_ctrl:escape_chars(Reason),
@@ -1515,7 +1515,7 @@ lookup_config(Key,Config) ->
{value,{Key,Val}} ->
Val;
_ ->
- io:format("Could not find element ~p in Config.~n",[Key]),
+ io:format("Could not find element ~tp in Config.~n",[Key]),
undefined
end.
@@ -1600,7 +1600,7 @@ format(Detail, Format, Args) ->
Str =
case catch io_lib:format(Format,Args) of
{'EXIT',_} ->
- io_lib:format("illegal format; ~p with args ~p.\n",
+ io_lib:format("illegal format; ~tp with args ~tp.\n",
[Format,Args]);
Valid -> Valid
end,
@@ -1732,7 +1732,7 @@ fail(Reason) ->
cast_to_list(X) when is_list(X) -> X;
cast_to_list(X) when is_atom(X) -> atom_to_list(X);
-cast_to_list(X) -> lists:flatten(io_lib:format("~p", [X])).
+cast_to_list(X) -> lists:flatten(io_lib:format("~tp", [X])).
@@ -1904,7 +1904,7 @@ ensure_timetrap(Config) ->
Garbage ->
erase(test_server_default_timetrap),
format("=== WARNING: garbage in "
- "test_server_default_timetrap: ~p~n",
+ "test_server_default_timetrap: ~tp~n",
[Garbage])
end,
DTmo = case lists:keysearch(default_timeout,1,Config) of
@@ -1933,7 +1933,7 @@ cancel_default_timetrap(true) ->
Garbage ->
erase(test_server_default_timetrap),
format("=== WARNING: garbage in "
- "test_server_default_timetrap: ~p~n",
+ "test_server_default_timetrap: ~tp~n",
[Garbage]),
error
end.
@@ -1942,7 +1942,7 @@ time_ms({hours,N}, _, _) -> hours(N);
time_ms({minutes,N}, _, _) -> minutes(N);
time_ms({seconds,N}, _, _) -> seconds(N);
time_ms({Other,_N}, _, _) ->
- format("=== ERROR: Invalid time specification: ~p. "
+ format("=== ERROR: Invalid time specification: ~tp. "
"Should be seconds, minutes, or hours.~n", [Other]),
exit({invalid_time_format,Other});
time_ms(Ms, _, _) when is_integer(Ms) -> Ms;
diff --git a/lib/common_test/src/test_server_ctrl.erl b/lib/common_test/src/test_server_ctrl.erl
index 064e375cd5..9412c43187 100644
--- a/lib/common_test/src/test_server_ctrl.erl
+++ b/lib/common_test/src/test_server_ctrl.erl
@@ -232,7 +232,7 @@ parse_cmd_line(['SPEC',Spec|Cmds], SpecList, Names, Param, Trc, Cov, TCCB) ->
parse_cmd_line(Cmds, TermList++SpecList, [Name|Names], Param,
Trc, Cov, TCCB);
{error,Reason} ->
- io:format("Can't open ~w: ~p\n",[Spec, file:format_error(Reason)]),
+ io:format("Can't open ~tw: ~tp\n",[Spec, file:format_error(Reason)]),
parse_cmd_line(Cmds, SpecList, Names, Param, Trc, Cov, TCCB)
end;
parse_cmd_line(['NAME',Name|Cmds], SpecList, Names, Param, Trc, Cov, TCCB) ->
@@ -261,7 +261,7 @@ parse_cmd_line(['COVER',App,CF,Analyse|Cmds], SpecList, Names, Param, Trc, _Cov,
parse_cmd_line(['TESTCASE_CALLBACK',Mod,Func|Cmds], SpecList, Names, Param, Trc, Cov, _) ->
parse_cmd_line(Cmds, SpecList, Names, Param, Trc, Cov, {Mod,Func});
parse_cmd_line([Obj|_Cmds], _SpecList, _Names, _Param, _Trc, _Cov, _TCCB) ->
- io:format("~w: Bad argument: ~w\n", [?MODULE,Obj]),
+ io:format("~w: Bad argument: ~tw\n", [?MODULE,Obj]),
io:format(" Use the `ts' module to start tests.\n", []),
io:format(" (If you ARE using `ts', there is a bug in `ts'.)\n", []),
halt(1);
@@ -280,7 +280,7 @@ parse_cmd_line([], SpecList, Names, Param, Trc, Cov, TCCB) ->
cast_to_list(X) when is_list(X) -> X;
cast_to_list(X) when is_atom(X) -> atom_to_list(X);
-cast_to_list(X) -> lists:flatten(io_lib:format("~w", [X])).
+cast_to_list(X) -> lists:flatten(io_lib:format("~tw", [X])).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% START INTERFACE
@@ -878,7 +878,7 @@ handle_call({testcase_callback,ModFunc}, _From, State) ->
ok;
false ->
io:format(user,
- "WARNING! Callback function ~w:~w/4 undefined.~n~n",
+ "WARNING! Callback function ~w:~tw/4 undefined.~n~n",
[Mod,Func])
end;
_ ->
@@ -1016,7 +1016,7 @@ handle_info({'EXIT',Pid,Reason}, State) ->
killed ->
io:format("Suite ~ts was killed\n", [Name]);
_Other ->
- io:format("Suite ~ts was killed with reason ~p\n",
+ io:format("Suite ~ts was killed with reason ~tp\n",
[Name,Reason])
end,
State2 = State#state{jobs=NewJobs},
@@ -1168,10 +1168,10 @@ init_tester(Mod, Func, Args, Dir, Name, {_,_,MinLev}=Levels,
{'EXIT',test_suites_done} ->
ok;
{'EXIT',_Pid,Reason} ->
- print(1, "EXIT, reason ~p", [Reason]);
+ print(1, "EXIT, reason ~tp", [Reason]);
{'EXIT',Reason} ->
report_severe_error(Reason),
- print(1, "EXIT, reason ~p", [Reason])
+ print(1, "EXIT, reason ~tp", [Reason])
end,
Time = TimeMy/1000000,
SuccessStr =
@@ -1263,7 +1263,7 @@ do_spec(SpecName, TimetrapSpec) when is_list(SpecName) ->
{ok,TermList} ->
do_spec_list(TermList,TimetrapSpec);
{error,Reason} ->
- io:format("Can't open ~ts: ~p\n", [SpecName,Reason]),
+ io:format("Can't open ~ts: ~tp\n", [SpecName,Reason]),
{error,{cant_open_spec,Reason}}
end.
@@ -1346,7 +1346,7 @@ do_spec_terms([{require_nodenames,NumNames}|Terms], TopCases, SkipList, Config)
do_spec_terms(Terms, TopCases, SkipList,
update_config(Config, {nodenames,NodeNames}));
do_spec_terms([Other|Terms], TopCases, SkipList, Config) ->
- io:format("** WARNING: Spec file contains unknown directive ~p\n",
+ io:format("** WARNING: Spec file contains unknown directive ~tp\n",
[Other]),
do_spec_terms(Terms, TopCases, SkipList, Config).
@@ -1503,7 +1503,7 @@ do_test_cases(TopCases, SkipCases,
FwMod = get_fw_mod(?MODULE),
case collect_all_cases(TopCases, SkipCases) of
{error,Why} ->
- print(1, "Error starting: ~p", [Why]),
+ print(1, "Error starting: ~tp", [Why]),
exit(test_suites_done);
TestSpec0 ->
N = case remove_conf(TestSpec0) of
@@ -1762,18 +1762,14 @@ make_html_link(LinkName, Target, Explanation) ->
start_minor_log_file(Mod, Func, ParallelTC) ->
MFA = {Mod,Func,1},
LogDir = get(test_server_log_dir_base),
- Name0 = lists:flatten(io_lib:format("~w.~w~ts", [Mod,Func,?html_ext])),
- Name = downcase(Name0),
+ Name = minor_log_file_name(Mod,Func),
AbsName = filename:join(LogDir, Name),
case (ParallelTC orelse (element(1,file:read_file_info(AbsName))==ok)) of
false -> %% normal case, unique name
start_minor_log_file1(Mod, Func, LogDir, AbsName, MFA);
true -> %% special case, duplicate names
Tag = test_server_sup:unique_name(),
- Name1_0 =
- lists:flatten(io_lib:format("~w.~w.~ts~ts", [Mod,Func,Tag,
- ?html_ext])),
- Name1 = downcase(Name1_0),
+ Name1 = minor_log_file_name(Mod,Func,[$.|Tag]),
AbsName1 = filename:join(LogDir, Name1),
start_minor_log_file1(Mod, Func, LogDir, AbsName1, MFA)
end.
@@ -1784,7 +1780,7 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName, MFA) ->
put(test_server_minor_fd, Fd),
test_server_gl:set_minor_fd(group_leader(), Fd, MFA),
- TestDescr = io_lib:format("Test ~w:~w result", [Mod,Func]),
+ TestDescr = io_lib:format("Test ~w:~tw result", [Mod,Func]),
{Header,Footer} =
case test_server_sup:framework_call(get_html_wrapper,
[TestDescr,false,
@@ -1825,13 +1821,13 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName, MFA) ->
lists:member(no_src, get(test_server_logopts))} of
{true,false} ->
print(Lev, ["$tc_html",
- Info ++ "<a href=\"~ts#~ts\">~w:~w/~w</a> "
+ Info ++ "<a href=\"~ts#~ts\">~w:~tw/~w</a> "
"(click for source code)\n"],
[uri_encode(SrcListing),
uri_encode(atom_to_list(Func)++"-1",utf8),
Mod,Func,Arity]);
_ ->
- print(Lev, ["$tc_html",Info ++ "~w:~w/~w\n"], [Mod,Func,Arity])
+ print(Lev, ["$tc_html",Info ++ "~w:~tw/~w\n"], [Mod,Func,Arity])
end
end,
@@ -1845,6 +1841,19 @@ stop_minor_log_file() ->
ok = file:close(Fd),
put(test_server_minor_fd, undefined).
+minor_log_file_name(Mod,Func) ->
+ minor_log_file_name(Mod,Func,"").
+minor_log_file_name(Mod,Func,Tag) ->
+ Name =
+ downcase(
+ lists:flatten(
+ io_lib:format("~w.~tw~s~s", [Mod,Func,Tag,?html_ext]))),
+ Ok = file:native_name_encoding()==utf8
+ orelse io_lib:printable_latin1_list(Name),
+ if Ok -> Name;
+ true -> exit({error,unicode_name_on_latin1_file_system})
+ end.
+
downcase(S) -> downcase(S, []).
downcase([Uc|Rest], Result) when $A =< Uc, Uc =< $Z ->
downcase(Rest, [Uc-$A+$a|Result]);
@@ -2736,7 +2745,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
TimetrapData, Mode, Status2);
Bad ->
print(minor,
- "~n*** ~w returned bad elements in Config: ~p.~n",
+ "~n*** ~tw returned bad elements in Config: ~tp.~n",
[Func,Bad]),
Reason = {failed,{Mod,init_per_suite,bad_return}},
Cases2 = skip_cases_upto(Ref, Cases, Reason, conf, CurrMode,
@@ -2752,9 +2761,9 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
stop_minor_log_file(),
run_test_cases_loop(Cases, [NewCfg|Config], TimetrapData, Mode, Status2);
{_,{framework_error,{FwMod,FwFunc},Reason},_} ->
- print(minor, "~n*** ~w failed in ~w. Reason: ~p~n",
+ print(minor, "~n*** ~w failed in ~tw. Reason: ~tp~n",
[FwMod,FwFunc,Reason]),
- print(1, "~w failed in ~w. Reason: ~p~n", [FwMod,FwFunc,Reason]),
+ print(1, "~w failed in ~tw. Reason: ~tp~n", [FwMod,FwFunc,Reason]),
exit(framework_error);
{_,Fail,_} when element(1,Fail) == 'EXIT';
element(1,Fail) == timetrap_timeout;
@@ -2763,7 +2772,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
{Cases2,Config1,Status3} =
if StartConf ->
ReportAbortRepeat(failed),
- print(minor, "~n*** ~w failed.~n"
+ print(minor, "~n*** ~tw failed.~n"
" Skipping all cases.", [Func]),
Reason = {failed,{Mod,Func,Fail}},
{skip_cases_upto(Ref, Cases, Reason, conf, CurrMode,
@@ -2786,7 +2795,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
{Cases2,Config1,Status3} =
if StartConf ->
ReportAbortRepeat(auto_skipped),
- print(minor, "~n*** ~w auto skipped.~n"
+ print(minor, "~n*** ~tw auto skipped.~n"
" Skipping all cases.", [Func]),
{skip_cases_upto(Ref, Cases, SkipReason, conf, CurrMode,
auto_skip_case),
@@ -2803,7 +2812,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
{_,{Skip,Reason},_} when StartConf and ((Skip==skip) or (Skip==skipped)) ->
ReportAbortRepeat(skipped),
- print(minor, "~n*** ~w skipped.~n"
+ print(minor, "~n*** ~tw skipped.~n"
" Skipping all cases.", [Func]),
set_io_buffering(IOHandler),
stop_minor_log_file(),
@@ -2813,7 +2822,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
delete_status(Ref, Status2));
{_,{skip_and_save,Reason,_SavedConfig},_} when StartConf ->
ReportAbortRepeat(skipped),
- print(minor, "~n*** ~w skipped.~n"
+ print(minor, "~n*** ~tw skipped.~n"
" Skipping all cases.", [Func]),
set_io_buffering(IOHandler),
stop_minor_log_file(),
@@ -2878,7 +2887,7 @@ run_test_cases_loop([{make,Ref,{Mod,Func,Args}}|Cases0], Config, TimetrapData,
Mode, Status) ->
case run_test_case(Ref, 0, Mod, Func, Args, skip_init, TimetrapData) of
{_,Why={'EXIT',_},_} ->
- print(minor, "~n*** ~w failed.~n"
+ print(minor, "~n*** ~tw failed.~n"
" Skipping all cases.", [Func]),
Reason = {failed,{Mod,Func,Why}},
Cases = skip_cases_upto(Ref, Cases0, Reason, conf, Mode,
@@ -2932,9 +2941,9 @@ run_test_cases_loop([{Mod,Func,Args}|Cases], Config, TimetrapData, Mode, Status)
RunInit, TimetrapData, Mode) of
%% callback to framework module failed, exit immediately
{_,{framework_error,{FwMod,FwFunc},Reason},_} ->
- print(minor, "~n*** ~w failed in ~w. Reason: ~p~n",
+ print(minor, "~n*** ~w failed in ~tw. Reason: ~tp~n",
[FwMod,FwFunc,Reason]),
- print(1, "~w failed in ~w. Reason: ~p~n", [FwMod,FwFunc,Reason]),
+ print(1, "~w failed in ~tw. Reason: ~tp~n", [FwMod,FwFunc,Reason]),
stop_minor_log_file(),
exit(framework_error);
%% sequential execution of test case finished
@@ -2965,7 +2974,7 @@ run_test_cases_loop([{Mod,Func,Args}|Cases], Config, TimetrapData, Mode, Status)
stop_minor_log_file(),
run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status1);
true -> % skip rest of cases in sequence
- print(minor, "~n*** ~w failed.~n"
+ print(minor, "~n*** ~tw failed.~n"
" Skipping all other cases in sequence.",
[Func]),
Reason = {failed,{Mod,Func}},
@@ -3105,8 +3114,8 @@ print_conf_time(ConfTime) ->
print_props([]) ->
ok;
print_props(Props) ->
- print(major, "=group_props ~p", [Props]),
- print(minor, "Group properties: ~p~n", [Props]).
+ print(major, "=group_props ~tp", [Props]),
+ print(minor, "Group properties: ~tp~n", [Props]).
%% repeat N times: {repeat,N}
%% repeat N times or until all successful: {repeat_until_all_ok,N}
@@ -3253,13 +3262,13 @@ skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) ->
ResultCol = if Type == auto -> ?auto_skip_color;
Type == user -> ?user_skip_color
end,
- print(major, "~n=case ~w:~w", [Mod,Func]),
+ print(major, "~n=case ~w:~tw", [Mod,Func]),
GroupName = case get_name(Mode) of
undefined ->
"";
GrName ->
GrName1 = cast_to_list(GrName),
- print(major, "=group_props ~p", [[{name,GrName1}]]),
+ print(major, "=group_props ~tp", [[{name,GrName1}]]),
GrName1
end,
print(major, "=started ~s", [lists:flatten(timestamp_get(""))]),
@@ -3270,9 +3279,9 @@ skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) ->
print(major, "=result skipped: ~ts", [Comment1])
end,
if CaseNum == 0 ->
- print(2,"*** Skipping ~w ***", [{Mod,Func}]);
+ print(2,"*** Skipping ~tw ***", [{Mod,Func}]);
true ->
- print(2,"*** Skipping test case #~w ~w ***", [CaseNum,{Mod,Func}])
+ print(2,"*** Skipping test case #~w ~tw ***", [CaseNum,{Mod,Func}])
end,
TR = xhtml("<tr valign=\"top\">", ["<tr class=\"",odd_or_even(),"\">"]),
GroupName = case get_name(Mode) of
@@ -3283,7 +3292,7 @@ skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) ->
TR ++ "<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "~w" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>"
- "<td>" ++ Col0 ++ "~w" ++ Col1 ++ "</td>"
+ "<td>" ++ Col0 ++ "~tw" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "< >" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "0.000s" ++ Col1 ++ "</td>"
"<td><font color=\"~ts\">SKIPPED</font></td>"
@@ -3504,7 +3513,7 @@ wait_and_resend(Ref, [{_,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, Ok,Skip,Fail) ->
{'EXIT',CurrPid,Reason} when Reason /= normal ->
%% unexpected termination of test case process
{value,{_,_,CaseNum,Mod,Func}} = lists:keysearch(CurrPid, 2, Cases),
- print(1, "Error! Process for test case #~w (~w:~w) died! Reason: ~p",
+ print(1, "Error! Process for test case #~w (~w:~tw) died! Reason: ~tp",
[CaseNum, Mod, Func, Reason]),
exit({unexpected_termination,{CaseNum,Mod,Func},{CurrPid,Reason}})
end;
@@ -3643,7 +3652,7 @@ handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases) ->
{'EXIT',TCPid,Reason} when Reason /= normal ->
test_server_io:print_buffered(CurrPid),
{value,{_,_,Num,M,F}} = lists:keysearch(TCPid, 2, Cases),
- print(1, "Error! Process for test case #~w (~w:~w) died! Reason: ~p",
+ print(1, "Error! Process for test case #~w (~w:~tw) died! Reason: ~tp",
[Num, M, F, Reason]),
exit({unexpected_termination,{Num,M,F},{TCPid,Reason}})
end.
@@ -3716,7 +3725,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
end,
TSDir = get(test_server_dir),
- print(major, "=case ~w:~w", [Mod, Func]),
+ print(major, "=case ~w:~tw", [Mod, Func]),
MinorName = start_minor_log_file(Mod, Func, self() /= Main),
MinorBase = filename:basename(MinorName),
print(major, "=logfile ~ts", [filename:basename(MinorName)]),
@@ -3778,7 +3787,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
print(html, TR ++ "<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "~w" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>"
- "<td><a href=\"~ts\">~w</a></td>"
+ "<td><a href=\"~ts\">~tw</a></td>"
"<td><a href=\"~ts#top\">&lt;</a> <a href=\"~ts#end\">&gt;</a></td>",
[num2str(Num),fw_name(Mod),GrNameStr,EncMinorBase,Func,
EncMinorBase,EncMinorBase]),
@@ -3894,13 +3903,13 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
{'EXIT',_} = Exit ->
print(minor,
"WARNING: There might be slavenodes left in the"
- " system. I tried to kill them, but I failed: ~p\n",
+ " system. I tried to kill them, but I failed: ~tp\n",
[Exit]);
[] -> ok;
List ->
print(minor, "WARNING: ~w slave nodes in system after test"++
"case. Tried to killed them.~n"++
- " Names:~p",
+ " Names:~tp",
[length(List),List])
end;
false ->
@@ -3960,7 +3969,7 @@ progress(skip, CaseNum, Mod, Func, GrName, Loc, Reason, Time,
if_auto_skip(Reason,
fun() -> {?auto_skip_color,auto_skip,auto_skipped} end,
fun() -> {?user_skip_color,skip,skipped} end),
- print(major, "=result ~w: ~p", [ReportTag,Reason1]),
+ print(major, "=result ~w: ~tp", [ReportTag,Reason1]),
print(1, "*** SKIPPED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},
@@ -3993,7 +4002,7 @@ progress(skip, CaseNum, Mod, Func, GrName, Loc, Reason, Time,
progress(failed, CaseNum, Mod, Func, GrName, Loc, timetrap_timeout, T,
Comment0, {St0,St1}) ->
- print(major, "=result failed: timeout, ~p", [Loc]),
+ print(major, "=result failed: timeout, ~tp", [Loc]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report,
@@ -4019,7 +4028,7 @@ progress(failed, CaseNum, Mod, Func, GrName, Loc, timetrap_timeout, T,
progress(failed, CaseNum, Mod, Func, GrName, Loc, {testcase_aborted,Reason}, _T,
Comment0, {St0,St1}) ->
- print(major, "=result failed: testcase_aborted, ~p", [Loc]),
+ print(major, "=result failed: testcase_aborted, ~tp", [Loc]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report,
@@ -4041,14 +4050,14 @@ progress(failed, CaseNum, Mod, Func, GrName, Loc, {testcase_aborted,Reason}, _T,
FormatLoc = test_server_sup:format_loc(Loc),
print(minor, "=== Location: ~ts", [FormatLoc]),
print(minor,
- escape_chars(io_lib:format("=== Reason: {testcase_aborted,~p}",
+ escape_chars(io_lib:format("=== Reason: {testcase_aborted,~tp}",
[Reason])),
[]),
failed;
progress(failed, CaseNum, Mod, Func, GrName, unknown, Reason, Time,
Comment0, {St0,St1}) ->
- print(major, "=result failed: ~p, ~w", [Reason,unknown_location]),
+ print(major, "=result failed: ~tp, ~w", [Reason,unknown_location]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},
@@ -4056,7 +4065,7 @@ progress(failed, CaseNum, Mod, Func, GrName, unknown, Reason, Time,
TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
true -> "~w"
end, [Time]),
- ErrorReason = escape_chars(lists:flatten(io_lib:format("~p", [Reason]))),
+ ErrorReason = escape_chars(lists:flatten(io_lib:format("~tp", [Reason]))),
ErrorReason1 = lists:flatten([string:strip(S,left) ||
S <- string:tokens(ErrorReason,[$\n])]),
ErrorReason2 =
@@ -4093,7 +4102,7 @@ progress(failed, CaseNum, Mod, Func, GrName, Loc, Reason, Time,
end;
true -> {Loc,Loc}
end,
- print(major, "=result failed: ~p, ~p", [Reason,LocMaj]),
+ print(major, "=result failed: ~tp, ~tp", [Reason,LocMaj]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},
@@ -4236,7 +4245,7 @@ update_skip_counters(Pat, {US,AS}) ->
Result.
get_info_str(Mod,Func, 0, _Cases) ->
- io_lib:format("~w", [{Mod,Func}]);
+ io_lib:format("~tw", [{Mod,Func}]);
get_info_str(_Mod,_Func, CaseNum, unknown) ->
"test case " ++ integer_to_list(CaseNum);
get_info_str(_Mod,_Func, CaseNum, Cases) ->
@@ -4251,11 +4260,11 @@ print_if_known(Known, {SK,AK}, {SU,AU}) ->
to_string(Term) when is_list(Term) ->
case (catch io_lib:format("~ts", [Term])) of
- {'EXIT',_} -> lists:flatten(io_lib:format("~p", [Term]));
+ {'EXIT',_} -> lists:flatten(io_lib:format("~tp", [Term]));
String -> lists:flatten(String)
end;
to_string(Term) ->
- lists:flatten(io_lib:format("~p", [Term])).
+ lists:flatten(io_lib:format("~tp", [Term])).
get_last_loc(Loc) when is_tuple(Loc) ->
Loc;
@@ -4327,14 +4336,14 @@ format_exception(Reason={_Error,Stack}) when is_list(Stack) ->
undefined ->
case application:get_env(test_server, format_exception) of
{ok,false} ->
- {"~p",Reason};
+ {"~tp",Reason};
_ ->
do_format_exception(Reason)
end;
FW ->
case application:get_env(FW, format_exception) of
{ok,false} ->
- {"~p",Reason};
+ {"~tp",Reason};
_ ->
do_format_exception(Reason)
end
@@ -4345,13 +4354,13 @@ format_exception(Error) ->
do_format_exception(Reason={Error,Stack}) ->
StackFun = fun(_, _, _) -> false end,
PF = fun(Term, I) ->
- io_lib:format("~." ++ integer_to_list(I) ++ "p", [Term])
+ io_lib:format("~." ++ integer_to_list(I) ++ "tp", [Term])
end,
- case catch lib:format_exception(1, error, Error, Stack, StackFun, PF) of
- {'EXIT',_} ->
- {"~p",Reason};
+ case catch lib:format_exception(1, error, Error, Stack, StackFun, PF, utf8) of
+ {'EXIT',_R} ->
+ {"~tp",Reason};
Formatted ->
- Formatted1 = re:replace(Formatted, "exception error: ", "", [{return,list}]),
+ Formatted1 = re:replace(Formatted, "exception error: ", "", [{return,list},unicode]),
{"~ts",lists:flatten(Formatted1)}
end.
@@ -4457,7 +4466,7 @@ format(Detail, Format, Args) ->
Str =
case catch io_lib:format(Format, Args) of
{'EXIT',_} ->
- io_lib:format("illegal format; ~p with args ~p.\n",
+ io_lib:format("illegal format; ~tp with args ~tp.\n",
[Format,Args]);
Valid -> Valid
end,
@@ -4853,7 +4862,7 @@ collect_files(Dir, Pattern, St, Mode) ->
Wc = filename:join([Dir1,Pattern++"{.erl,"++code:objfile_extension()++"}"]),
case catch filelib:wildcard(Wc) of
{'EXIT', Reason} ->
- io:format("Could not collect files: ~p~n", [Reason]),
+ io:format("Could not collect files: ~tp~n", [Reason]),
{error,{collect_fail,Dir,Pattern}};
Files ->
%% convert to module names and remove duplicates
@@ -4897,13 +4906,13 @@ check_deny_req({Req,Val}, DenyList) ->
%%io:format("ValCheck ~p=~p in ~p\n", [Req,Val,DenyList]),
case lists:keysearch(Req, 1, DenyList) of
{value,{_Req,DenyVal}} when Val >= DenyVal ->
- {denied,io_lib:format("Requirement ~p=~p", [Req,Val])};
+ {denied,io_lib:format("Requirement ~tp=~tp", [Req,Val])};
_ ->
check_deny_req(Req, DenyList)
end;
check_deny_req(Req, DenyList) ->
case lists:member(Req, DenyList) of
- true -> {denied,io_lib:format("Requirement ~p", [Req])};
+ true -> {denied,io_lib:format("Requirement ~tp", [Req])};
false -> granted
end.
@@ -5004,7 +5013,7 @@ get_target_info() ->
start_node(Name, Type, Options) ->
T = 10 * ?ACCEPT_TIMEOUT * test_server:timetrap_scale_factor(),
- format(minor, "Attempt to start ~w node ~p with options ~p",
+ format(minor, "Attempt to start ~w node ~tp with options ~tp",
[Type, Name, Options]),
case controller_call({start_node,Name,Type,Options}, T) of
{{ok,Nodename}, Host, Cmd, Info, Warning} ->
@@ -5026,16 +5035,16 @@ start_node(Name, Type, Options) ->
{fail,{Ret, Host, Cmd}} ->
format(minor,
"Failed to start node ~tp on ~tp with command: ~ts~n"
- "Reason: ~p",
+ "Reason: ~tp",
[Name, Host, Cmd, Ret]),
{fail,Ret};
{Ret, undefined, undefined} ->
- format(minor, "Failed to start node ~tp: ~p", [Name,Ret]),
+ format(minor, "Failed to start node ~tp: ~tp", [Name,Ret]),
Ret;
{Ret, Host, Cmd} ->
format(minor,
"Failed to start node ~tp on ~tp with command: ~ts~n"
- "Reason: ~p",
+ "Reason: ~tp",
[Name, Host, Cmd, Ret]),
Ret
end.
@@ -5134,8 +5143,8 @@ display_info([Pid|T], R, M) ->
Reds = fetch(reductions, Info),
LM = length(fetch(messages, Info)),
pformat(io_lib:format("~w", [Pid]),
- io_lib:format("~w", [Call]),
- io_lib:format("~w", [Curr]), Reds, LM),
+ io_lib:format("~tw", [Call]),
+ io_lib:format("~tw", [Curr]), Reds, LM),
display_info(T, R+Reds, M + LM)
end;
display_info([], R, M) ->
@@ -5249,11 +5258,11 @@ read_cover_file(CoverFile) ->
case check_cover_file(List, [], [], []) of
{ok,Exclude,Include,Cross} -> {Exclude,Include,Cross};
error ->
- io:fwrite("Faulty format of CoverFile ~p\n", [CoverFile]),
+ io:fwrite("Faulty format of CoverFile ~tp\n", [CoverFile]),
{[],[],[]}
end;
{error,Reason} ->
- io:fwrite("Can't read CoverFile ~ts\nReason: ~p\n",
+ io:fwrite("Can't read CoverFile ~ts\nReason: ~tp\n",
[CoverFile,Reason]),
{[],[],[]}
end.
@@ -5521,8 +5530,8 @@ write_coverlog_header(CoverLog) ->
case catch io:put_chars(CoverLog,html_header("Coverage results")) of
{'EXIT',Reason} ->
io:format("\n\nERROR: Could not write normal heading in coverlog.\n"
- "CoverLog: ~w\n"
- "Reason: ~p\n",
+ "CoverLog: ~tw\n"
+ "Reason: ~tp\n",
[CoverLog,Reason]),
io:format(CoverLog,"<html><body>\n", []);
_ ->
diff --git a/lib/common_test/src/test_server_gl.erl b/lib/common_test/src/test_server_gl.erl
index 4845b86dd3..ce7682d101 100644
--- a/lib/common_test/src/test_server_gl.erl
+++ b/lib/common_test/src/test_server_gl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-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.
@@ -173,8 +173,8 @@ handle_info({'DOWN',Ref,process,_,Reason}=D, #st{minor_monitor=Ref}=St) ->
case Reason of
normal -> ok;
_ ->
- Data = io_lib:format("=== WARNING === TC: ~w\n"
- "Got down from minor Fd ~w: ~w\n\n",
+ Data = io_lib:format("=== WARNING === TC: ~tw\n"
+ "Got down from minor Fd ~w: ~tw\n\n",
[St#st.tc,St#st.minor,D]),
test_server_io:print_unexpected(Data)
end,
@@ -319,7 +319,7 @@ output(Level, Str, Sender, From, St) when is_atom(Level) ->
output_to_file(Level, dress_output(Str, Sender, St), From, St).
output_to_file(minor, Data0, From, #st{tc={M,F,A},minor=none}) ->
- Data = [io_lib:format("=== ~w:~w/~w\n", [M,F,A]),Data0],
+ Data = [io_lib:format("=== ~w:~tw/~w\n", [M,F,A]),Data0],
test_server_io:print(From, unexpected_io, Data),
ok;
output_to_file(minor, Data, From, #st{tc=TC,minor=Fd}) ->
@@ -328,10 +328,10 @@ output_to_file(minor, Data, From, #st{tc=TC,minor=Fd}) ->
catch
Type:Reason ->
Data1 =
- [io_lib:format("=== ERROR === TC: ~w\n"
+ [io_lib:format("=== ERROR === TC: ~tw\n"
"Failed to write to minor Fd: ~w\n"
"Type: ~w\n"
- "Reason: ~w\n",
+ "Reason: ~tw\n",
[TC,Fd,Type,Reason]),
Data,"\n"],
test_server_io:print(From, unexpected_io, Data1)
diff --git a/lib/common_test/src/test_server_io.erl b/lib/common_test/src/test_server_io.erl
index fdabf17b08..062e3bd8ff 100644
--- a/lib/common_test/src/test_server_io.erl
+++ b/lib/common_test/src/test_server_io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-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.
@@ -359,7 +359,7 @@ handle_info(kill_group_leaders, #st{gls=Gls,stopping=From,
end, St#st{phase=idle,pending_ops=[]}, Ops),
{noreply,St1};
handle_info(Other, St) ->
- io:format("Ignoring: ~p\n", [Other]),
+ io:format("Ignoring: ~tp\n", [Other]),
{noreply,St}.
terminate(_, _) ->
@@ -395,7 +395,7 @@ do_output(Tag, Str, Phase, #st{fds=Fds}=St) ->
none when Phase /= started ->
buffer;
none ->
- S = io_lib:format("\n*** ERROR: ~w, line ~w: No known '~p' log file\n",
+ S = io_lib:format("\n*** ERROR: ~w, line ~w: No known '~tp' log file\n",
[?MODULE,?LINE,Tag]),
do_output(stdout, [S,Str], Phase, St);
{value,Fd} ->
@@ -407,7 +407,7 @@ do_output(Tag, Str, Phase, #st{fds=Fds}=St) ->
end
catch _:Error ->
S = io_lib:format("\n*** ERROR: ~w, line ~w: Error writing to "
- "log file '~p': ~p\n",
+ "log file '~tp': ~tp\n",
[?MODULE,?LINE,Tag,Error]),
do_output(stdout, [S,Str], Phase, St)
end
diff --git a/lib/common_test/src/test_server_node.erl b/lib/common_test/src/test_server_node.erl
index 32d7d14a1b..a18ff1fd62 100644
--- a/lib/common_test/src/test_server_node.erl
+++ b/lib/common_test/src/test_server_node.erl
@@ -237,23 +237,23 @@ print_trc(Out,{trace_ts,P,call,{M,F,A},C,Ts},N) ->
io:format(Out,
"~w: ~s~n"
"Process : ~w~n"
- "Call : ~w:~w/~w~n"
- "Arguments : ~p~n"
- "Caller : ~w~n~n",
+ "Call : ~w:~tw/~w~n"
+ "Arguments : ~tp~n"
+ "Caller : ~tw~n~n",
[N,ts(Ts),P,M,F,length(A),A,C]);
print_trc(Out,{trace_ts,P,call,{M,F,A},Ts},N) ->
io:format(Out,
"~w: ~s~n"
"Process : ~w~n"
- "Call : ~w:~w/~w~n"
- "Arguments : ~p~n~n",
+ "Call : ~w:~tw/~w~n"
+ "Arguments : ~tp~n~n",
[N,ts(Ts),P,M,F,length(A),A]);
print_trc(Out,{trace_ts,P,return_from,{M,F,A},R,Ts},N) ->
io:format(Out,
"~w: ~s~n"
"Process : ~w~n"
- "Return from : ~w:~w/~w~n"
- "Return value : ~p~n~n",
+ "Return from : ~w:~tw/~w~n"
+ "Return value : ~tp~n~n",
[N,ts(Ts),P,M,F,A,R]);
print_trc(Out,{drop,X},N) ->
io:format(Out,
@@ -263,7 +263,7 @@ print_trc(Out,Trace,N) ->
Ts = element(size(Trace),Trace),
io:format(Out,
"~w: ~s~n"
- "Trace : ~p~n~n",
+ "Trace : ~tp~n~n",
[N,ts(Ts),Trace]).
ts({_, _, Micro} = Now) ->
{{Y,M,D},{H,Min,S}} = calendar:now_to_local_time(Now),
@@ -580,7 +580,7 @@ kill_node(SI) ->
cast_to_list(X) when is_list(X) -> X;
cast_to_list(X) when is_atom(X) -> atom_to_list(X);
-cast_to_list(X) -> lists:flatten(io_lib:format("~w", [X])).
+cast_to_list(X) -> lists:flatten(io_lib:format("~tw", [X])).
%%% L contains elements of the forms
diff --git a/lib/common_test/src/test_server_sup.erl b/lib/common_test/src/test_server_sup.erl
index 6922e01fcc..9a26de4774 100644
--- a/lib/common_test/src/test_server_sup.erl
+++ b/lib/common_test/src/test_server_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1998-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.
@@ -83,7 +83,7 @@ kill_the_process(Pid, Timeout0, TruncTO, ReportTVal) ->
"Testcase process ~w not "
"responding to timetrap "
"timeout:~n"
- " ~p.~n"
+ " ~tp.~n"
"Killing testcase...~n",
[Pid, Trap]),
exit(Pid, kill)
@@ -144,11 +144,11 @@ call_crash(Time,Crash,M,F,A) ->
{'EXIT',Pid,_Reason} when Crash==any ->
ok;
{'EXIT',Reason} ->
- test_server:format(12, "Wrong crash reason. Wanted ~p, got ~p.",
+ test_server:format(12, "Wrong crash reason. Wanted ~tp, got ~tp.",
[Crash, Reason]),
exit({wrong_crash_reason,Reason});
{'EXIT',Pid,Reason} ->
- test_server:format(12, "Wrong crash reason. Wanted ~p, got ~p.",
+ test_server:format(12, "Wrong crash reason. Wanted ~tp, got ~tp.",
[Crash, Reason]),
exit({wrong_crash_reason,Reason});
{'EXIT',OtherPid,Reason} when OldTrapExit == false ->
@@ -334,11 +334,11 @@ do_appup_tests(_, _Application, Up, Down, Modules) ->
ok ->
test_server:format(minor, "OK~n");
Error ->
- test_server:format(minor, "ERROR ~p~n", [Error]),
+ test_server:format(minor, "ERROR ~tp~n", [Error]),
test_server:fail(Error)
end;
Error ->
- test_server:format(minor, "ERROR ~p~n", [Error]),
+ test_server:format(minor, "ERROR ~tp~n", [Error]),
test_server:fail(Error)
end.
@@ -557,7 +557,7 @@ check_dict(Dict, Reason) ->
[] ->
1; % All ok.
List ->
- io:format("** ~ts (~ts) ->~n~p~n",[Reason, Dict, List]),
+ io:format("** ~ts (~ts) ->~n~tp~n",[Reason, Dict, List]),
0
end.
@@ -566,7 +566,7 @@ check_dict_tolerant(Dict, Reason, Mode) ->
[] ->
1; % All ok.
List ->
- io:format("** ~ts (~ts) ->~n~p~n",[Reason, Dict, List]),
+ io:format("** ~ts (~ts) ->~n~tp~n",[Reason, Dict, List]),
case Mode of
pedantic ->
0;
@@ -646,7 +646,7 @@ append_files_to_logfile([File|Files]) ->
%% fail, but in that case it will throw an exception so that
%% we will be aware of the problem.
io:format(Fd, "Unable to write the crash dump "
- "to this file: ~p~n", [file:format_error(Error)])
+ "to this file: ~tp~n", [file:format_error(Error)])
end;
_Error ->
io:format(Fd, "Failed to read: ~ts\n", [File])
@@ -802,9 +802,9 @@ format_loc([{Mod,Func,Line}|Rest]) ->
format_loc([{Mod,LineOrFunc}]) ->
format_loc({Mod,LineOrFunc});
format_loc({Mod,Func}) when is_atom(Func) ->
- io_lib:format("{~w,~w}",[Mod,Func]);
+ io_lib:format("{~w,~tw}",[Mod,Func]);
format_loc(Loc) ->
- io_lib:format("~p",[Loc]).
+ io_lib:format("~tp",[Loc]).
format_loc1([{Mod,Func,Line}]) ->
[" ",format_loc1({Mod,Func,Line}),"]"];
@@ -824,12 +824,12 @@ format_loc1({Mod,Func,Line}) ->
true ->
Line
end,
- io_lib:format("{~w,~w,<a href=\"~ts~ts#~s\">~w</a>}",
+ io_lib:format("{~w,~tw,<a href=\"~ts~ts#~ts\">~tw</a>}",
[Mod,Func,
test_server_ctrl:uri_encode(downcase(ModStr)),
?src_listing_ext,Link,Line]);
_ ->
- io_lib:format("{~w,~w,~w}",[Mod,Func,Line])
+ io_lib:format("{~w,~tw,~tw}",[Mod,Func,Line])
end.
downcase(S) -> downcase(S, []).
diff --git a/lib/common_test/src/unix_telnet.erl b/lib/common_test/src/unix_telnet.erl
index 0f29b2dbb2..8ac467014c 100644
--- a/lib/common_test/src/unix_telnet.erl
+++ b/lib/common_test/src/unix_telnet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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.
@@ -132,25 +132,25 @@ connect1(Name,Ip,Port,Timeout,KeepAlive,TCPNoDelay,Username,Password) ->
Prompt=/=?password ->
{ok,Pid};
Error ->
- log(Name,recv,"Password failed\n~p\n",
+ log(Name,recv,"Password failed\n~tp\n",
[Error]),
{error,Error}
end;
Error ->
- log(Name,recv,"Login to ~p:~p failed\n~p\n",[Ip,Port,Error]),
+ log(Name,recv,"Login to ~p:~p failed\n~tp\n",[Ip,Port,Error]),
{error,Error}
end;
{ok,[{prompt,_OtherPrompt1},{prompt,_OtherPrompt2}],_} ->
{ok,Pid};
Error ->
log(Name,conn_error,
- "Did not get expected prompt from ~p:~p\n~p\n",
+ "Did not get expected prompt from ~p:~p\n~tp\n",
[Ip,Port,Error]),
{error,Error}
end;
Error ->
log(Name,conn_error,
- "Could not open telnet connection to ~p:~p\n~p\n",
+ "Could not open telnet connection to ~p:~p\n~tp\n",
[Ip,Port,Error]),
Error
end,
diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl
index f1c5051164..99a109cfe8 100644
--- a/lib/common_test/src/vts.erl
+++ b/lib/common_test/src/vts.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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.
@@ -250,7 +250,7 @@ loop(State) ->
{'EXIT',Pid,Reason} ->
case State#state.test_runner of
Pid ->
- io:format("Test run error: ~p\n",[Reason]),
+ io:format("Test run error: ~tp\n",[Reason]),
loop(State);
_ ->
loop(State)
@@ -551,7 +551,7 @@ case_select(Dir,Suite,Case,N) ->
true = code:add_pathz(Dir),
case catch apply(Suite,all,[]) of
{'EXIT',Reason} ->
- io:format("\n~p\n",[Reason]),
+ io:format("\n~tp\n",[Reason]),
red(["COULD NOT READ TESTCASES!!",br(),
"See erlang shell for info"]);
{skip,_Reason} ->
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 4f3e0e8266..0d9149f489 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -72,7 +72,8 @@ MODULES= \
ct_release_test_SUITE \
ct_log_SUITE \
ct_SUITE \
- ct_keep_logs_SUITE
+ ct_keep_logs_SUITE \
+ ct_unicode_SUITE
ERL_FILES= $(MODULES:%=%.erl)
HRL_FILES= test_server_test_lib.hrl
diff --git a/lib/common_test/test/ct_config_SUITE.erl b/lib/common_test/test/ct_config_SUITE.erl
index cbbfe408a8..250700741c 100644
--- a/lib/common_test/test/ct_config_SUITE.erl
+++ b/lib/common_test/test/ct_config_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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.
@@ -172,10 +172,10 @@ testspec_dynamic(Config) when is_list(Config) ->
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
make_spec(DataDir, ConfigDir, Filename, Suites, Config)->
- {ok, Fd} = file:open(filename:join(ConfigDir, Filename), [write]),
- ok = file:write(Fd,
- io_lib:format("{suites, \"~sconfig/test/\", ~p}.~n", [DataDir, Suites])),
- lists:foreach(fun(C)-> ok=file:write(Fd, io_lib:format("~p.~n", [C])) end, Config),
+ {ok, Fd} = file:open(filename:join(ConfigDir, Filename),
+ [write, {encoding,utf8}]),
+ ok = io:format(Fd,"{suites, \"~tsconfig/test/\", ~p}.~n", [DataDir, Suites]),
+ lists:foreach(fun(C)-> ok=io:format(Fd, "~tp.~n", [C]) end, Config),
ok = file:close(Fd).
run_test(Name, Config, CTConfig, SuiteNames)->
diff --git a/lib/common_test/test/ct_keep_logs_SUITE.erl b/lib/common_test/test/ct_keep_logs_SUITE.erl
index 6b7aaa57ac..83b7963d7d 100644
--- a/lib/common_test/test/ct_keep_logs_SUITE.erl
+++ b/lib/common_test/test/ct_keep_logs_SUITE.erl
@@ -72,7 +72,7 @@ keep_logs(Config) ->
LogDir=?config(logdir,Opts),
KeepLogsDir = create_dir(filename:join(LogDir,"keep_logs-")),
Opts1 = lists:keyreplace(logdir,1,Opts,{logdir,KeepLogsDir}),
- ct:log("New LogDir = ~s", [KeepLogsDir]),
+ ct:log("New LogDir = ~ts", [KeepLogsDir]),
%% Create 6 ct_run.* log directories
[ok = ct_test_support:run(Opts1, Config) || _ <- lists:seq(1,3)],
@@ -134,7 +134,7 @@ refresh_logs(Config) ->
LogDir=?config(logdir,Opts0),
KeepLogsDir = create_dir(filename:join(LogDir,"refresh_logs-")),
Opts1 = lists:keyreplace(logdir,1,Opts0,{logdir,KeepLogsDir}),
- ct:log("New LogDir = ~s", [KeepLogsDir]),
+ ct:log("New LogDir = ~ts", [KeepLogsDir]),
%% Create 6 ct_run.* log directories
SuiteOpts = [{suite,Suite},{label,refresh_logs} | Opts1],
diff --git a/lib/common_test/test/ct_netconfc_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE.erl
index 05edb45fe8..e8c7b65140 100644
--- a/lib/common_test/test/ct_netconfc_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
index 6a41f0a04c..586589ad40 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-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/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
index 3ce2d18c66..cbdb4cf11a 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2014-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/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
index c40bf9e2cc..63bf9be134 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-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/lib/common_test/test/ct_surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE.erl
index 832e105517..a71288fb12 100644
--- a/lib/common_test/test/ct_surefire_SUITE.erl
+++ b/lib/common_test/test/ct_surefire_SUITE.erl
@@ -304,7 +304,7 @@ test_events(Test) ->
check_xml(Case,XmlRe) ->
case filelib:wildcard(XmlRe) of
[] ->
- ct:fail("No xml files found with regexp ~p~n", [XmlRe]);
+ ct:fail("No xml files found with regexp ~tp~n", [XmlRe]);
[_] = Xmls when Case==absolute_path ->
do_check_xml(Case,Xmls);
[_,_] = Xmls ->
@@ -326,12 +326,12 @@ check_xml(Case,XmlRe) ->
%% ...
%% </testsuites>
do_check_xml(Case,[Xml|Xmls]) ->
- ct:log("Checking <a href=~p>~s</a>~n",[Xml,Xml]),
+ ct:log("Checking <a href=~tp>~ts</a>~n",[Xml,Xml]),
{E,_} = xmerl_scan:file(Xml),
Expected = events_to_result(lists:flatten(test_events(Case))),
ParseResult = testsuites(Case,E),
- ct:log("Expecting: ~p~n",[Expected]),
- ct:log("Actual : ~p~n",[ParseResult]),
+ ct:log("Expecting: ~tp~n",[Expected]),
+ ct:log("Actual : ~tp~n",[ParseResult]),
Expected = ParseResult,
do_check_xml(Case,Xmls);
do_check_xml(_,[]) ->
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index ba7aadfeec..44c27e54c2 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -67,7 +67,7 @@ init_per_suite(Config, Level) ->
end,
case delete_old_logs(os:type(), Config) of
{'EXIT',DelLogsReason} ->
- test_server:format(0, "Failed to delete old log directories: ~p~n",
+ test_server:format(0, "Failed to delete old log directories: ~tp~n",
[DelLogsReason]);
_ ->
ok
@@ -91,7 +91,8 @@ start_slave(NodeName, Config, Level) ->
[_,Host] = string:tokens(atom_to_list(node()), "@"),
test_server:format(0, "Trying to start ~s~n",
[atom_to_list(NodeName)++"@"++Host]),
- case slave:start(Host, NodeName, []) of
+ PR = proplists:get_value(printable_range,Config,io:printable_range()),
+ case slave:start(Host, NodeName, "+pc " ++ atom_to_list(PR)) of
{error,Reason} ->
test_server:fail(Reason);
{ok,CTNode} ->
@@ -119,7 +120,7 @@ start_slave(NodeName, Config, Level) ->
[true = rpc:call(CTNode, code, add_patha, [D]) || D <- PathDirs],
test_server:format(Level, "Dirs added to code path (on ~w):~n",
[CTNode]),
- [io:format("~s~n", [D]) || D <- PathDirs],
+ [io:format("~ts~n", [D]) || D <- PathDirs],
case proplists:get_value(start_sasl, Config) of
true ->
@@ -161,12 +162,12 @@ init_per_testcase(_TestCase, Config) ->
case lists:keysearch(master, 1, Config) of
false->
test_server:format("See Common Test logs here:\n\n"
- "<a href=\"file://~s/all_runs.html\">~s/all_runs.html</a>\n"
- "<a href=\"file://~s/index.html\">~s/index.html</a>",
+ "<a href=\"file://~ts/all_runs.html\">~ts/all_runs.html</a>\n"
+ "<a href=\"file://~ts/index.html\">~ts/index.html</a>",
[LogDir,LogDir,LogDir,LogDir]);
{value, _}->
test_server:format("See CT Master Test logs here:\n\n"
- "<a href=\"file://~s/master_runs.html\">~s/master_runs.html</a>",
+ "<a href=\"file://~ts/master_runs.html\">~ts/master_runs.html</a>",
[LogDir,LogDir])
end,
Config.
@@ -192,11 +193,11 @@ write_testspec(TestSpec, Dir, Name) ->
write_testspec(TestSpec, filename:join(Dir, Name)).
write_testspec(TestSpec, TSFile) ->
- {ok,Dev} = file:open(TSFile, [write]),
- [io:format(Dev, "~p.~n", [Entry]) || Entry <- TestSpec],
+ {ok,Dev} = file:open(TSFile, [write,{encoding,utf8}]),
+ [io:format(Dev, "~tp.~n", [Entry]) || Entry <- TestSpec],
file:close(Dev),
- io:format("Test specification written to: ~p~n", [TSFile]),
- io:format(user, "Test specification written to: ~p~n", [TSFile]),
+ io:format("Test specification written to: ~tp~n", [TSFile]),
+ io:format(user, "Test specification written to: ~tp~n", [TSFile]),
TSFile.
@@ -269,7 +270,7 @@ run(Opts0, Config) when is_list(Opts0) ->
Override =
fun(O={Key,_}, Os) ->
io:format(user, "ADDING START "
- "OPTION: ~p~n", [O]),
+ "OPTION: ~tp~n", [O]),
[O | lists:keydelete(Key, 1, Os)]
end,
lists:foldl(Override, Opts0, OROpts);
@@ -291,14 +292,14 @@ run(Opts0, Config) when is_list(Opts0) ->
run_ct_run_test(Opts,Config) ->
CTNode = proplists:get_value(ct_node, Config),
Level = proplists:get_value(trace_level, Config),
- test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~p) on ~p~n",
+ test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~tp) on ~p~n",
[Opts, CTNode]),
T0 = erlang:monotonic_time(),
CtRunTestResult = rpc:call(CTNode, ct, run_test, [Opts]),
T1 = erlang:monotonic_time(),
Elapsed = erlang:convert_time_unit(T1-T0, native, milli_seconds),
- test_server:format(Level, "~n[RUN #1] Got return value ~p after ~p ms~n",
+ test_server:format(Level, "~n[RUN #1] Got return value ~tp after ~p ms~n",
[CtRunTestResult,Elapsed]),
case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of
undefined ->
@@ -316,7 +317,7 @@ run_ct_script_start(Opts, Config) ->
CTNode = proplists:get_value(ct_node, Config),
Level = proplists:get_value(trace_level, Config),
Opts1 = [{halt_with,{?MODULE,ct_test_halt}} | Opts],
- test_server:format(Level, "Saving start opts on ~p: ~p~n",
+ test_server:format(Level, "Saving start opts on ~p: ~tp~n",
[CTNode, Opts1]),
rpc:call(CTNode, application, set_env,
[common_test, run_test_start_opts, Opts1]),
@@ -326,7 +327,7 @@ run_ct_script_start(Opts, Config) ->
ExitStatus = rpc:call(CTNode, ct_run, script_start, []),
T1 = erlang:monotonic_time(),
Elapsed = erlang:convert_time_unit(T1-T0, native, milli_seconds),
- test_server:format(Level, "[RUN #2] Got exit status value ~p after ~p ms~n",
+ test_server:format(Level, "[RUN #2] Got exit status value ~tp after ~p ms~n",
[ExitStatus,Elapsed]),
ExitStatus.
@@ -372,12 +373,12 @@ run({M,F,A}, InitCalls, Config) ->
Level = proplists:get_value(trace_level, Config),
lists:foreach(
fun({IM,IF,IA}) ->
- test_server:format(Level, "~nInit call ~w:~w(~p) on ~p...~n",
+ test_server:format(Level, "~nInit call ~w:~tw(~tp) on ~p...~n",
[IM, IF, IA, CTNode]),
Result = rpc:call(CTNode, IM, IF, IA),
- test_server:format(Level, "~n...with result: ~p~n", [Result])
+ test_server:format(Level, "~n...with result: ~tp~n", [Result])
end, InitCalls),
- test_server:format(Level, "~nStarting test with ~w:~w(~p) on ~p~n",
+ test_server:format(Level, "~nStarting test with ~w:~tw(~tp) on ~p~n",
[M, F, A, CTNode]),
rpc:call(CTNode, M, F, A).
@@ -404,7 +405,7 @@ wait_for_ct_stop(Retries, CTNode) ->
Info = (catch process_info(Pid)),
test_server:format(0, "Waiting for CT (~p) to finish (~p)...",
[Pid,Retries]),
- test_server:format(0, "Process info for ~p:~n~p", [Info]),
+ test_server:format(0, "Process info for ~p:~n~tp", [Pid,Info]),
timer:sleep(5000),
wait_for_ct_stop(Retries-1, CTNode)
end.
@@ -414,7 +415,7 @@ wait_for_ct_stop(Retries, CTNode) ->
ct_rpc({M,F,A}, Config) ->
CTNode = proplists:get_value(ct_node, Config),
Level = proplists:get_value(trace_level, Config),
- test_server:format(Level, "~nCalling ~w:~w(~p) on ~p...",
+ test_server:format(Level, "~nCalling ~w:~tw(~tp) on ~p...",
[M,F,A, CTNode]),
rpc:call(CTNode, M, F, A).
@@ -530,7 +531,7 @@ verify_events(TEvs, Evs, Node, Config) ->
verify_events1([TestEv|_], [{TEH,#event{name=stop_logging,node=Node,data=_}}|_], Node, _)
when element(1,TestEv) == TEH, element(2,TestEv) =/= stop_logging ->
- test_server:format("Failed to find ~p in the list of events!~n", [TestEv]),
+ test_server:format("Failed to find ~tp in the list of events!~n", [TestEv]),
exit({event_not_found,TestEv});
verify_events1(TEvs = [TestEv | TestEvs], Evs = [_|Events], Node, Config) ->
@@ -538,8 +539,8 @@ verify_events1(TEvs = [TestEv | TestEvs], Evs = [_|Events], Node, Config) ->
nomatch ->
verify_events1(TEvs, Events, Node, Config);
{'EXIT',Reason} ->
- test_server:format("Failed to find ~p in ~p~n"
- "Reason: ~p~n", [TestEv,Evs,Reason]),
+ test_server:format("Failed to find ~tp in ~tp~n"
+ "Reason: ~tp~n", [TestEv,Evs,Reason]),
exit(Reason);
{Config1,Events1} ->
if is_list(TestEv) ->
@@ -547,13 +548,13 @@ verify_events1(TEvs = [TestEv | TestEvs], Evs = [_|Events], Node, Config) ->
element(1,TestEv) == parallel ; element(1,TestEv) == shuffle ->
ok;
true ->
- test_server:format("Found ~p!", [TestEv])
+ test_server:format("Found ~tp!", [TestEv])
end,
verify_events1(TestEvs, Events1, Node, Config1)
end;
verify_events1([TestEv|_], [], _, _) ->
- test_server:format("Failed to find ~p in the list of events!~n", [TestEv]),
+ test_server:format("Failed to find ~tp in the list of events!~n", [TestEv]),
exit({event_not_found,TestEv});
verify_events1([], Evs, _, Config) ->
@@ -586,8 +587,8 @@ locate(TEvs, Node, Evs, Config) when is_list(TEvs) ->
false ->
nomatch;
true ->
- test_server:format("Found ~p!", [InitStart]),
- test_server:format("Found ~p!", [InitDone]),
+ test_server:format("Found ~tp!", [InitStart]),
+ test_server:format("Found ~tp!", [InitDone]),
verify_events1(TEvs1, Evs1, Node, Config)
end;
_ ->
@@ -635,8 +636,8 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
true
end, Es),
- test_server:format("Found ~p!", [InitStart]),
- test_server:format("Found ~p!", [InitDone]),
+ test_server:format("Found ~tp!", [InitStart]),
+ test_server:format("Found ~tp!", [InitDone]),
{TEs,EvsG};
_ ->
nomatch
@@ -688,10 +689,10 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
[] when Evs2 == [] ->
exit({unmatched,TEv});
[] ->
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
exit({tc_done_not_found,TEv});
[TcDone|Evs3] ->
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
RemSize1 = length(Evs3),
if RemSize1 < RemSize ->
{[TcDone|Done],Evs3,RemSize1};
@@ -707,7 +708,7 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
EH == TEH, EvNode == Node, Mod == M,
Func == F, result_match(R, Result)] of
[TcDone|_] ->
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
{lists:delete(TcDone, Done),RemEvs,RemSize};
[] ->
exit({unmatched,TEv})
@@ -735,7 +736,7 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
[] ->
exit({end_per_group_not_found,TEv});
[_ | RemEvs2] ->
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
{Done,RemEvs2,length(RemEvs2)}
end;
%% tc_done event for end_per_group
@@ -766,7 +767,7 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
[] ->
exit({end_per_group_not_found,TEv});
[_ | RemEvs2] ->
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
{Done,RemEvs2,length(RemEvs2)}
end;
%% end_per_group auto- or user skipped
@@ -813,7 +814,7 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
[] ->
exit({unmatched,TEv});
_ ->
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
Acc
end;
%% start of a sub-group
@@ -866,8 +867,8 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
_ ->
Props = EvProps
end,
- test_server:format("Found ~p!", [InitStart]),
- test_server:format("Found ~p!", [InitDone]),
+ test_server:format("Found ~tp!", [InitStart]),
+ test_server:format("Found ~tp!", [InitDone]),
{TEs,Es};
false ->
nomatch
@@ -908,7 +909,7 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
[_TcStart, TcDone={TEH,#event{name=tc_done,
node=Node,
data={M,F,_}}} | Evs3] ->
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
RemSize1 = length(Evs3),
if RemSize1 < RemSize ->
{[TcDone|Done],Evs3,RemSize1};
@@ -924,7 +925,7 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
EH == TEH, EvNode == Node, Mod == M,
Func == F, result_match(R, Result)] of
[TcDone|_] ->
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
{lists:delete(TcDone, Done),RemEvs,RemSize};
[] ->
exit({unmatched,TEv})
@@ -965,7 +966,7 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
_ ->
Props = EvProps1
end,
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
{Done,RemEvs2,length(RemEvs2)}
end;
%% tc_done event for end_per_group
@@ -1009,7 +1010,7 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
_ ->
Props = EvProps1
end,
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
{Done,RemEvs2,length(RemEvs2)}
end;
%% end_per_group auto-or user skipped
@@ -1050,7 +1051,7 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
[] ->
exit({unmatched,TEv});
_ ->
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
Acc
end;
(TEv={TEH,N,D}, Acc) ->
@@ -1061,7 +1062,7 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
[] ->
exit({unmatched,TEv});
_ ->
- test_server:format("Found ~p!", [TEv]),
+ test_server:format("Found ~tp!", [TEv]),
Acc
end;
%% start of a sub-group
@@ -1232,54 +1233,54 @@ result_match(_, _) ->
log_events(TC, Events, EvLogDir, Opts) ->
LogFile = filename:join(EvLogDir, atom_to_list(TC)++".events"),
- {ok,Dev} = file:open(LogFile, [write]),
+ {ok,Dev} = file:open(LogFile, [write,{encoding,utf8}]),
io:format(Dev, "[~n", []),
log_events1(Events, Dev, " "),
file:close(Dev),
FullLogFile = join_abs_dirs(proplists:get_value(net_dir, Opts),
LogFile),
- ct:log("Events written to logfile: <a href=\"file://~s\">~s</a>~n",
+ ct:log("Events written to logfile: <a href=\"file://~ts\">~ts</a>~n",
[FullLogFile,FullLogFile],[no_css]),
- io:format(user, "Events written to logfile: ~p~n", [LogFile]).
+ io:format(user, "Events written to logfile: ~tp~n", [LogFile]).
log_events1(Evs, Dev, "") ->
log_events1(Evs, Dev, " ");
log_events1([E={_EH,tc_start,{_M,{init_per_group,_GrName,Props}}} | Evs], Dev, Ind) ->
case get_prop(Props) of
undefined ->
- io:format(Dev, "~s[~p,~n", [Ind,E]),
+ io:format(Dev, "~s[~tp,~n", [Ind,E]),
log_events1(Evs, Dev, Ind++" ");
Prop ->
- io:format(Dev, "~s{~w,~n~s[~p,~n", [Ind,Prop,Ind++" ",E]),
+ io:format(Dev, "~s{~w,~n~s[~tp,~n", [Ind,Prop,Ind++" ",E]),
log_events1(Evs, Dev, Ind++" ")
end;
log_events1([E={_EH,tc_done,{_M,{init_per_group,_GrName,_Props},_R}} | Evs], Dev, Ind) ->
- io:format(Dev, "~s~p,~n", [Ind,E]),
+ io:format(Dev, "~s~tp,~n", [Ind,E]),
log_events1(Evs, Dev, Ind++" ");
log_events1([E={_EH,tc_start,{_M,{end_per_group,_GrName,_Props}}} | Evs], Dev, Ind) ->
Ind1 = Ind -- " ",
- io:format(Dev, "~s~p,~n", [Ind1,E]),
+ io:format(Dev, "~s~tp,~n", [Ind1,E]),
log_events1(Evs, Dev, Ind1);
log_events1([E={_EH,tc_done,{_M,{end_per_group,_GrName,Props},_R}} | Evs], Dev, Ind) ->
case get_prop(Props) of
undefined ->
- io:format(Dev, "~s~p],~n", [Ind,E]),
+ io:format(Dev, "~s~tp],~n", [Ind,E]),
log_events1(Evs, Dev, Ind--" ");
_Prop ->
- io:format(Dev, "~s~p]},~n", [Ind,E]),
+ io:format(Dev, "~s~tp]},~n", [Ind,E]),
log_events1(Evs, Dev, Ind--" ")
end;
log_events1([E={_EH,tc_auto_skip,{_M,{end_per_group,_GrName},_Reason}} | Evs], Dev, Ind) ->
- io:format(Dev, "~s~p],~n", [Ind,E]),
+ io:format(Dev, "~s~tp],~n", [Ind,E]),
log_events1(Evs, Dev, Ind--" ");
log_events1([E={_EH,tc_user_skip,{_M,{end_per_group,_GrName},_Reason}} | Evs], Dev, Ind) ->
- io:format(Dev, "~s~p],~n", [Ind,E]),
+ io:format(Dev, "~s~tp],~n", [Ind,E]),
log_events1(Evs, Dev, Ind--" ");
log_events1([E], Dev, Ind) ->
- io:format(Dev, "~s~p~n].~n", [Ind,E]),
+ io:format(Dev, "~s~tp~n].~n", [Ind,E]),
ok;
log_events1([E | Evs], Dev, Ind) ->
- io:format(Dev, "~s~p,~n", [Ind,E]),
+ io:format(Dev, "~s~tp,~n", [Ind,E]),
log_events1(Evs, Dev, Ind);
log_events1([], _Dev, _Ind) ->
ok.
@@ -1393,10 +1394,10 @@ delete_dirs(LogDir) ->
delete_dirs(_, []) ->
ok;
delete_dirs(LogDir, [Dir | Dirs]) ->
- test_server:format(0, "Removing old log directory: ~s", [Dir]),
+ test_server:format(0, "Removing old log directory: ~ts", [Dir]),
case catch rm_rec(Dir) of
{_,Reason} ->
- test_server:format(0, "Delete failed! (~p)", [Reason]);
+ test_server:format(0, "Delete failed! (~tp)", [Reason]);
ok ->
ok
end,
diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl
index fca5ef3eb3..2d2c42999f 100644
--- a/lib/common_test/test/ct_testspec_1_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_1_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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.
@@ -616,7 +616,7 @@ setup_and_execute(TCName, TestSpec, Config) ->
FullSpecFile = ct_test_support:join_abs_dirs(?config(net_dir, Opts),
SpecFile),
- io:format("~nTest spec created here~n~n<a href=\"file://~s\">~s</a>~n",
+ io:format("~nTest spec created here~n~n<a href=\"file://~ts\">~ts</a>~n",
[FullSpecFile,FullSpecFile]),
ok = ct_test_support:run(Opts, Config),
@@ -638,8 +638,8 @@ setup_and_execute(TCName, TestSpec, Config) ->
create_spec_file(SpecDir, TCName, TestSpec) ->
FileName = filename:join(SpecDir,
atom_to_list(TCName)++".spec"),
- {ok,Dev} = file:open(FileName, [write]),
- [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec],
+ {ok,Dev} = file:open(FileName, [write,{encoding,utf8}]),
+ [io:format(Dev, "~tp.~n", [Term]) || Term <- TestSpec],
file:close(Dev),
FileName.
diff --git a/lib/common_test/test/ct_unicode_SUITE.erl b/lib/common_test/test/ct_unicode_SUITE.erl
new file mode 100644
index 0000000000..355503a5dc
--- /dev/null
+++ b/lib/common_test/test/ct_unicode_SUITE.erl
@@ -0,0 +1,218 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-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%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_unicode_SUITE
+%%%
+%%% Description:
+%%% Test that common_test handles and logs unicode strings and atoms
+%%% correctly.
+%%%
+%%% The suite used for the test is located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_unicode_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ case file:native_name_encoding() of
+ latin1 -> {skip,"Test is not applicable on latin1 file system"};
+ _ ->
+ ct_test_support:init_per_suite([{printable_range,unicode}|Config])
+ end.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [].
+
+all() ->
+ [unicode_atoms_SUITE,
+ unicode_spec].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+unicode_atoms_SUITE(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ run_test(unicode_atoms_SUITE,
+ [{dir,DataDir},{suite,unicode_atoms_SUITE}], Config).
+
+unicode_spec(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ CfgName = "unicode_αβ.cfg",
+ Cfg = io_lib:format("{~tw,[{~tw,\"~ts\"}]}.~n",
+ ['key_αβ','subkey_αβ',"value_αβ"]),
+ ok = file:write_file(filename:join(PrivDir,CfgName),
+ unicode:characters_to_binary(Cfg)),
+ TestSpec = [{cases, DataDir, unicode_atoms_SUITE, ['config_αβ']},
+ {config, PrivDir, CfgName}],
+ TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir,
+ "unicode_αβ.spec"),
+ run_test(unicode_spec,[{spec,TestSpecName}],Config).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+run_test(Label, Test, Config) ->
+ {Opts,ERPid} = setup_env([{label,Label}|Test], Config),
+ ok = ct_test_support:run(Opts, Config),
+ TestEvents = ct_test_support:get_events(ERPid, Config),
+ ct_test_support:log_events(Label,
+ reformat_events(TestEvents, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+ ExpEvents = events_to_check(Label),
+ ok = ct_test_support:verify_events(ExpEvents, TestEvents, Config),
+ check_logs([_,_]=get_log_dirs(TestEvents)).
+
+setup_env(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}} | Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat_events(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+get_log_dirs([{?eh,#event{name=start_logging,data=LogDir}}|Events]) ->
+ [LogDir|get_log_dirs(Events)];
+get_log_dirs([_|Events]) ->
+ get_log_dirs(Events);
+get_log_dirs([]) ->
+ [].
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(unicode_atoms_SUITE) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,6}},
+ {?eh,tc_start,{unicode_atoms_SUITE,init_per_suite}},
+ {?eh,tc_done,{unicode_atoms_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{unicode_atoms_SUITE,'test_αβ'}},
+ {?eh,tc_done,{unicode_atoms_SUITE,'test_αβ',ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{unicode_atoms_SUITE,'fail_αβ_1'}},
+ {?eh,tc_done,{unicode_atoms_SUITE,'fail_αβ_1','_'}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ {?eh,tc_start,{unicode_atoms_SUITE,'fail_αβ_2'}},
+ {?eh,tc_done,{unicode_atoms_SUITE,'fail_αβ_2','_'}},
+ {?eh,test_stats,{1,2,{0,0}}},
+ {?eh,tc_start,{unicode_atoms_SUITE,'fail_αβ_3'}},
+ {?eh,tc_done,{unicode_atoms_SUITE,'fail_αβ_3','_'}},
+ {?eh,test_stats,{1,3,{0,0}}},
+ {?eh,tc_start,{unicode_atoms_SUITE,'fail_αβ_4'}},
+ {?eh,tc_done,{unicode_atoms_SUITE,'fail_αβ_4','_'}},
+ {?eh,test_stats,{1,4,{0,0}}},
+ {?eh,tc_start,{unicode_atoms_SUITE,'skip_αβ'}},
+ {?eh,tc_done,{unicode_atoms_SUITE,'skip_αβ','_'}},
+ {?eh,test_stats,{1,4,{1,0}}},
+ {?eh,tc_start,{unicode_atoms_SUITE,end_per_suite}},
+ {?eh,tc_done,{unicode_atoms_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+test_events(unicode_spec) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{unicode_atoms_SUITE,init_per_suite}},
+ {?eh,tc_done,{unicode_atoms_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{unicode_atoms_SUITE,'config_αβ'}},
+ {?eh,tc_done,{unicode_atoms_SUITE,'config_αβ',ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{unicode_atoms_SUITE,end_per_suite}},
+ {?eh,tc_done,{unicode_atoms_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
+
+%%%-----------------------------------------------------------------
+%%% Check logs for escaped unicode characters
+check_logs(Dirs) ->
+ ct:log("Checking logs for escaped unicode characters (αβ).~nDirs:~n~tp",
+ [Dirs]),
+ {ok,RE} = re:compile(<<"x{3B[12]}"/utf8>>),
+ case check_logs1(RE,Dirs,[]) of
+ [] ->
+ ok;
+ Match ->
+ MatchStr = string:join(Match,"\n"),
+ ct:log("ERROR: Escaped unicode characters found in:~n~ts",[MatchStr]),
+ ct:fail(escaped_unicode_characters_found)
+ end.
+
+check_logs1(RE,[F|Fs],Match) ->
+ New = case filelib:is_dir(F) of
+ true ->
+ {ok,Files} = file:list_dir(F),
+ check_logs1(RE,[filename:join(F,File)||File<-Files],[]);
+ false ->
+ check_log(RE,F)
+ end,
+ check_logs1(RE,Fs,New++Match);
+check_logs1(_RE,[],Match) ->
+ Match.
+
+check_log(RE,F) ->
+ {ok,Bin} = file:read_file(F),
+ case re:run(Bin,RE,[{capture,none}]) of
+ match ->
+ [F];
+ nomatch ->
+ []
+ end.
diff --git a/lib/common_test/test/ct_unicode_SUITE_data/unicode_atoms_SUITE.erl b/lib/common_test/test/ct_unicode_SUITE_data/unicode_atoms_SUITE.erl
new file mode 100644
index 0000000000..993452500e
--- /dev/null
+++ b/lib/common_test/test/ct_unicode_SUITE_data/unicode_atoms_SUITE.erl
@@ -0,0 +1,98 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-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(unicode_atoms_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{timetrap,{seconds,30}}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+groups() ->
+ [].
+
+all() ->
+ ['test_αβ',
+ 'fail_αβ_1',
+ 'fail_αβ_2',
+ 'fail_αβ_3',
+ 'fail_αβ_4',
+ 'skip_αβ'].
+
+'test_αβ'(_Config) ->
+ ct:log("This is test case ~tw",[?FUNCTION_NAME]),
+ ok.
+
+'fail_αβ_1'(_Config) ->
+ ct:log("This is test case ~tw",[?FUNCTION_NAME]),
+ 'α' = 'β',
+ ok.
+
+'fail_αβ_2'(_Config) ->
+ ct:log("This is test case ~tw",[?FUNCTION_NAME]),
+ ct:fail({failing,testcase,?FUNCTION_NAME}),
+ ok.
+
+'fail_αβ_3'(_Config) ->
+ ct:log("This is test case ~tw",[?FUNCTION_NAME]),
+ exit({exiting,testcase,?FUNCTION_NAME}),
+ ok.
+
+'fail_αβ_4'(_Config) ->
+ ct:log("This is test case ~tw",[?FUNCTION_NAME]),
+ S = try throw(ok) catch throw:ok -> erlang:get_stacktrace() end,
+ erlang:raise(error,{error,testcase,?FUNCTION_NAME},S),
+ ok.
+
+'skip_αβ'(_Config) ->
+ ct:log("This is test case ~tw",[?FUNCTION_NAME]),
+ {skip,"Skipping " ++ atom_to_list(?FUNCTION_NAME)}.
+
+
+%% This should not be listed in all/0. It is only to be run explicitly
+%% using a test spec where the config file is declared as well.
+'config_αβ'() ->
+ [{require,'alias_αβ','key_αβ'}].
+'config_αβ'(_Config) ->
+ ct:log("This is test case ~tw",[?FUNCTION_NAME]),
+ Conf = ct:get_config('alias_αβ'),
+ Conf = ct:get_config('key_αβ'),
+ ct:log("Required config: ~tp",[Conf]),
+ ok.
diff --git a/lib/common_test/test/ct_verbosity_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE.erl
index b9298e54ca..095cd1b70b 100644
--- a/lib/common_test/test/ct_verbosity_SUITE.erl
+++ b/lib/common_test/test/ct_verbosity_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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.
@@ -47,8 +47,8 @@
init_per_suite(Config) ->
DataDir = ?config(data_dir, Config),
EvH = filename:join(DataDir,"simple_evh.erl"),
- ct:pal("Compiling ~s: ~p", [EvH,compile:file(EvH,[{outdir,DataDir},
- debug_info])]),
+ ct:pal("Compiling ~ts: ~p", [EvH,compile:file(EvH,[{outdir,DataDir},
+ debug_info])]),
ct_test_support:init_per_suite([{path_dirs,[DataDir]} | Config]).
end_per_suite(Config) ->
diff --git a/lib/common_test/test/erl2html2_SUITE.erl b/lib/common_test/test/erl2html2_SUITE.erl
index bdce43c9c9..53a63578b2 100644
--- a/lib/common_test/test/erl2html2_SUITE.erl
+++ b/lib/common_test/test/erl2html2_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-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.
@@ -163,9 +163,9 @@ convert_module(Mod,InclDirs,Config) ->
PrivDir = ?config(priv_dir,Config),
Src = filename:join(DataDir,Mod++".erl"),
Dst = filename:join(PrivDir,Mod++".erl.html"),
- io:format("<a href=\"~s\">~s</a>\n",[Src,filename:basename(Src)]),
+ io:format("<a href=\"~ts\">~s</a>\n",[Src,filename:basename(Src)]),
ok = erl2html2:convert(Src, Dst, InclDirs, "<html><body>"),
- io:format("<a href=\"~s\">~s</a>\n",[Dst,filename:basename(Dst)]),
+ io:format("<a href=\"~ts\">~s</a>\n",[Dst,filename:basename(Dst)]),
{Src,Dst}.
%% Check that there are the same number of lines in each file, and
diff --git a/lib/common_test/test/test_server_test_lib.erl b/lib/common_test/test/test_server_test_lib.erl
index cf5951ae03..c18b89b178 100644
--- a/lib/common_test/test/test_server_test_lib.erl
+++ b/lib/common_test/test/test_server_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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.
@@ -81,7 +81,7 @@ prepare_tester_node(Node,Config) ->
[true = rpc:call(Node, code, add_patha, [D]) || D <- PathDirs],
io:format("Dirs added to code path (on ~w):~n",
[Node]),
- [io:format("~s~n", [D]) || D <- PathDirs],
+ [io:format("~ts~n", [D]) || D <- PathDirs],
true = rpc:call(Node, os, putenv,
["TEST_SERVER_FRAMEWORK", "undefined"]),
diff --git a/lib/common_test/test_server/ts_install.erl b/lib/common_test/test_server/ts_install.erl
index 5734bd0787..c4e0223ac7 100644
--- a/lib/common_test/test_server/ts_install.erl
+++ b/lib/common_test/test_server/ts_install.erl
@@ -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.
@@ -136,7 +136,7 @@ unix_autoconf(XConf) ->
true ->
OSXEnv = macosx_cflags(),
UnQuotedEnv = assign_vars(unquote(Env++OSXEnv)),
- io:format("Running ~s~nEnv: ~p~n",
+ io:format("Running ~ts~nEnv: ~p~n",
[lists:flatten(Configure ++ Args),UnQuotedEnv]),
Port = open_port({spawn, lists:flatten(["\"",Configure,"\"",Args])},
[stream, eof, {env,UnQuotedEnv}]),
diff --git a/lib/common_test/test_server/ts_make.erl b/lib/common_test/test_server/ts_make.erl
index 456e913c39..ad5b75b529 100644
--- a/lib/common_test/test_server/ts_make.erl
+++ b/lib/common_test/test_server/ts_make.erl
@@ -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.
@@ -42,7 +42,7 @@ unmake(Config) when is_list(Config) ->
make(Make, Dir, Makefile) ->
{RunFile, RunCmd, Script} = run_make_script(os:type(), Make, Dir, Makefile),
- case file:write_file(RunFile, Script) of
+ case file:write_file(RunFile, unicode:characters_to_binary(Script)) of
ok ->
Log = filename:join(Dir, "make.log"),
file:delete(Log),
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index e6ae8b2e7a..9fc3f7f797 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.14
+COMMON_TEST_VSN = 1.15.1
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index 1dc0c808e7..f3d42a909b 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -32,6 +32,180 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>For many releases, it has been legal to override a BIF
+ with a local function having the same name. However,
+ calling a local function with the same name as guard BIF
+ as filter in a list comprehension was not allowed.</p>
+ <p>
+ Own Id: OTP-13690</p>
+ </item>
+ <item>
+ <p>compile:forms/2 would not return the module name as
+ documented when one of the options '<c>from_core</c>',
+ '<c>from_asm</c>', or '<c>from_beam</c>' was given. Also,
+ the compiler would crash if one of those options was
+ combined with '<c>native</c>'.</p>
+ <p>
+ Own Id: OTP-14408 Aux Id: ERL-417 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Optimized test for tuples with an atom as first element.</p>
+ <p>
+ Own Id: OTP-12148</p>
+ </item>
+ <item>
+ <p>
+ Compilation of modules with huge literal binary strings
+ is now much faster.</p>
+ <p>
+ Own Id: OTP-13794</p>
+ </item>
+ <item>
+ <p>Replaced usage of deprecated symbolic <seealso
+ marker="erts:erlang#type-time_unit"><c>time
+ unit</c></seealso> representations.</p>
+ <p>
+ Own Id: OTP-13831 Aux Id: OTP-13735 </p>
+ </item>
+ <item>
+ <p>The undocumented and unsupported module
+ <c>sys_pre_expand</c> has been removed. As a partial
+ replacement for the functionality, there is a new
+ function <c>erl_internal:add_predefined_functions/1</c>
+ and <c>erl_expand_records</c> will now add a module
+ prefix to calls to BIFs and imported functions.</p>
+ <p>
+ Own Id: OTP-13856</p>
+ </item>
+ <item>
+ <p>The internal compiler passes now start all generated
+ variables with "@" to avoid any conflicts with variables
+ in languages such as Elixir or LFE.</p>
+ <p>
+ Own Id: OTP-13924</p>
+ </item>
+ <item>
+ <p>The function <c>fmod/2</c> has been added to the
+ <c>math</c> module.</p>
+ <p>
+ Own Id: OTP-14000</p>
+ </item>
+ <item>
+ <p>Code generation for complicated guards have been
+ improved.</p>
+ <p>
+ Own Id: OTP-14042</p>
+ </item>
+ <item>
+ <p>
+ The compiler has new warnings for repeated identical map
+ keys.</p>
+ <p>
+ A map expression such as,</p>
+ <p>
+ <c> #{'a' => 1, 'b' => 2, 'a' => 3}.</c></p>
+ <p>
+ will produce a warning for the repeated key 'a'.</p>
+ <p>
+ Own Id: OTP-14058</p>
+ </item>
+ <item>
+ <p>By default, there will now be a warning when
+ <c>export_all</c> is used. The warning can be disabled
+ using <c>nowarn_export_all</c>.</p>
+ <p>
+ Own Id: OTP-14071</p>
+ </item>
+ <item>
+ <p>
+ Optimize maps pattern matching by only examining the
+ common keys in each clause first instead of all keys.
+ This will reduce the number of lookups of each key in
+ maps pattern matching.</p>
+ <p>
+ Own Id: OTP-14072</p>
+ </item>
+ <item>
+ <p>There is a new '<c>deterministic</c>' option to omit
+ '<c>source</c>' and '<c>options</c>' tuples in the BEAM
+ file.</p>
+ <p>
+ Own Id: OTP-14087</p>
+ </item>
+ <item>
+ <p>
+ Analyzing modules with binary construction with huge
+ strings is now much faster. The compiler also compiles
+ such modules slightly faster.</p>
+ <p>
+ Own Id: OTP-14125 Aux Id: ERL-308 </p>
+ </item>
+ <item>
+ <p>Atoms may now contain arbitrary Unicode
+ characters.</p>
+ <p>
+ Own Id: OTP-14178</p>
+ </item>
+ <item>
+ <p><c>compile:file/2</c> now accepts the option
+ <c>extra_chunks</c> to include extra chunks in the BEAM
+ file.</p>
+ <p>
+ Own Id: OTP-14221</p>
+ </item>
+ <item>
+ <p>The format of debug information that is stored in BEAM
+ files (when <c>debug_info</c> is used) has been changed.
+ The purpose of the change is to better support other
+ BEAM-based languages such as Elixir or LFE.</p>
+ <p>All tools included in OTP (dialyzer, debugger, cover,
+ and so on) will handle both the new format and the
+ previous format. Tools that retrieve the debug
+ information using <c>beam_lib:chunk(Beam,
+ [abstract_code])</c> will continue to work with both the
+ new and old format. Tools that call
+ <c>beam_lib:chunk(Beam, ["Abst"])</c> will not work with
+ the new format.</p>
+ <p>For more information, see the description of
+ <c>debug_info</c> in the documentation for
+ <c>beam_lib</c> and the description of the
+ <c>{debug_info,{Backend,Data}}</c> option in the
+ documentation for <c>compile</c>.</p>
+ <p>
+ Own Id: OTP-14369 Aux Id: PR-1367 </p>
+ </item>
+ <item>
+ <p>In a future release, <c>erlang:get_stacktrace/0</c>
+ will probably only work when called from within a
+ '<c>try</c>' expression (otherwise it will return
+ <c>[]</c>.</p>
+ <p>To help prepare for that change, the compiler will now
+ by default warn if '<c>get_stacktrace/0</c>' is used in a
+ way that will not work in the future. Note that the
+ warning will not be issued if '<c>get_stacktrace/0</c>'
+ is used in a function that uses neither '<c>catch</c>'
+ nor '<c>try</c>' (because that could be a legal use if
+ the function is called from within a '<c>try</c>'.</p>
+ <p>
+ Own Id: OTP-14401</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 7.0.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index f06b8b9ec3..ef6db66ff6 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -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.
diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src
index d4b4d4da04..3139d68902 100644
--- a/lib/compiler/src/compiler.app.src
+++ b/lib/compiler/src/compiler.app.src
@@ -1,7 +1,7 @@
% This is an -*- erlang -*- file.
%% %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/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index 9efb9ad6f8..b5688de339 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2016. All Rights Reserved.
+# Copyright Ericsson AB 1998-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/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 7acf08129a..e0cd6da06f 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index ae650546e5..20cb3343fb 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -2505,8 +2505,46 @@ cexpr(#ifun{anno=#a{us=Us0}=A0,name={named,Name},fc=#iclause{pats=Ps}}=Fun0,
end;
cexpr(#iapply{anno=A,op=Op,args=Args}, _As, St) ->
{#c_apply{anno=A#a.anno,op=Op,args=Args},[],A#a.us,St};
-cexpr(#icall{anno=A,module=Mod,name=Name,args=Args}, _As, St) ->
- {#c_call{anno=A#a.anno,module=Mod,name=Name,args=Args},[],A#a.us,St};
+cexpr(#icall{anno=A,module=Mod,name=Name,args=Args}, _As, St0) ->
+ Anno = A#a.anno,
+ case (not cerl:is_c_atom(Mod)) andalso member(tuple_calls, St0#core.opts) of
+ true ->
+ GenAnno = [compiler_generated|Anno],
+
+ %% Generate the clause that matches on the tuple
+ {TupleVar,St1} = new_var(GenAnno, St0),
+ {TupleSizeVar, St2} = new_var(GenAnno, St1),
+ {TupleModVar, St3} = new_var(GenAnno, St2),
+ {TupleArgsVar, St4} = new_var(GenAnno, St3),
+ TryVar = cerl:c_var('Try'),
+
+ TupleGuardExpr =
+ cerl:c_let([TupleSizeVar],
+ c_call_erl(tuple_size, [TupleVar]),
+ c_call_erl('>', [TupleSizeVar, cerl:c_int(0)])),
+
+ TupleGuard =
+ cerl:c_try(TupleGuardExpr, [TryVar], TryVar,
+ [cerl:c_var('T'),cerl:c_var('R')], cerl:c_atom(false)),
+
+ TupleApply =
+ cerl:c_let([TupleModVar],
+ c_call_erl(element, [cerl:c_int(1),TupleVar]),
+ cerl:c_let([TupleArgsVar],
+ cerl:make_list(Args ++ [TupleVar]),
+ c_call_erl(apply, [TupleModVar,Name,TupleArgsVar]))),
+
+ TupleClause = cerl:ann_c_clause(GenAnno, [TupleVar], TupleGuard, TupleApply),
+
+ %% Generate the fallback clause
+ {OtherVar,St5} = new_var(GenAnno, St4),
+ OtherApply = cerl:ann_c_call(GenAnno, OtherVar, Name, Args),
+ OtherClause = cerl:ann_c_clause(GenAnno, [OtherVar], OtherApply),
+
+ {cerl:ann_c_case(GenAnno, Mod, [TupleClause,OtherClause]),[],A#a.us,St5};
+ false ->
+ {#c_call{anno=Anno,module=Mod,name=Name,args=Args},[],A#a.us,St0}
+ end;
cexpr(#iprimop{anno=A,name=Name,args=Args}, _As, St) ->
{#c_primop{anno=A#a.anno,name=Name,args=Args},[],A#a.us,St};
cexpr(#iprotect{anno=A,body=Es}, _As, St0) ->
@@ -2536,6 +2574,9 @@ cfun(#ifun{anno=A,id=Id,vars=Args,clauses=Lcs,fc=Lfc}, _As, St0) ->
clauses=Ccs ++ [Cfc]}},
[],A#a.us,St2}.
+c_call_erl(Fun, Args) ->
+ cerl:c_call(cerl:c_atom(erlang), cerl:c_atom(Fun), Args).
+
%% lit_vars(Literal) -> [Var].
lit_vars(Lit) -> lit_vars(Lit, []).
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index 716280a95c..53097d0d7d 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 75bfbf68cc..c23514b36b 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl
index fd97eea4cb..da99aba346 100644
--- a/lib/compiler/test/bs_construct_SUITE.erl
+++ b/lib/compiler/test/bs_construct_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 106d8eb45a..0ec05456ec 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index f647a4030d..aaa2005e73 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_pp/1,
+ cover/1, env/1, core_pp/1, tuple_calls/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,
@@ -49,7 +49,7 @@ all() ->
test_lib:recompile(?MODULE),
[app_test, appup_test, file_1, forms_2, module_mismatch, big_file, outdir,
binary, makedep, cond_and_ifdef, listings, listings_big,
- other_output, kernel_listing, encrypted_abstr,
+ other_output, kernel_listing, encrypted_abstr, tuple_calls,
strict_record, utf8_atoms, utf8_functions, extra_chunks,
cover, env, core_pp, core_roundtrip, asm, optimized_guards,
sys_pre_attributes, dialyzer, warnings, pre_load_check,
@@ -781,6 +781,37 @@ extra_chunks(Config) when is_list(Config) ->
{ok,{extra_chunks,[{"ExCh",<<"Contents">>}]}} =
beam_lib:chunks(ExtraChunksBinary, ["ExCh"]).
+tuple_calls(Config) when is_list(Config) ->
+ Anno = erl_anno:new(1),
+ Forms = [{attribute,Anno,export,[{size,1},{store,1}]},
+ {function,Anno,size,1,
+ [{clause,Anno,[{var,[],mod}],[],
+ [{call,[],{remote,[],{var,[],mod},{atom,[],size}},[]}]}]},
+ {function,Anno,store,1,
+ [{clause,Anno,[{var,[],mod}],[],
+ [{call,[],{remote,[],{var,[],mod},{atom,[],store}},[{atom,[],key},{atom,[],value}]}]}]}],
+
+ TupleCallsFalse = [{attribute,Anno,module,tuple_calls_false}|Forms],
+ {ok,_,TupleCallsFalseBinary} = compile:forms(TupleCallsFalse, [binary]),
+ code:load_binary(tuple_calls_false, "compile_SUITE.erl", TupleCallsFalseBinary),
+ {'EXIT',{badarg,_}} = (catch tuple_calls_false:store(dict())),
+ {'EXIT',{badarg,_}} = (catch tuple_calls_false:size(dict())),
+ {'EXIT',{badarg,_}} = (catch tuple_calls_false:size(empty_tuple())),
+
+ TupleCallsTrue = [{attribute,Anno,module,tuple_calls_true}|Forms],
+ {ok,_,TupleCallsTrueBinary} = compile:forms(TupleCallsTrue, [binary,tuple_calls]),
+ code:load_binary(tuple_calls_true, "compile_SUITE.erl", TupleCallsTrueBinary),
+ Dict = tuple_calls_true:store(dict()),
+ 1 = tuple_calls_true:size(Dict),
+ {'EXIT',{badarg,_}} = (catch tuple_calls_true:size(empty_tuple())),
+
+ ok.
+
+dict() ->
+ dict:new().
+empty_tuple() ->
+ {}.
+
env(Config) when is_list(Config) ->
{Simple,Target} = get_files(Config, simple, env),
{ok,Cwd} = file:get_cwd(),
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 77e4234c70..857995b6a6 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index 5c87304a01..463c264a5f 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 7.0.4
+COMPILER_VSN = 7.1
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 62b013e463..574353ce7a 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -31,6 +31,134 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 4.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ LibreSSL can now be used by the modernized crypto app.</p>
+ <p>
+ Own Id: OTP-14247</p>
+ </item>
+ <item>
+ <p>
+ Add compile option <c>-compile(no_native)</c> in modules
+ with <c>on_load</c> directive which is not yet supported
+ by HiPE.</p>
+ <p>
+ Own Id: OTP-14316 Aux Id: PR-1390 </p>
+ </item>
+ <item>
+ <p>
+ Fix a bug in aes cfb128 function introduced by the bug
+ fix in GitHub pull request <url
+ href="https://github.com/erlang/otp/pull/1393">#1393</url>.</p>
+ <p>
+ Own Id: OTP-14435 Aux Id: PR-1462, PR-1393, OTP-14313 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add basic support for CMAC</p>
+ <p>
+ Own Id: OTP-13779 Aux Id: ERL-82 PR-1138 </p>
+ </item>
+ <item>
+ <p>
+ Removed functions deprecated in crypto-3.0 first released
+ in OTP-R16B01</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13873</p>
+ </item>
+ <item>
+ <p>
+ The <c>crypto</c> application now supports OpenSSL 1.1.</p>
+ <p>
+ Own Id: OTP-13900</p>
+ </item>
+ <item>
+ <p>
+ Allow Erlang/OTP to use OpenSSL in FIPS-140 mode, in
+ order to satisfy specific security requirements (mostly
+ by different parts of the US federal government). </p>
+ <p>
+ See the new crypto users guide "FIPS mode" chapter about
+ building and using the FIPS support which is disabled by
+ default.</p>
+ <p>
+ (Thanks to dszoboszlay and legoscia)</p>
+ <p>
+ Own Id: OTP-13921 Aux Id: PR-1180 </p>
+ </item>
+ <item>
+ <p>
+ Crypto chacha20-poly1305 as in RFC 7539 enabled for
+ OpenSSL >= 1.1.</p>
+ <p>
+ Thanks to mururu.</p>
+ <p>
+ Own Id: OTP-14092 Aux Id: PR-1291 </p>
+ </item>
+ <item>
+ <p>
+ RSA key generation added to <c>crypto:generate_key/2</c>.
+ Thanks to wiml.</p>
+ <p>
+ An interface is also added to
+ <c>public_key:generate_key/1</c>.</p>
+ <p>
+ Own Id: OTP-14140 Aux Id: ERL-165, PR-1299 </p>
+ </item>
+ <item>
+ <p>
+ Raised minimum requirement for OpenSSL version to
+ OpenSSL-0.9.8.c although we recommend a much higher
+ version, that is a version that is still maintained
+ officially by the OpenSSL project. Note that using such
+ an old version may restrict the crypto algorithms
+ supported.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14171</p>
+ </item>
+ <item>
+ <p>
+ Deprecate crypto:rand_uniform/2 as it is not
+ cryptographically strong</p>
+ <p>
+ Own Id: OTP-14274</p>
+ </item>
+ <item>
+ <p>
+ The Crypto application now supports generation of
+ cryptographically strong random numbers (floats &lt; 1.0
+ and integer arbitrary ranges) as a plugin to the 'rand'
+ module.</p>
+ <p>
+ Own Id: OTP-14317 Aux Id: PR-1372 </p>
+ </item>
+ <item>
+ <p>
+ This replaces the hard coded test values for AES, CMAC
+ and GCM ciphers with the full validation set from NIST's
+ CAVP program.</p>
+ <p>
+ Own Id: OTP-14436 Aux Id: PR-1396 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 3.7.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index f3e0623ac9..796e3b6d84 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 3.7.4
+CRYPTO_VSN = 4.0
diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml
index 532a17bd81..fa85567a84 100644
--- a/lib/debugger/doc/src/notes.xml
+++ b/lib/debugger/doc/src/notes.xml
@@ -33,6 +33,42 @@
<p>This document describes the changes made to the Debugger
application.</p>
+<section><title>Debugger 4.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> The Erlang shell, <c>qlc:string_to_handle()</c>, and
+ the Debugger (the Evaluator area and Edit variable window
+ of the Bindings area) can parse pids, ports, references,
+ and external funs, as long as they can be created in the
+ running system. </p>
+ <p>
+ Own Id: OTP-14296</p>
+ </item>
+ <item>
+ <p> Fix editing of simple binary values in the Bindings
+ area of the Debugger's Attach Process Window. </p>
+ <p>
+ Own Id: OTP-14318</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Debugger 4.2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk
index f5440865ef..3534570ef5 100644
--- a/lib/debugger/vsn.mk
+++ b/lib/debugger/vsn.mk
@@ -1 +1 @@
-DEBUGGER_VSN = 4.2.1
+DEBUGGER_VSN = 4.2.2
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index f7613b3145..0d2cb6c4df 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -32,6 +32,75 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> The check of bad type variables in type declarations
+ was mistakingly removed in Erlang/OTP 18, and is now
+ re-introduced. </p>
+ <p>
+ Own Id: OTP-14423 Aux Id: OTP-14323 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Analyzing modules with binary construction with huge
+ strings is now much faster. The compiler also compiles
+ such modules slightly faster.</p>
+ <p>
+ Own Id: OTP-14125 Aux Id: ERL-308 </p>
+ </item>
+ <item>
+ <p> The peak memory consumption is reduced. </p>
+ <p>
+ Own Id: OTP-14127</p>
+ </item>
+ <item>
+ <p> Warnings about unknown types are now also generated
+ for types not used by any function specification. </p>
+ <p>
+ Own Id: OTP-14218 Aux Id: OTP-14127 </p>
+ </item>
+ <item>
+ <p>TypEr has been removed as separate application and is
+ now a part of the Dialyzer application. Documentation for
+ TypEr has been added in the Dialyzer application.</p>
+ <p>
+ Own Id: OTP-14336</p>
+ </item>
+ <item>
+ <p>The format of debug information that is stored in BEAM
+ files (when <c>debug_info</c> is used) has been changed.
+ The purpose of the change is to better support other
+ BEAM-based languages such as Elixir or LFE.</p>
+ <p>All tools included in OTP (dialyzer, debugger, cover,
+ and so on) will handle both the new format and the
+ previous format. Tools that retrieve the debug
+ information using <c>beam_lib:chunk(Beam,
+ [abstract_code])</c> will continue to work with both the
+ new and old format. Tools that call
+ <c>beam_lib:chunk(Beam, ["Abst"])</c> will not work with
+ the new format.</p>
+ <p>For more information, see the description of
+ <c>debug_info</c> in the documentation for
+ <c>beam_lib</c> and the description of the
+ <c>{debug_info,{Backend,Data}}</c> option in the
+ documentation for <c>compile</c>.</p>
+ <p>
+ Own Id: OTP-14369 Aux Id: PR-1367 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 3.1.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 511a6d66bf..6e501f32b2 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -120,92 +120,10 @@ get_core_from_beam(File, Opts) ->
{error, " Could not get Core Erlang code for: " ++ File ++ "\n"}
end;
_ ->
- deprecated_get_core_from_beam(File, Opts)
+ {error, " Could not get Core Erlang code for: " ++ File ++ "\n" ++
+ " Recompile with +debug_info or analyze starting from source code"}
end.
-deprecated_get_core_from_beam(File, Opts) ->
- case get_abstract_code_from_beam(File) of
- error ->
- {error, " Could not get abstract code for: " ++ File ++ "\n" ++
- " Recompile with +debug_info or analyze starting from source code"};
- {ok, AbstrCode} ->
- case get_compile_options_from_beam(File) of
- error ->
- {error, " Could not get compile options for: " ++ File ++ "\n" ++
- " Recompile or analyze starting from source code"};
- {ok, CompOpts} ->
- case get_core_from_abstract_code(AbstrCode, Opts ++ CompOpts) of
- error ->
- {error, " Could not get core Erlang code for: " ++ File};
- {ok, _} = Core ->
- Core
- end
- end
- end.
-
-get_abstract_code_from_beam(File) ->
- case beam_lib:chunks(File, [abstract_code]) of
- {ok, {_, List}} ->
- case lists:keyfind(abstract_code, 1, List) of
- {abstract_code, {raw_abstract_v1, Abstr}} -> {ok, Abstr};
- _ -> error
- end;
- _ ->
- %% No or unsuitable abstract code.
- error
- end.
-
-get_compile_options_from_beam(File) ->
- case beam_lib:chunks(File, [compile_info]) of
- {ok, {_, List}} ->
- case lists:keyfind(compile_info, 1, List) of
- {compile_info, CompInfo} -> compile_info_to_options(CompInfo);
- _ -> error
- end;
- _ ->
- %% No or unsuitable compile info.
- error
- end.
-
-compile_info_to_options(CompInfo) ->
- case lists:keyfind(options, 1, CompInfo) of
- {options, CompOpts} -> {ok, CompOpts};
- _ -> error
- end.
-
-get_core_from_abstract_code(AbstrCode, Opts) ->
- %% We do not want the parse_transforms around since we already
- %% performed them. In some cases we end up in trouble when
- %% performing them again.
- AbstrCode1 = cleanup_parse_transforms(AbstrCode),
- %% Remove parse_transforms (and other options) from compile options.
- Opts2 = cleanup_compile_options(Opts),
- try compile:noenv_forms(AbstrCode1, Opts2 ++ src_compiler_opts()) of
- {ok, _, Core} -> {ok, Core};
- _What -> error
- catch
- error:_ -> error
- end.
-
-cleanup_parse_transforms([{attribute, _, compile, {parse_transform, _}}|Left]) ->
- cleanup_parse_transforms(Left);
-cleanup_parse_transforms([Other|Left]) ->
- [Other|cleanup_parse_transforms(Left)];
-cleanup_parse_transforms([]) ->
- [].
-
-cleanup_compile_options(Opts) ->
- lists:filter(fun keep_compile_option/1, Opts).
-
-%% Using abstract, not asm or core.
-keep_compile_option(from_asm) -> false;
-keep_compile_option(from_core) -> false;
-%% The parse transform will already have been applied, may cause
-%% problems if it is re-applied.
-keep_compile_option({parse_transform, _}) -> false;
-keep_compile_option(warnings_as_errors) -> false;
-keep_compile_option(_) -> true.
-
%% ============================================================================
%%
%% Typed Records
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index 0919fba834..4a1a7c25a0 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 3.1
+DIALYZER_VSN = 3.2
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index 50f568abaa..60478606ad 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -43,6 +43,76 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 2.0</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Let candidate peers be passed to diameter:call/4</p>
+ <p>
+ With call option peer, to allow a request to be sent to a
+ peer that hasn't advertised support for the application
+ in question.</p>
+ <p>
+ RFC 6733 2.4 requires a node to send the application
+ identifiers of all locally supported applications at
+ capabilities exchange, but not all nodes respect this for
+ the common application, and diameter itself will send
+ D[WP][RA] without the common application having been
+ explicitly advertised. Regarding the common application
+ as implicit renders Result-Code 5010
+ (DIAMETER_NO_COMMON_APPLICATION) meaningless however, so
+ allow any request to be sent as long as there is a
+ configured dictionary to support it.</p>
+ <p>
+ Own Id: OTP-14338</p>
+ </item>
+ <item>
+ <p>
+ Improve performance of message encode/decode and related
+ handling.</p>
+ <p>
+ Dictionaries using @custom_types or @codecs will need to
+ adapt the corresponding functions to accept an additional
+ argument that is now passed through encode/decode, which
+ was required to remove various process dictionary-based
+ workarounds that have been used to solve problems in the
+ past.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14343</p>
+ </item>
+ <item>
+ <p>
+ Add transport options to avoid deadlock and allow for
+ load regulation.</p>
+ <p>
+ Both diameter_tcp and diameter_sctp now accept two new
+ configuration options: sender and message_cb. The former
+ causes outgoing sends to take place in a dedicated
+ process, to avoid the possibility of deadlock when both
+ the transport process and its peer block in send. The
+ latter allows a callback to control the reading of
+ messages on the socket, to allow for backpressure towards
+ peers when the rate of incoming traffic is greater than
+ can otherwise be handled.</p>
+ <p>
+ Neither of these options are yet documented, but are
+ unlikely to change unless problems are discovered. The
+ sender option is not the default since it should probably
+ always be used in combination with message_cb, to prevent
+ incoming requests from being read at a higher rate than a
+ peer allows outgoing answers to be sent.</p>
+ <p>
+ Own Id: OTP-14455 Aux Id: ERL-332 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 1.12.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/diameter/src/base/diameter_lib.erl b/lib/diameter/src/base/diameter_lib.erl
index 58b9a29812..8792e97621 100644
--- a/lib/diameter/src/base/diameter_lib.erl
+++ b/lib/diameter/src/base/diameter_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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/lib/diameter/src/base/diameter_reg.erl b/lib/diameter/src/base/diameter_reg.erl
index 4910979219..97e74657bd 100644
--- a/lib/diameter/src/base/diameter_reg.erl
+++ b/lib/diameter/src/base/diameter_reg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl
index 5d74e63b8d..9de5cbe685 100644
--- a/lib/diameter/test/diameter_relay_SUITE.erl
+++ b/lib/diameter/test/diameter_relay_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
index 4982488335..7894811c78 100644
--- a/lib/edoc/doc/src/notes.xml
+++ b/lib/edoc/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2007</year><year>2016</year>
+ <year>2007</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,28 @@
<p>This document describes the changes made to the EDoc
application.</p>
+<section><title>Edoc 0.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>To support stable builds, <c>edoc</c> no longer
+ includes time stamps in the footer for generated
+ files.</p>
+ <p>
+ Own Id: OTP-14277</p>
+ </item>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Edoc 0.8.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index d66802fdac..1a933b2ad8 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.8.1
+EDOC_VSN = 0.9
diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index 4824a755d9..59a268d6ac 100644
--- a/lib/erl_docgen/doc/src/notes.xml
+++ b/lib/erl_docgen/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2016</year>
+ <year>2004</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,7 +31,42 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.6.1</title>
+ <section><title>Erl_Docgen 0.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Sort index of C functions alphabetically in the sidebar.</p>
+ <p>
+ Own Id: OTP-14333 Aux Id: ERL-393 </p>
+ </item>
+ <item>
+ <p> The right side index of functions now handle
+ functions with same name but different arity. </p>
+ <p>
+ Own Id: OTP-14431</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improvements in the OTP documentation style.</p>
+ <p>
+ (Thanks to Mariano Guerra)</p>
+ <p>
+ Own Id: OTP-14371 Aux Id: PR-1215 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.6.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index 77d503b4d2..ec20f3c67f 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -31,6 +31,45 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug where gethostname would incorrectly fail with
+ enametoolong on Linux.</p>
+ <p>
+ Own Id: OTP-14310</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Remove generation of atoms in old latin1 external format
+ in the distribution between erlang nodes,
+ <c>erl_interface</c>, and <c>jinterface</c>. The new utf8
+ format for atoms was introduced in OTP R16. An OTP 20
+ node can therefore not connect to nodes older than R16.</p>
+ <p>
+ Atoms that can be encoded using latin1 are still encoded
+ by <c>term_to_binary()</c> using latin1 encoding. Note
+ that all atoms will by default be encoded using utf8 in a
+ future Erlang/OTP release. For more information see the
+ documentation of <seealso
+ marker="erts:erlang#term_to_binary/2"><c>erlang:term_to_binary/2</c></seealso>.</p>
+ <p>
+ Own Id: OTP-14337</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.9.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index 563694a0c1..01fcee86dd 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1,2 +1,2 @@
-EI_VSN = 3.9.3
+EI_VSN = 3.10
ERL_INTERFACE_VSN = $(EI_VSN)
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index cd4e230254..2a4ca6d12c 100644
--- a/lib/eunit/doc/src/notes.xml
+++ b/lib/eunit/doc/src/notes.xml
@@ -33,6 +33,21 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The surefire reports from <c>eunit</c> will no longer
+ have names with embedded double quotes.</p>
+ <p>
+ Own Id: OTP-14287</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.3.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 7eee75ee10..107ad5c101 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.3.2
+EUNIT_VSN = 2.3.3
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index 38f8dbfea3..9167d0aaec 100644
--- a/lib/hipe/doc/src/notes.xml
+++ b/lib/hipe/doc/src/notes.xml
@@ -31,6 +31,101 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.16</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix hipe compiler flags <c>o0</c> and <c>o1</c> that have
+ previously been ignored by mistake.</p>
+ <p>
+ Own Id: OTP-13862 Aux Id: PR-1154 </p>
+ </item>
+ <item>
+ <p>
+ Fix LLVM backend to not convert all remote calls to own
+ module, like <c>?MODULE:foo()</c>, into local calls.</p>
+ <p>
+ Own Id: OTP-13983</p>
+ </item>
+ <item>
+ <p>
+ Hipe optional LLVM backend does require LLVM version 3.9
+ or later as older versions forced strong dependencies on
+ erts internals structures.</p>
+ <p>
+ Own Id: OTP-14238</p>
+ </item>
+ <item>
+ <p>
+ Fix a bug that has been seen causing failed loading of
+ hipe compiled modules on NetBSD due to unaligned data
+ pointers.</p>
+ <p>
+ Own Id: OTP-14302 Aux Id: ERL-376, PR-1386 </p>
+ </item>
+ <item>
+ <p>
+ Fix miscompilation bug in hipe that could cause wrong
+ function clause to be called from non-tail calls, where
+ the return value is unused, if the right function clause
+ is only reachable from those non-tail calls.</p>
+ <p>
+ Own Id: OTP-14306 Aux Id: ERL-278, PR-1392 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improve hipe compilation time for large functions.</p>
+ <p>
+ Own Id: OTP-13810 Aux Id: PR-1124 </p>
+ </item>
+ <item>
+ <p>Replaced usage of deprecated symbolic <seealso
+ marker="erts:erlang#type-time_unit"><c>time
+ unit</c></seealso> representations.</p>
+ <p>
+ Own Id: OTP-13831 Aux Id: OTP-13735 </p>
+ </item>
+ <item>
+ <p>
+ Speed up hipe compile time register allocation for larger
+ function.</p>
+ <p>
+ Own Id: OTP-13879</p>
+ </item>
+ <item>
+ <p>
+ Various code generation improvements.</p>
+ <p>
+ Own Id: OTP-14261 Aux Id: PR-1360 </p>
+ </item>
+ <item>
+ <p>
+ Improve hipe compiler to generate code with better CPU
+ register utilization at runtime by the use of 'Live Range
+ Splitting' techniques.</p>
+ <p>
+ Own Id: OTP-14293 Aux Id: PR-1380 </p>
+ </item>
+ <item>
+ <p>
+ Allow HiPE to run on VM built with
+ <c>--enable-m32-build</c>.</p>
+ <p>
+ Own Id: OTP-14330 Aux Id: PR-1397 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.15.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index 172d976931..0ef4aa7f09 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.15.4
+HIPE_VSN = 3.16
diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml
index acf0d2163a..20c042c202 100644
--- a/lib/inets/doc/src/http_uri.xml
+++ b/lib/inets/doc/src/http_uri.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2012</year><year>2016</year>
+ <year>2012</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 46484977c9..66ec6cabd8 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2016</year>
+ <year>2004</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 1fad94fac1..2f4f20347a 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,44 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.3.9</title>
+ <section><title>Inets 6.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ httpd_util:rfc1123_date/1 gracefully handle invalid DST
+ dates by returning the original time in the expected
+ rfc1123 format.</p>
+ <p>
+ Own Id: OTP-14394</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add unicode binary support to http_uri functions</p>
+ <p>
+ Own Id: OTP-14404</p>
+ </item>
+ <item>
+ <p>
+ httpc - Change timeout handling so the redirects cause a
+ new timer to be set. This means that a simple redirected
+ request could return after 2*timeout milliseconds.</p>
+ <p>
+ Own Id: OTP-14429</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.9</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 4b2bcc7242..bd1d2e833a 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl
index 4568d165e7..c4be5abd7c 100644
--- a/lib/inets/src/http_lib/http_uri.erl
+++ b/lib/inets/src/http_lib/http_uri.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl
index 78b0aa2885..487d04f7aa 100644
--- a/lib/inets/src/http_lib/http_util.erl
+++ b/lib/inets/src/http_lib/http_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl
index 4f771015a6..4a2eff4770 100644
--- a/lib/inets/src/http_server/httpd_util.erl
+++ b/lib/inets/src/http_server/httpd_util.erl
@@ -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/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index a0a38ca103..931cd076cc 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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/lib/inets/test/uri_SUITE.erl b/lib/inets/test/uri_SUITE.erl
index d055af59e3..3e7799141c 100644
--- a/lib/inets/test/uri_SUITE.erl
+++ b/lib/inets/test/uri_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 758cef7ac4..96796f11c0 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.3.9
+INETS_VSN = 6.4
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index 30f607c357..b44a04d7cd 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2016</year>
+ <year>2000</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,32 @@
</header>
<p>This document describes the changes made to the Jinterface application.</p>
+<section><title>Jinterface 1.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Remove generation of atoms in old latin1 external format
+ in the distribution between erlang nodes,
+ <c>erl_interface</c>, and <c>jinterface</c>. The new utf8
+ format for atoms was introduced in OTP R16. An OTP 20
+ node can therefore not connect to nodes older than R16.</p>
+ <p>
+ Atoms that can be encoded using latin1 are still encoded
+ by <c>term_to_binary()</c> using latin1 encoding. Note
+ that all atoms will by default be encoded using utf8 in a
+ future Erlang/OTP release. For more information see the
+ documentation of <seealso
+ marker="erts:erlang#term_to_binary/2"><c>erlang:term_to_binary/2</c></seealso>.</p>
+ <p>
+ Own Id: OTP-14337</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Jinterface 1.7.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index c29d9df3d7..373e2dab22 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1 +1 @@
-JINTERFACE_VSN = 1.7.1
+JINTERFACE_VSN = 1.8
diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml
index 570d3ef9bd..1be28adfb8 100644
--- a/lib/kernel/doc/src/disk_log.xml
+++ b/lib/kernel/doc/src/disk_log.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2016</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index 27db00819f..91bf57cb91 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -126,6 +126,12 @@ ok</pre>
<seealso marker="#error_report/1"><c>error_report/1</c></seealso>
instead.</p>
</warning>
+ <warning>
+ <p>If the Unicode translation modifier (<c>t</c>) is used in
+ the format string, all error handlers must ensure that the
+ formatted output is correctly encoded for the I/O
+ device.</p>
+ </warning>
</desc>
</func>
<func>
@@ -197,6 +203,12 @@ ok</pre>
the standard event handler, meaning no further events are
logged. When in doubt, use <c>info_report/1</c> instead.</p>
</warning>
+ <warning>
+ <p>If the Unicode translation modifier (<c>t</c>) is used in
+ the format string, all error handlers must ensure that the
+ formatted output is correctly encoded for the I/O
+ device.</p>
+ </warning>
</desc>
</func>
<func>
@@ -262,7 +274,7 @@ ok</pre>
successful, or <c>{error, allready_have_logfile}</c> if
logging to file is already enabled, or an error tuple if
another error occurred (for example, if <c><anno>Filename</anno></c>
- cannot be opened).</p>
+ cannot be opened). The file is opened with encoding UTF-8.</p>
</item>
<tag><c>close</c></tag>
<item>
@@ -345,6 +357,12 @@ ok</pre>
the standard event handler, meaning no further events are
logged. When in doubt, use <c>warning_report/1</c> instead.</p>
</warning>
+ <warning>
+ <p>If the Unicode translation modifier (<c>t</c>) is used in
+ the format string, all error handlers must ensure that the
+ formatted output is correctly encoded for the I/O
+ device.</p>
+ </warning>
</desc>
</func>
<func>
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index 75e1e18d86..e5ac031539 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -186,7 +186,7 @@
<tag><c>{file, FileName}</c></tag>
<item><p>Installs the standard event handler, which prints error
reports to file <c>FileName</c>, where <c>FileName</c>
- is a string.</p></item>
+ is a string. The file is opened with encoding UTF-8.</p></item>
<tag><c>false</c></tag>
<item>
<p>No standard event handler is installed, but
diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml
index 7ddb849824..0b94fc0fa6 100644
--- a/lib/kernel/doc/src/net_kernel.xml
+++ b/lib/kernel/doc/src/net_kernel.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>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 7127a59a0c..e1cf45109d 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,161 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 5.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Function <c>inet:ntoa/1</c> has been fixed to return
+ lowercase letters according to RFC 5935 that has been
+ approved after this function was written. Previously
+ uppercase letters were returned so this may be a
+ backwards incompatible change depending on how the
+ returned address string is used.</p>
+ <p>Function <c>inet:parse_address/1</c> has been fixed to
+ accept %-suffixes on scoped addresses. The addresses does
+ not work yet, but gives no parse errors.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13006 Aux Id: ERIERL-20, ERL-429 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug where gethostname would incorrectly fail with
+ enametoolong on Linux.</p>
+ <p>
+ Own Id: OTP-14310</p>
+ </item>
+ <item>
+ <p>
+ Fix bug causing <c>code:is_module_native</c> to falsely
+ return true when <c>local</c> call trace is enabled for
+ the module.</p>
+ <p>
+ Own Id: OTP-14390</p>
+ </item>
+ <item>
+ <p>
+ Add early reject of invalid node names from distributed
+ nodes.</p>
+ <p>
+ Own Id: OTP-14426</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Since Unicode is now allowed in atoms an extra check is
+ needed for node names, which are restricted to Latin-1.</p>
+ <p>
+ Own Id: OTP-13805</p>
+ </item>
+ <item>
+ <p>Replaced usage of deprecated symbolic <seealso
+ marker="erts:erlang#type-time_unit"><c>time
+ unit</c></seealso> representations.</p>
+ <p>
+ Own Id: OTP-13831 Aux Id: OTP-13735 </p>
+ </item>
+ <item>
+ <p><c>file:write_file(Name, Data, [raw])</c> would turn
+ <c>Data</c> into a single binary before writing. This
+ meant it could not take advantage of the <c>writev()</c>
+ system call if it was given a list of binaries and told
+ to write with <c>raw</c> mode.</p>
+ <p>
+ Own Id: OTP-13909</p>
+ </item>
+ <item>
+ <p>The performance of the <c>disk_log</c> has been
+ somewhat improved in some corner cases (big items), and
+ the documentation has been clarified. </p>
+ <p>
+ Own Id: OTP-14057 Aux Id: PR-1245 </p>
+ </item>
+ <item>
+ <p>Functions for detecting changed code has been added.
+ <c>code:modified_modules/0</c> returns all currently
+ loaded modules that have changed on disk.
+ <c>code:module_status/1</c> returns the status for a
+ module. In the shell and in <c>c</c> module, <c>mm/0</c>
+ is short for <c>code:modified_modules/0</c>, and
+ <c>lm/0</c> reloads all currently loaded modules that
+ have changed on disk.</p>
+ <p>
+ Own Id: OTP-14059</p>
+ </item>
+ <item>
+ <p>
+ Introduce an event manager in Erlang to handle OS
+ signals. A subset of OS signals may be subscribed to and
+ those are described in the Kernel application.</p>
+ <p>
+ Own Id: OTP-14186</p>
+ </item>
+ <item>
+ <p> Sockets can now be bound to device (SO_BINDTODEVICE)
+ on platforms where it is supported. </p> <p> This has
+ been implemented e.g to support VRF-Lite under Linux; see
+ <url
+ href="https://www.kernel.org/doc/Documentation/networking/vrf.txt">
+ VRF </url>, and GitHub pull request <url
+ href="https://github.com/erlang/otp/pull/1326">#1326</url>.
+ </p>
+ <p>
+ Own Id: OTP-14357 Aux Id: PR-1326 </p>
+ </item>
+ <item>
+ <p>
+ Added option to store shell_history on disk so that the
+ history can be reused between sessions.</p>
+ <p>
+ Own Id: OTP-14409 Aux Id: PR-1420 </p>
+ </item>
+ <item>
+ <p> The size of crash reports created by
+ <c>gen_server</c>, <c>gen_statem</c> and <c>proc_lib</c>
+ is limited with aid of the Kernel application variable
+ <c>error_logger_format_depth</c>. The purpose is to limit
+ the size of the messages sent to the <c>error_logger</c>
+ process when processes with huge message queues or states
+ crash. </p> <p>The crash report generated by
+ <c>proc_lib</c> includes the new tag
+ <c>message_queue_len</c>. The neighbour report also
+ includes the new tag <c>current_stacktrace</c>. Finally,
+ the neighbour report no longer includes the tags
+ <c>messages</c> and <c>dictionary</c>. </p> <p> The new
+ function <c>error_logger:get_format_depth/0</c> can be
+ used to retrieve the value of the Kernel application
+ variable <c>error_logger_format_depth</c>. </p>
+ <p>
+ Own Id: OTP-14417</p>
+ </item>
+ <item>
+ <p> One of the ETS tables used by the <c>global</c>
+ module is created with <c>{read_concurrency, true}</c> in
+ order to reduce contention. </p>
+ <p>
+ Own Id: OTP-14419</p>
+ </item>
+ <item>
+ <p>
+ Warnings have been added to the relevant documentation
+ about not using un-secure distributed nodes in exposed
+ environments.</p>
+ <p>
+ Own Id: OTP-14425</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 5.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile
index 78aa6192a9..5946620f0f 100644
--- a/lib/kernel/src/Makefile
+++ b/lib/kernel/src/Makefile
@@ -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.
diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl
index 10c22e1ad6..93856aa7b3 100644
--- a/lib/kernel/src/disk_log_1.erl
+++ b/lib/kernel/src/disk_log_1.erl
@@ -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/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl
index ad92aafc2f..480db6814e 100644
--- a/lib/kernel/src/erts_debug.erl
+++ b/lib/kernel/src/erts_debug.erl
@@ -33,10 +33,10 @@
-export([breakpoint/2, disassemble/1, display/1, dist_ext_to_term/2,
dump_monitors/1, dump_links/1, flat_size/1,
- get_internal_state/1, instructions/0, lock_counters/1,
+ get_internal_state/1, instructions/0,
map_info/1, same/2, set_internal_state/2,
- size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2,
- dirty/3]).
+ size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, dirty/3,
+ lcnt_control/1, lcnt_control/2, lcnt_collect/0, lcnt_clear/0]).
-spec breakpoint(MFA, Flag) -> non_neg_integer() when
MFA :: {Module :: module(),
@@ -142,12 +142,31 @@ ic(F) when is_function(F) ->
io:format("Total: ~w~n",[lists:sum([C||{_I,C}<-Is])]),
R.
--spec lock_counters(info) -> term();
- (clear) -> ok;
- ({copy_save, boolean()}) -> boolean();
- ({process_locks, boolean()}) -> boolean().
+-spec lcnt_control
+ (copy_save, boolean()) -> ok;
+ (mask, list(atom())) -> ok.
-lock_counters(_) ->
+lcnt_control(_Option, _Value) ->
+ erlang:nif_error(undef).
+
+-spec lcnt_control
+ (copy_save) -> boolean();
+ (mask) -> list(atom()).
+
+lcnt_control(_Option) ->
+ erlang:nif_error(undef).
+
+-type lcnt_lock_info() :: {atom(), term(), atom(), term()}.
+
+-spec lcnt_collect() ->
+ list({duration, {non_neg_integer(), non_neg_integer()}} |
+ {locks, list(lcnt_lock_info())}).
+
+lcnt_collect() ->
+ erlang:nif_error(undef).
+
+-spec lcnt_clear() -> ok.
+lcnt_clear() ->
erlang:nif_error(undef).
-spec same(Term1, Term2) -> boolean() when
diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl
index 0eeaaad8d2..bf785959ff 100644
--- a/lib/kernel/src/group.erl
+++ b/lib/kernel/src/group.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.
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 1128ee3ec5..2a11b04310 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -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.
@@ -120,6 +120,6 @@
{applications, []},
{env, [{error_logger, tty}]},
{mod, {kernel, []}},
- {runtime_dependencies, ["erts-9.0", "stdlib-3.0", "sasl-3.0"]}
+ {runtime_dependencies, ["erts-9.1", "stdlib-3.0", "sasl-3.0"]}
]
}.
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index 3f11e25b93..12d22519ce 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1998-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/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 929f66d400..331864b5de 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -50,9 +50,8 @@
killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1,
several_accepts_in_one_go/1, accept_system_limit/1,
active_once_closed/1, send_timeout/1, send_timeout_active/1,
- otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1,
- wrapping_oct/0, wrapping_oct/1,
- otp_9389/1]).
+ otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1,
+ wrapping_oct/0, wrapping_oct/1, otp_9389/1, otp_13939/1]).
%% Internal exports.
-export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1,
@@ -3014,3 +3013,42 @@ ok({ok,V}) -> V.
get_hostname(Name) ->
"@"++Host = lists:dropwhile(fun(C) -> C =/= $@ end, atom_to_list(Name)),
Host.
+
+otp_13939(doc) ->
+ ["Check that writing to a remotely closed socket doesn't block forever "
+ "when exit_on_close is false."];
+otp_13939(suite) ->
+ [];
+otp_13939(Config) when is_list(Config) ->
+ {Pid, Ref} = spawn_opt(
+ fun() ->
+ {ok, Listener} = gen_tcp:listen(0, [{exit_on_close, false}]),
+ {ok, Port} = inet:port(Listener),
+
+ spawn_link(
+ fun() ->
+ {ok, Client} = gen_tcp:connect("localhost", Port,
+ [{active, false}]),
+ ok = gen_tcp:close(Client)
+ end),
+
+ {ok, Accepted} = gen_tcp:accept(Listener),
+
+ ok = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>),
+
+ %% The bug surfaces when there's a delay between the send
+ %% operations; inet:getstat is a red herring.
+ timer:sleep(100),
+
+ {error, Code} = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>),
+ ct:pal("gen_tcp:send returned ~p~n", [Code])
+ end, [link, monitor]),
+
+ receive
+ {'DOWN', Ref, process, Pid, normal} ->
+ ok
+ after 1000 ->
+ demonitor(Ref, [flush]),
+ exit(Pid, normal),
+ ct:fail("Server process blocked on send.")
+ end.
diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl
index 836e0c5a05..aa616d43d6 100644
--- a/lib/kernel/test/gen_udp_SUITE.erl
+++ b/lib/kernel/test/gen_udp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1998-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/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl
index 9413cbd976..ada9c2689c 100644
--- a/lib/kernel/test/inet_sockopt_SUITE.erl
+++ b/lib/kernel/test/inet_sockopt_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 1233e362f2..53a9e168ef 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -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/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl
index e839959623..bfa564c32c 100644
--- a/lib/kernel/test/sendfile_SUITE.erl
+++ b/lib/kernel/test/sendfile_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index 76b020e8ed..4edecd8969 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 5.2
+KERNEL_VSN = 5.3
diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index a05a339003..068389c0c2 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2016</year>
+ <year>2000</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -37,7 +37,22 @@
section is the version number of Megaco.</p>
- <section><title>Megaco 3.18.1</title>
+ <section><title>Megaco 3.18.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Typos have been fixed.</p>
+ <p>
+ Own Id: OTP-14387</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Megaco 3.18.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index 0d40f863b2..9c6ba5bba0 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -2,7 +2,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.
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.18.1
+MEGACO_VSN = 3.18.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 062a62298b..3ca4026190 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -39,7 +39,26 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.14.3</title>
+ <section><title>Mnesia 4.15</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Removed the wrapping of select continuations in extension
+ plugin handling. This might require the user to rewrite
+ user backend plugin if used.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14039</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.14.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index 93a7e21f4e..6b93935cb4 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.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.
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index e272a469bb..81b15d65db 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.14.3
+MNESIA_VSN = 4.15
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index bd9aa265f8..d329be5d5a 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -32,6 +32,63 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>etop</c> had a hardcoded timeout value of 1 second
+ when waiting for data from a remote node. When this
+ expired, which could happen for instance if there were
+ very many processes on the remote node, etop would exit
+ with reason <c>connection_lost</c>. To overcome this
+ problem, the timeout is now changed to be the same as the
+ update interval, which is configurable.</p>
+ <p>
+ Own Id: OTP-14393</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Show dirty-scheduler threads in performance monitor graph
+ and add a column with maximum allocated memory in the
+ Memory Allocators table.</p>
+ <p>
+ Own Id: OTP-14137</p>
+ </item>
+ <item>
+ <p>
+ Keep table and port selection after refresh of tables.
+ Store settings before shutdown and restore when starting
+ application.</p>
+ <p>
+ Own Id: OTP-14270</p>
+ </item>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ <item>
+ <p>
+ When observing a node older than OTP-19.0, a pop-up will
+ be displayed when trying to access port information.
+ Earlier, observer would crash in this situation.</p>
+ <p>
+ Own Id: OTP-14345 Aux Id: ERL-399 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 2.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/observer/src/etop.erl b/lib/observer/src/etop.erl
index 2093e6a0d7..f0990f1f25 100644
--- a/lib/observer/src/etop.erl
+++ b/lib/observer/src/etop.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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.
@@ -81,7 +81,7 @@ check_runtime_config(accumulate,A) when A=:=true; A=:=false -> ok;
check_runtime_config(_Key,_Value) -> error.
dump(File) ->
- case file:open(File,[write]) of
+ case file:open(File,[write,{encoding,utf8}]) of
{ok,Fd} -> etop_server ! {dump,Fd};
Error -> Error
end.
@@ -161,7 +161,7 @@ data_handler(Reader, Opts) ->
{'EXIT', EPid, Reason} when EPid == Opts#opts.out_proc ->
case Reason of
normal -> ok;
- _ -> io:format("Output server crashed: ~p~n",[Reason])
+ _ -> io:format("Output server crashed: ~tp~n",[Reason])
end,
stop(Opts),
out_proc_stopped;
diff --git a/lib/observer/src/etop_txt.erl b/lib/observer/src/etop_txt.erl
index 6b8f9df24f..183641119a 100644
--- a/lib/observer/src/etop_txt.erl
+++ b/lib/observer/src/etop_txt.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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.
@@ -29,8 +29,6 @@
-import(etop,[loadinfo/2,meminfo/2]).
--define(PROCFORM,"~-15w~-20s~8w~8w~8w~8w ~-20s~n").
-
stop(Pid) -> Pid ! stop.
init(Config) ->
@@ -50,6 +48,7 @@ do_update(Prev,Config) ->
do_update(standard_io,Info,Prev,Config).
do_update(Fd,Info,Prev,Config) ->
+ Encoding = encoding(Fd),
{Cpu,NProcs,RQ,Clock} = loadinfo(Info,Prev),
io:nl(Fd),
writedoubleline(Fd),
@@ -73,7 +72,7 @@ do_update(Fd,Info,Prev,Config) ->
io:nl(Fd),
writepinfo_header(Fd),
writesingleline(Fd),
- writepinfo(Fd,Info#etop_info.procinfo),
+ writepinfo(Fd,Info#etop_info.procinfo,Encoding),
writedoubleline(Fd),
io:nl(Fd),
Info.
@@ -93,19 +92,37 @@ writepinfo(Fd,[#etop_proc_info{pid=Pid,
runtime=Time,
cf=MFA,
mq=MQ}
- |T]) ->
- io:fwrite(Fd,?PROCFORM,[Pid,to_list(Name),Time,Reds,Mem,MQ,formatmfa(MFA)]),
- writepinfo(Fd,T);
-writepinfo(_Fd,[]) ->
+ |T],
+ Encoding) ->
+ io:fwrite(Fd,proc_format(Encoding),
+ [Pid,to_list(Name,Encoding),Time,Reds,Mem,MQ,
+ formatmfa(MFA,Encoding)]),
+ writepinfo(Fd,T,Encoding);
+writepinfo(_Fd,[],_) ->
ok.
-formatmfa({M, F, A}) ->
+formatmfa({M, F, A},latin1) ->
io_lib:format("~w:~w/~w",[M, F, A]);
-formatmfa(Other) ->
+formatmfa({M, F, A},_) ->
+ io_lib:format("~w:~tw/~w",[M, F, A]);
+formatmfa(Other,_) ->
%% E.g. when running hipe - the current_function for some
%% processes will be 'undefined'
io_lib:format("~w",[Other]).
-to_list(Name) when is_atom(Name) -> atom_to_list(Name);
-to_list({_M,_F,_A}=MFA) -> formatmfa(MFA).
+to_list(Name,_) when is_atom(Name) -> atom_to_list(Name);
+to_list({_M,_F,_A}=MFA,Encoding) -> formatmfa(MFA,Encoding).
+
+encoding(Device) ->
+ case io:getopts(Device) of
+ List when is_list(List) ->
+ proplists:get_value(encoding,List,latin1);
+ _ ->
+ latin1
+ end.
+
+proc_format(latin1) ->
+ "~-15w~-20s~8w~8w~8w~8w ~-20s~n";
+proc_format(_) ->
+ "~-15w~-20ts~8w~8w~8w~8w ~-20ts~n".
diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl
index 87a50e046b..09b0bc6710 100644
--- a/lib/observer/src/ttb.erl
+++ b/lib/observer/src/ttb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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.
@@ -391,16 +391,16 @@ run_config(ConfigFile,N) ->
print_func(M,F,A) ->
Args = arg_list(A,[]),
- io:format("~w:~w(~s) ->~n",[M,F,Args]).
+ io:format("~w:~tw(~ts) ->~n",[M,F,Args]).
print_result(R) ->
- io:format("~p~n~n",[R]).
+ io:format("~tp~n~n",[R]).
arg_list([],[]) ->
"";
arg_list([A1],Acc) ->
- Acc++io_lib:format("~w",[A1]);
+ Acc++io_lib:format("~tw",[A1]);
arg_list([A1|A],Acc) ->
- arg_list(A,Acc++io_lib:format("~w,",[A1])).
+ arg_list(A,Acc++io_lib:format("~tw,",[A1])).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1117,7 +1117,7 @@ get_fd(Out) ->
Out;
_file ->
file:delete(Out),
- case file:open(Out,[append]) of
+ case file:open(Out,[append,{encoding,utf8}]) of
{ok,Fd} -> Fd;
Error -> exit(Error)
end
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index ca9ad72473..21edfcd184 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 2.3.1
+OBSERVER_VSN = 2.4
diff --git a/lib/orber/test/multi_ORB_SUITE.erl b/lib/orber/test/multi_ORB_SUITE.erl
index 8becc11d6a..3c5bb0b5e5 100644
--- a/lib/orber/test/multi_ORB_SUITE.erl
+++ b/lib/orber/test/multi_ORB_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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/lib/orber/test/orber_test_lib.erl b/lib/orber/test/orber_test_lib.erl
index 4a247ce492..9b19c4bc4e 100644
--- a/lib/orber/test/orber_test_lib.erl
+++ b/lib/orber/test/orber_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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/lib/parsetools/doc/src/notes.xml b/lib/parsetools/doc/src/notes.xml
index 5a16445577..3fa7169f50 100644
--- a/lib/parsetools/doc/src/notes.xml
+++ b/lib/parsetools/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,33 @@
</header>
<p>This document describes the changes made to the Parsetools application.</p>
+<section><title>Parsetools 2.1.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Minor documentation fixes</p>
+ <p>
+ Own Id: OTP-14276 Aux Id: PR-1357 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Parsetools 2.1.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk
index d102c63730..502ca00a47 100644
--- a/lib/parsetools/vsn.mk
+++ b/lib/parsetools/vsn.mk
@@ -1 +1 @@
-PARSETOOLS_VSN = 2.1.4
+PARSETOOLS_VSN = 2.1.5
diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index dd83e1961d..64592a6d87 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -35,6 +35,41 @@
<file>notes.xml</file>
</header>
+<section><title>Public_Key 1.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Bug for <c>public_key:generate_key({namedCurve,OID})</c>
+ fixed.</p>
+ <p>
+ Own Id: OTP-14258</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Modernized internal representation used for crl
+ validation by use of maps.</p>
+ <p>
+ Own Id: OTP-14111</p>
+ </item>
+ <item>
+ <p>
+ Support EC key in pkix_sign/2</p>
+ <p>
+ Own Id: OTP-14294</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 1.4</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index b94768ae77..83a77d2a28 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 1.4
+PUBLIC_KEY_VSN = 1.4.1
diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml
index b47d451055..8593a1017f 100644
--- a/lib/reltool/doc/src/notes.xml
+++ b/lib/reltool/doc/src/notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2016</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -38,7 +38,23 @@
thus constitutes one section in this document. The title of each
section is the version number of Reltool.</p>
- <section><title>Reltool 0.7.3</title>
+ <section><title>Reltool 0.7.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The User's Guide examples are updated after removal
+ of support for Dets files created with Erlang/OTP R7 and
+ earlier. </p>
+ <p>
+ Own Id: OTP-14422 Aux Id: OTP-13830 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Reltool 0.7.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/reltool/doc/src/reltool_examples.xml b/lib/reltool/doc/src/reltool_examples.xml
index 49bde56964..30cb3c13b6 100644
--- a/lib/reltool/doc/src/reltool_examples.xml
+++ b/lib/reltool/doc/src/reltool_examples.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2016</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index 3b1e868757..8b4898570b 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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/lib/reltool/vsn.mk b/lib/reltool/vsn.mk
index 2d07eeb8f0..3617f6e0d9 100644
--- a/lib/reltool/vsn.mk
+++ b/lib/reltool/vsn.mk
@@ -1 +1 @@
-RELTOOL_VSN = 0.7.3
+RELTOOL_VSN = 0.7.4
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 49615cf077..d50994306b 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -32,6 +32,63 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.12.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A faulty encoding comment was added when saving trace
+ patterns to file. This is now corrected.</p>
+ <p>
+ Own Id: OTP-14479</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Runtime_Tools 1.12</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add compile option <c>-compile(no_native)</c> in modules
+ with <c>on_load</c> directive which is not yet supported
+ by HiPE.</p>
+ <p>
+ Own Id: OTP-14316 Aux Id: PR-1390 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ <item>
+ <p> Sockets can now be bound to device (SO_BINDTODEVICE)
+ on platforms where it is supported. </p> <p> This has
+ been implemented e.g to support VRF-Lite under Linux; see
+ <url
+ href="https://www.kernel.org/doc/Documentation/networking/vrf.txt">
+ VRF </url>, and GitHub pull request <url
+ href="https://github.com/erlang/otp/pull/1326">#1326</url>.
+ </p>
+ <p>
+ Own Id: OTP-14357 Aux Id: PR-1326 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.11.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 9989cf5c07..92938ed5c1 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -50,7 +50,8 @@ fun2ms(ShellFun) when is_function(ShellFun) ->
case ms_transform:transform_from_shell(
?MODULE,Clauses,ImportList) of
{error,[{_,[{_,_,Code}|_]}|_],_} ->
- io:format("Error: ~ts~n",
+ Modifier = modifier(),
+ io:format("Error: ~"++Modifier++"s~n",
[ms_transform:format_error(Code)]),
{error,transform_error};
Else ->
@@ -233,8 +234,10 @@ ctpe(Event) when Event =:= send;
%% List saved and built-in trace patterns.
%%
ltp() ->
+ Modifier = modifier(),
+ Format = "~p: ~"++Modifier++"p~n",
pt_doforall(fun({X, El},_Ignore) ->
- io:format("~p: ~p~n", [X,El])
+ io:format(Format, [X,El])
end,[]).
%%
@@ -261,12 +264,13 @@ dtp(_) ->
%%
%% Actually write the built-in trace patterns too.
wtp(FileName) ->
- case file:open(FileName,[write]) of
+ case file:open(FileName,[write,{encoding,utf8}]) of
{error, Reason} ->
{error, Reason};
{ok, File} ->
+ io:format(File, "%% ~s\n", [epp:encoding_to_string(utf8)]),
pt_doforall(fun ({_, Val}, _) when is_list(Val) ->
- io:format(File, "~p.~n", [Val]);
+ io:format(File, "~tp.~n", [Val]);
({_, _}, _) ->
ok
end,
@@ -667,7 +671,9 @@ init(Parent) ->
loop({C,T}=SurviveLinks, Table) ->
receive
{From,i} ->
- reply(From, display_info(lists:map(fun({N,_}) -> N end,get()))),
+ Modifier = modifier(),
+ Reply = display_info(lists:map(fun({N,_}) -> N end,get()), Modifier),
+ reply(From, Reply),
loop(SurviveLinks, Table);
{From,{p,Pid,Flags}} ->
reply(From, trace_process(Pid, Flags)),
@@ -773,7 +779,10 @@ loop({C,T}=SurviveLinks, Table) ->
C ->
case lists:delete(Pid,T) of
T ->
- io:format(user,"** dbg got EXIT - terminating: ~p~n",
+ Modifier = modifier(user),
+ io:format(user,
+ "** dbg got EXIT - terminating: ~"++
+ Modifier++"p~n",
[Reason]),
exit(done);
NewT ->
@@ -784,7 +793,8 @@ loop({C,T}=SurviveLinks, Table) ->
loop({NewC,T}, Table)
end;
Other ->
- io:format(user,"** dbg got garbage: ~p~n",
+ Modifier = modifier(user),
+ io:format(user,"** dbg got garbage: ~"++Modifier++"p~n",
[{Other,SurviveLinks,Table}]),
loop(SurviveLinks, Table)
end.
@@ -836,7 +846,9 @@ recv_all_traces(Suspended0, Traces, Timeout) ->
{done, Suspended0, Traces};
Other ->
%%% Is this really a good idea?
- io:format(user,"** tracer received garbage: ~p~n", [Other]),
+ Modifier = modifier(user),
+ io:format(user,"** tracer received garbage: ~"++Modifier++"p~n",
+ [Other]),
recv_all_traces(Suspended0, Traces, Timeout)
after Timeout ->
{loop, Suspended0, Traces}
@@ -962,165 +974,224 @@ do_relay_1(RelP) ->
RelP ! TraceInfo,
do_relay_1(RelP);
Other ->
- io:format(user,"** relay got garbage: ~p~n", [Other]),
+ Modifier = modifier(user),
+ io:format(user,"** relay got garbage: ~"++Modifier++"p~n", [Other]),
do_relay_1(RelP)
end.
dhandler(end_of_trace, Out) ->
Out;
dhandler(Trace, Out) when element(1, Trace) == trace, tuple_size(Trace) >= 3 ->
- dhandler1(Trace, tuple_size(Trace), Out);
+ dhandler1(Trace, tuple_size(Trace), out(Out));
dhandler(Trace, Out) when element(1, Trace) == trace_ts, tuple_size(Trace) >= 4 ->
- dhandler1(Trace, tuple_size(Trace)-1, element(tuple_size(Trace),Trace), Out);
+ dhandler1(Trace, tuple_size(Trace)-1, element(tuple_size(Trace),Trace)
+ , out(Out));
dhandler(Trace, Out) when element(1, Trace) == drop, tuple_size(Trace) =:= 2 ->
- io:format(Out, "*** Dropped ~p messages.~n", [element(2,Trace)]),
- Out;
-dhandler(Trace, Out) when element(1, Trace) == seq_trace, tuple_size(Trace) >= 3 ->
+ {Device,Modifier} = out(Out),
+ io:format(Device, "*** Dropped ~p messages.~n", [element(2,Trace)]),
+ {Device,Modifier};
+dhandler(Trace, Out) when element(1, Trace) == seq_trace,
+ tuple_size(Trace) >= 3 ->
+ {Device,Modifier} = out(Out),
SeqTraceInfo = case Trace of
{seq_trace, Lbl, STI, TS} ->
- io:format(Out, "SeqTrace ~p [~p]: ",
+ io:format(Device, "SeqTrace ~p [~p]: ",
[TS, Lbl]),
STI;
{seq_trace, Lbl, STI} ->
- io:format(Out, "SeqTrace [~p]: ",
+ io:format(Device, "SeqTrace [~p]: ",
[Lbl]),
STI
end,
case SeqTraceInfo of
{send, Ser, Fr, To, Mes} ->
- io:format(Out, "(~p) ~p ! ~p [Serial: ~p]~n",
+ io:format(Device, "(~p) ~p ! ~"++Modifier++"p [Serial: ~p]~n",
[Fr, To, Mes, Ser]);
{'receive', Ser, Fr, To, Mes} ->
- io:format(Out, "(~p) << ~p [Serial: ~p, From: ~p]~n",
+ io:format(Device, "(~p) << ~"++Modifier++"p [Serial: ~p, From: ~p]~n",
[To, Mes, Ser, Fr]);
{print, Ser, Fr, _, Info} ->
- io:format(Out, "-> ~p [Serial: ~p, From: ~p]~n",
+ io:format(Device, "-> ~"++Modifier++"p [Serial: ~p, From: ~p]~n",
[Info, Ser, Fr]);
Else ->
- io:format(Out, "~p~n", [Else])
+ io:format(Device, "~"++Modifier++"p~n", [Else])
end,
- Out;
+ {Device,Modifier};
dhandler(_Trace, Out) ->
Out.
-dhandler1(Trace, Size, Out) ->
+dhandler1(Trace, Size, {Device,Modifier}) ->
From = element(2, Trace),
case element(3, Trace) of
'receive' ->
case element(4, Trace) of
{dbg,ok} -> ok;
Message ->
- io:format(Out, "(~p) << ~p~n", [From,Message])
+ io:format(Device, "(~p) << ~"++Modifier++"p~n",
+ [From,Message])
end;
'send' ->
Message = element(4, Trace),
To = element(5, Trace),
- io:format(Out, "(~p) ~p ! ~p~n", [From,To,Message]);
+ io:format(Device, "(~p) ~p ! ~"++Modifier++"p~n", [From,To,Message]);
call ->
case element(4, Trace) of
MFA when Size == 5 ->
Message = element(5, Trace),
- io:format(Out, "(~p) call ~s (~p)~n", [From,ffunc(MFA),Message]);
+ io:format(Device,
+ "(~p) call ~"++Modifier++"s (~"++Modifier++"p)~n",
+ [From,ffunc(MFA,Modifier),Message]);
MFA ->
- io:format(Out, "(~p) call ~s~n", [From,ffunc(MFA)])
+ io:format(Device, "(~p) call ~"++Modifier++"s~n",
+ [From,ffunc(MFA,Modifier)])
end;
return -> %% To be deleted...
case element(4, Trace) of
MFA when Size == 5 ->
Ret = element(5, Trace),
- io:format(Out, "(~p) old_ret ~s -> ~p~n", [From,ffunc(MFA),Ret]);
+ io:format(Device,
+ "(~p) old_ret ~"++Modifier++"s -> ~"++Modifier++
+ "p~n",
+ [From,ffunc(MFA,Modifier),Ret]);
MFA ->
- io:format(Out, "(~p) old_ret ~s~n", [From,ffunc(MFA)])
+ io:format(Device, "(~p) old_ret ~"++Modifier++"s~n",
+ [From,ffunc(MFA,Modifier)])
end;
return_from ->
MFA = element(4, Trace),
Ret = element(5, Trace),
- io:format(Out, "(~p) returned from ~s -> ~p~n", [From,ffunc(MFA),Ret]);
+ io:format(Device,
+ "(~p) returned from ~"++Modifier++"s -> ~"++Modifier++"p~n",
+ [From,ffunc(MFA,Modifier),Ret]);
return_to ->
MFA = element(4, Trace),
- io:format(Out, "(~p) returning to ~s~n", [From,ffunc(MFA)]);
+ io:format(Device, "(~p) returning to ~"++Modifier++"s~n",
+ [From,ffunc(MFA,Modifier)]);
spawn when Size == 5 ->
Pid = element(4, Trace),
MFA = element(5, Trace),
- io:format(Out, "(~p) spawn ~p as ~s~n", [From,Pid,ffunc(MFA)]);
+ io:format(Device, "(~p) spawn ~p as ~"++Modifier++"s~n",
+ [From,Pid,ffunc(MFA,Modifier)]);
Op ->
- io:format(Out, "(~p) ~p ~s~n", [From,Op,ftup(Trace,4,Size)])
+ io:format(Device, "(~p) ~p ~"++Modifier++"s~n",
+ [From,Op,ftup(Trace,4,Size,Modifier)])
end,
- Out.
+ {Device,Modifier}.
-dhandler1(Trace, Size, TS, Out) ->
+dhandler1(Trace, Size, TS, {Device,Modifier}) ->
From = element(2, Trace),
case element(3, Trace) of
'receive' ->
case element(4, Trace) of
{dbg,ok} -> ok;
Message ->
- io:format(Out, "(~p) << ~p (Timestamp: ~p)~n", [From,Message,TS])
+ io:format(Device,
+ "(~p) << ~"++Modifier++"p (Timestamp: ~p)~n",
+ [From,Message,TS])
end;
'send' ->
Message = element(4, Trace),
To = element(5, Trace),
- io:format(Out, "(~p) ~p ! ~p (Timestamp: ~p)~n", [From,To,Message,TS]);
+ io:format(Device, "(~p) ~p ! ~"++Modifier++"p (Timestamp: ~p)~n",
+ [From,To,Message,TS]);
call ->
case element(4, Trace) of
MFA when Size == 5 ->
Message = element(5, Trace),
- io:format(Out, "(~p) call ~s (~p) (Timestamp: ~p)~n", [From,ffunc(MFA),Message,TS]);
+ io:format(Device,
+ "(~p) call ~"++Modifier++"s (~"++Modifier++
+ "p) (Timestamp: ~p)~n",
+ [From,ffunc(MFA,Modifier),Message,TS]);
MFA ->
- io:format(Out, "(~p) call ~s (Timestamp: ~p)~n", [From,ffunc(MFA),TS])
+ io:format(Device,
+ "(~p) call ~"++Modifier++"s (Timestamp: ~p)~n",
+ [From,ffunc(MFA,Modifier),TS])
end;
return -> %% To be deleted...
case element(4, Trace) of
MFA when Size == 5 ->
Ret = element(5, Trace),
- io:format(Out, "(~p) old_ret ~s -> ~p (Timestamp: ~p)~n", [From,ffunc(MFA),Ret,TS]);
+ io:format(Device,
+ "(~p) old_ret ~"++Modifier++"s -> ~"++Modifier++
+ "p (Timestamp: ~p)~n",
+ [From,ffunc(MFA,Modifier),Ret,TS]);
MFA ->
- io:format(Out, "(~p) old_ret ~s (Timestamp: ~p)~n", [From,ffunc(MFA),TS])
+ io:format(Device,
+ "(~p) old_ret ~"++Modifier++"s (Timestamp: ~p)~n",
+ [From,ffunc(MFA,Modifier),TS])
end;
return_from ->
MFA = element(4, Trace),
Ret = element(5, Trace),
- io:format(Out, "(~p) returned from ~s -> ~p (Timestamp: ~p)~n", [From,ffunc(MFA),Ret,TS]);
+ io:format(Device,
+ "(~p) returned from ~"++Modifier++"s -> ~"++Modifier++
+ "p (Timestamp: ~p)~n",
+ [From,ffunc(MFA,Modifier),Ret,TS]);
return_to ->
MFA = element(4, Trace),
- io:format(Out, "(~p) returning to ~s (Timestamp: ~p)~n", [From,ffunc(MFA),TS]);
+ io:format(Device,
+ "(~p) returning to ~"++Modifier++"s (Timestamp: ~p)~n",
+ [From,ffunc(MFA,Modifier),TS]);
spawn when Size == 5 ->
Pid = element(4, Trace),
MFA = element(5, Trace),
- io:format(Out, "(~p) spawn ~p as ~s (Timestamp: ~p)~n", [From,Pid,ffunc(MFA),TS]);
+ io:format(Device,
+ "(~p) spawn ~p as ~"++Modifier++"s (Timestamp: ~p)~n",
+ [From,Pid,ffunc(MFA,Modifier),TS]);
Op ->
- io:format(Out, "(~p) ~p ~s (Timestamp: ~p)~n", [From,Op,ftup(Trace,4,Size),TS])
+ io:format(Device, "(~p) ~p ~"++Modifier++"s (Timestamp: ~p)~n",
+ [From,Op,ftup(Trace,4,Size,Modifier),TS])
end,
- Out.
-
-
+ {Device,Modifier}.
%%% These f* functions returns non-flat strings
%% {M,F,[A1, A2, ..., AN]} -> "M:F(A1, A2, ..., AN)"
%% {M,F,A} -> "M:F/A"
-ffunc({M,F,Argl}) when is_list(Argl) ->
- io_lib:format("~p:~p(~s)", [M, F, fargs(Argl)]);
-ffunc({M,F,Arity}) ->
- io_lib:format("~p:~p/~p", [M,F,Arity]);
-ffunc(X) -> io_lib:format("~p", [X]).
+ffunc({M,F,Argl},Modifier) when is_list(Argl) ->
+ io_lib:format("~p:~"++Modifier++"p(~"++Modifier++"s)",
+ [M, F, fargs(Argl,Modifier)]);
+ffunc({M,F,Arity},Modifier) ->
+ io_lib:format("~p:~"++Modifier++"p/~p", [M,F,Arity]);
+ffunc(X,Modifier) -> io_lib:format("~"++Modifier++"p", [X]).
%% Integer -> "Integer"
%% [A1, A2, ..., AN] -> "A1, A2, ..., AN"
-fargs(Arity) when is_integer(Arity) -> integer_to_list(Arity);
-fargs([]) -> [];
-fargs([A]) -> io_lib:format("~p", [A]); %% last arg
-fargs([A|Args]) -> [io_lib:format("~p,", [A]) | fargs(Args)];
-fargs(A) -> io_lib:format("~p", [A]). % last or only arg
+fargs(Arity,_) when is_integer(Arity) -> integer_to_list(Arity);
+fargs([],_) -> [];
+fargs([A],Modifier) ->
+ io_lib:format("~"++Modifier++"p", [A]); %% last arg
+fargs([A|Args],Modifier) ->
+ [io_lib:format("~"++Modifier++"p,", [A]) | fargs(Args,Modifier)];
+fargs(A,Modifier) ->
+ io_lib:format("~"++Modifier++"p", [A]). % last or only arg
%% {A_1, A_2, ..., A_N} -> "A_Index A_Index+1 ... A_Size"
-ftup(Trace, Index, Index) ->
- io_lib:format("~p", [element(Index, Trace)]);
-ftup(Trace, Index, Size) ->
- [io_lib:format("~p ", [element(Index, Trace)])
- | ftup(Trace, Index+1, Size)].
-
+ftup(Trace, Index, Index, Modifier) ->
+ io_lib:format("~"++Modifier++"p", [element(Index, Trace)]);
+ftup(Trace, Index, Size, Modifier) ->
+ [io_lib:format("~"++Modifier++"p ", [element(Index, Trace)])
+ | ftup(Trace, Index+1, Size, Modifier)].
+out({_,_}=Out) ->
+ Out;
+out(Device) ->
+ {Device,modifier(Device)}.
+
+modifier() ->
+ modifier(group_leader()).
+modifier(Device) ->
+ Encoding =
+ case io:getopts(Device) of
+ List when is_list(List) ->
+ proplists:get_value(encoding,List,latin1);
+ _ ->
+ latin1
+ end,
+ encoding_to_modifier(Encoding).
+
+encoding_to_modifier(latin1) -> "";
+encoding_to_modifier(_) -> "t".
trace_process(Pid, [clear]) ->
trac(Pid, false, all());
@@ -1157,22 +1228,22 @@ all() ->
timestamp,monotonic_timestamp,strict_monotonic_timestamp,
arity,return_to,silent,running_procs,running_ports,exiting].
-display_info([Node|Nodes]) ->
+display_info([Node|Nodes],Modifier) ->
io:format("~nNode ~w:~n",[Node]),
io:format("~-12s ~-21s Trace ~n", ["Pid", "Initial call"]),
List = rpc:call(Node,?MODULE,get_info,[]),
- display_info1(List),
- display_info(Nodes);
-display_info([]) ->
+ display_info1(List,Modifier),
+ display_info(Nodes,Modifier);
+display_info([],_) ->
ok.
-display_info1([{Pid,Call,Flags}|T]) ->
- io:format("~-12s ~-21s ~s~n",
+display_info1([{Pid,Call,Flags}|T],Modifier) ->
+ io:format("~-12s ~-21"++Modifier++"s ~s~n",
[io_lib:format("~w",[Pid]),
- io_lib:format("~p", [Call]),
+ io_lib:format("~"++Modifier++"p", [Call]),
format_trace(Flags)]),
- display_info1(T);
-display_info1([]) ->
+ display_info1(T,Modifier);
+display_info1([],_) ->
ok.
get_info() ->
@@ -1304,7 +1375,8 @@ tc_loop([], Handler, HData) ->
tc_loop(Reader, Handler, HData) when is_function(Reader) ->
tc_loop(Reader(), Handler, HData);
tc_loop(Other, _Handler, _HData) ->
- io:format("~p:tc_loop ~p~n", [?MODULE, Other]),
+ Modifier = modifier(),
+ io:format("~p:tc_loop ~"++Modifier++"p~n", [?MODULE, Other]),
exit({unknown_term_from_reader, Other}).
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index 8ec532de76..7296221033 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.11.1
+RUNTIME_TOOLS_VSN = 1.12.1
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index 8684151b3a..bc35939d7e 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -31,6 +31,35 @@
</header>
<p>This document describes the changes made to the SASL application.</p>
+<section><title>SASL 3.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Documented default values for the 'mod' and
+ 'start_phases' fields in .app files were not allowed as
+ actual values in a .app file. This is now corrected.</p>
+ <p>
+ Own Id: OTP-14029</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SASL 3.0.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/sasl/src/format_lib_supp.erl b/lib/sasl/src/format_lib_supp.erl
index 0b474a0232..80dcdc91da 100644
--- a/lib/sasl/src/format_lib_supp.erl
+++ b/lib/sasl/src/format_lib_supp.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.
@@ -25,15 +25,12 @@
%%% tools.
%%% The main parts are:
%%% 1) print_info. Prints information tagged by 'header', 'data',
-%%% 'table', 'items' and 'newline'.
+%%% 'items' and 'newline'.
%%%---------------------------------------------------------------------
%% intermodule exports
-export([print_info/2, print_info/3]).
-%% exports for use within module
--export([maxcol/2]).
-
%%---------------------------------------------------------------------
%% Format is an ordered list of:
%% {header, HeaderString}
@@ -42,10 +39,6 @@
%% (if possible); 'Key: Value'.
%% Elements in the list may also be single terms, which are
%% printed as they are.
-%% {table, {TableName, ColumnNames, Columns}}
-%% ColumnNames is a tuple of names for the columns, and
-%% Columns is a list, where each element is a tuple of
-%% data for that column.
%% {items, {Name, Items}}
%% Items is a list of KeyValue_tuples. Will be printed as:
%% 'Name:
@@ -77,9 +70,6 @@ print_format(Device, _Line, []) ->
print_format(Device, Line, [{data, Data}|T]) ->
print_data(Device, Line, Data),
print_format(Device, Line, T);
-print_format(Device, Line, [{table, Table}|T]) ->
- _ = print_table(Device, Line, Table),
- print_format(Device, Line, T);
print_format(Device, Line, [{items, Items}|T]) ->
print_items(Device, Line, Items),
print_format(Device, Line, T);
@@ -94,51 +84,49 @@ print_data(Device, Line, [{Key, Value}|T]) ->
print_one_line(Device, Line, Key, Value),
print_data(Device, Line, T);
print_data(Device, Line, [Value|T]) ->
- io:format(Device, "~p~n", [Value]),
+ Modifier = misc_supp:modifier(Device),
+ io:format(Device, "~"++Modifier++"p~n", [Value]),
print_data(Device, Line, T).
+
print_items(Device, Line, {Name, Items}) ->
print_items(Device, Line, Name, Items).
-print_table(Device, Line, {TableName, ColumnNames, Columns}) ->
- print_table(Device, Line, TableName, ColumnNames, Columns).
-
print_newlines(_Device, 0) -> ok;
print_newlines(Device, N) when N > 0 ->
io:format(Device, '~n', []),
print_newlines(Device, N-1).
print_one_line(Device, Line, Key, Value) ->
- StrKey = term_to_string(Key),
+ Modifier = misc_supp:modifier(Device),
+ StrKey = term_to_string(Key,Modifier),
KeyLen = lists:min([length(StrKey), Line]),
ValueLen = Line - KeyLen,
- Format1 = lists:concat(["~-", KeyLen, s]),
- Format2 = lists:concat(["~", ValueLen, "s~n"]),
+ Format1 = lists:concat(["~-", KeyLen, Modifier, "s"]),
+ Format2 = lists:concat(["~", ValueLen, Modifier, "s~n"]),
io:format(Device, Format1, [StrKey]),
- Try = term_to_string(Value),
+ Try = term_to_string(Value,Modifier),
Length = length(Try),
if
Length < ValueLen ->
io:format(Device, Format2, [Try]);
true ->
io:format(Device, "~n ", []),
- Format3 = lists:concat(["~", Line, ".9p~n"]),
+ Format3 = lists:concat(["~", Line, ".9", Modifier, "p~n"]),
io:format(Device, Format3, [Value])
end.
-term_to_string(Value) ->
- lists:flatten(io_lib:format(get_format(Value), [Value])).
+term_to_string(Value,Modifier) ->
+ lists:flatten(io_lib:format(get_format(Value,Modifier), [Value])).
-get_format(Value) ->
- case misc_supp:is_string(Value) of
- true -> "~s";
- false -> "~p"
+get_format([],_) ->
+ "~p";
+get_format(Value,Modifier) ->
+ case io_lib:printable_list(Value) of
+ true -> "~"++Modifier++"s";
+ false -> "~"++Modifier++"p"
end.
-make_list(0, _Elem) -> [];
-make_list(N, Elem) -> [Elem|make_list(N-1, Elem)].
-
-
%%-----------------------------------------------------------------
%% Items
%%-----------------------------------------------------------------
@@ -150,76 +138,3 @@ print_item_elements(_Device, _Line, []) -> ok;
print_item_elements(Device, Line, [{Key, Value}|T]) ->
print_one_line(Device, Line, lists:concat([" ", Key]), Value),
print_item_elements(Device, Line, T).
-
-%%-----------------------------------------------------------------
-%% Table handling
-%%-----------------------------------------------------------------
-extra_space_between_columns() -> 3.
-
-find_max_col([Row | T], ColumnSizes) ->
- find_max_col(T, misc_supp:multi_map({format_lib_supp, maxcol},
- [Row, ColumnSizes]));
-
-find_max_col([], ColumnSizes) -> ColumnSizes.
-
-maxcol(Term, OldMax) ->
- lists:max([length(term_to_string(Term)), OldMax]).
-
-make_column_format(With) ->
- lists:concat(["~", With + extra_space_between_columns(), s]).
-
-is_correct_column_length(_Length, []) -> true;
-is_correct_column_length(Length, [Tuple|T]) ->
- case size(Tuple) of
- Length -> is_correct_column_length(Length, T);
- _ -> false
- end;
-is_correct_column_length(_, _) -> false.
-
-print_table(Device, Line, TableName, _TupleOfColumnNames, []) ->
- print_one_line(Device, Line, TableName, "<empty table>"),
- io:format(Device, "~n", []);
-
-print_table(Device, Line, TableName, TupleOfColumnNames, ListOfTuples)
- when is_list(ListOfTuples), is_tuple(TupleOfColumnNames) ->
- case is_correct_column_length(size(TupleOfColumnNames),
- ListOfTuples) of
- true ->
- print_one_line(Device, Line, TableName, " "),
- ListOfColumnNames = tuple_to_list(TupleOfColumnNames),
- ListOfLists = lists:map(fun(Tuple) ->
- tuple_to_list(Tuple)
- end,
- ListOfTuples),
- ColWidths = find_max_col([ListOfColumnNames |
- ListOfLists],
- make_list(length(ListOfColumnNames),0)),
- Format = lists:flatten([lists:map(fun(CW) ->
- make_column_format(CW)
- end,
- ColWidths), "~n"]),
- io:format(Device, Format, ListOfColumnNames),
- io:format(Device,
- lists:concat(['~', extra_space_between_columns(),
- 'c', '~', lists:sum(ColWidths)
- + (length(ColWidths) - 1)
- * extra_space_between_columns(),
- 'c~n']), [$ , $-]),
- lists:foreach(fun(List) ->
- print_row(List, Device, Format)
- end,
- ListOfLists),
- io:format(Device, '~n', []),
- true;
- false ->
- {error, {'a tuple has wrong size',
- {TableName, TupleOfColumnNames, ListOfTuples}}}
- end.
-
-%%--------------------------------------------------
-%% Device MUST be 2nd arg because of extraarg ni foreach...
-%%--------------------------------------------------
-print_row(Row, Device, Format) ->
- io:format(Device, Format,
- lists:map(fun(Term) -> term_to_string(Term) end,
- Row)).
diff --git a/lib/sasl/src/misc_supp.erl b/lib/sasl/src/misc_supp.erl
index 093b337a2c..cbbcba0def 100644
--- a/lib/sasl/src/misc_supp.erl
+++ b/lib/sasl/src/misc_supp.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.
@@ -32,7 +32,7 @@
%%%---------------------------------------------------------------------
-export([format_pdict/3, format_tuples/2, assq/2, passq/2, is_string/1,
- multi_map/2]).
+ multi_map/2, modifier/1]).
%%-----------------------------------------------------------------
%% Uses format_tuples to format the data in process dictionary.
@@ -105,3 +105,19 @@ multi_map(Func, ListOfLists) ->
[apply(Func, lists:map(fun(List) -> hd(List) end, ListOfLists)) |
multi_map(Func,
lists:map(fun(List) -> tl(List) end, ListOfLists))].
+
+%%%-----------------------------------------------------------------
+%%% Check encoding of the given device and return "t" if this format
+%%% modifier should be used.
+modifier(Device) ->
+ Encoding =
+ case io:getopts(Device) of
+ List when is_list(List) ->
+ proplists:get_value(encoding,List,latin1);
+ _ ->
+ latin1
+ end,
+ encoding_to_modifier(Encoding).
+
+encoding_to_modifier(latin1) -> "";
+encoding_to_modifier(_) -> "t".
diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl
index 79df150b41..6595d29a9c 100644
--- a/lib/sasl/src/rb.erl
+++ b/lib/sasl/src/rb.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.
@@ -273,7 +273,7 @@ code_change(_OldVsn, State, _Extra) ->
open_log_file(standard_io) -> standard_io;
open_log_file(Fd) when is_atom(Fd),Fd=/=standard_error ->
case whereis(Fd) of
- undefined -> io:format("rb: Registered name not found '~s'.~n",
+ undefined -> io:format("rb: Registered name not found '~ts'.~n",
[Fd]),
io:format("rb: Using standard_io~n"),
open_log_file(standard_io);
@@ -281,10 +281,10 @@ open_log_file(Fd) when is_atom(Fd),Fd=/=standard_error ->
end;
open_log_file(Fd) when is_pid(Fd)-> Fd;
open_log_file(FileName) when is_list(FileName) ->
- case file:open(FileName, [write,append]) of
+ case file:open(FileName, [write,append,{encoding,utf8}]) of
{ok, Fd} -> Fd;
Error ->
- io:format("rb: Cannot open file '~s' (~w).~n",
+ io:format("rb: Cannot open file '~ts' (~w).~n",
[FileName, Error]),
io:format("rb: Using standard_io~n"),
standard_io
@@ -419,8 +419,8 @@ read_reports(No, Fd, Fname, Max, Type) ->
add_report_data(NewRes, No, Fname);
{error, [Problem | Res]} ->
_ = file:close(Fd),
- io:format("Error: ~p~n",[Problem]),
- io:format("Salvaged ~p entries from corrupt report file ~s...~n",
+ io:format("Error: ~tp~n",[Problem]),
+ io:format("Salvaged ~p entries from corrupt report file ~ts...~n",
[length(Res),Fname]),
NewRes =
if
@@ -431,7 +431,7 @@ read_reports(No, Fd, Fname, Max, Type) ->
end,
add_report_data(NewRes, No, Fname);
Else ->
- io:format("err ~p~n", [Else]),
+ io:format("err ~tp~n", [Else]),
[{No, unknown, "Can't read reports from file " ++ Fname,
"???", Fname, 0}]
end.
@@ -530,21 +530,18 @@ get_short_descr({{Date, Time}, {error_report, Pid, {_, crash_report, Rep}}}) ->
{value, {_Key, N}} -> N;
_ -> Pid
end,
- NameStr = lists:flatten(io_lib:format("~w", [Name])),
- {NameStr, date_str(Date, Time)};
+ {Name, date_str(Date, Time)};
get_short_descr({{Date, Time}, {error_report, Pid, {_, supervisor_report,Rep}}}) ->
Name =
case lists:keysearch(supervisor, 1, Rep) of
{value, {_Key, N}} when is_atom(N) -> N;
_ -> Pid
end,
- NameStr = lists:flatten(io_lib:format("~w", [Name])),
- {NameStr, date_str(Date,Time)};
+ {Name, date_str(Date,Time)};
get_short_descr({{Date, Time}, {_Type, Pid, _}}) ->
- NameStr = lists:flatten(io_lib:format("~w", [Pid])),
- {NameStr, date_str(Date,Time)};
+ {Pid, date_str(Date,Time)};
get_short_descr(_) ->
- {"???", "???"}.
+ {'???', "???"}.
date_str({Y,Mo,D}=Date,{H,Mi,S}=Time) ->
case application:get_env(sasl,utc_log) of
@@ -571,53 +568,57 @@ local_time_to_universal_time({Date,Time}) ->
end.
-print_list(Fd, Data, Type) ->
+print_list(Fd, Data0, Type) ->
+ Modifier = misc_supp:modifier(Fd),
Header = {"No", "Type", "Process", "Date Time"},
- Width = find_width([Header | Data], 0)+1,
- DateWidth = find_date_width([Header | Data], 0) +1,
- Format = lists:concat(["~4s~20s ~", Width, "s~20s~n"]),
+ {DescrWidth,DateWidth,Data} = find_widths(Data0, Modifier, 7, 13, []),
+ Format = lists:concat(["~4s~20s ~", DescrWidth, "s~20s~n"]),
io:format(Fd, Format, tuple_to_list(Header)),
io:format(Fd, Format, ["==", "====", "=======", "==== ===="]),
- print_list(Fd, Data, Type, Width, DateWidth).
-print_list(_, [], _, _, _) -> true;
-print_list(Fd, [H|T], Type, Width, DateWidth) ->
- print_one_report(Fd, H, Type, Width, DateWidth),
- print_list(Fd, T, Type, Width, DateWidth).
-
-find_width([], Width) -> Width;
-find_width([H|T], Width) ->
- Try = length(element(3, H)),
- if
- Try > Width -> find_width(T, Try);
- true -> find_width(T, Width)
- end.
-find_date_width([], Width) -> Width;
-find_date_width([H|T], Width) ->
- Try = length(element(4, H)),
- if
- Try > Width -> find_date_width(T, Try);
- true -> find_date_width(T, Width)
- end.
+ print_list(Fd, Data, Type, DescrWidth, DateWidth, Modifier).
+print_list(_, [], _, _, _, _) -> true;
+print_list(Fd, [H|T], Type, Width, DateWidth, Modifier) ->
+ print_one_report(Fd, H, Type, Width, DateWidth, Modifier),
+ print_list(Fd, T, Type, Width, DateWidth, Modifier).
+
+
+find_widths([], _Modifier, DescrWidth, DateWidth, Data) ->
+ {DescrWidth+1, DateWidth+1, lists:reverse(Data)};
+find_widths([H|T], Modifier, DescrWidth, DateWidth, Data) ->
+ DescrTerm = element(3,H),
+ Descr = lists:flatten(io_lib:format("~"++Modifier++"w", [DescrTerm])),
+ DescrTry = length(Descr),
+ NewDescrWidth =
+ if
+ DescrTry > DescrWidth -> DescrTry;
+ true -> DescrWidth
+ end,
+ DateTry = length(element(4, H)),
+ NewDateWitdh =
+ if
+ DateTry > DateWidth -> DateTry;
+ true -> DateWidth
+ end,
+ NewH = setelement(3,H,Descr),
+ find_widths(T, Modifier, NewDescrWidth, NewDateWitdh, [NewH|Data]).
print_one_report(Fd, {No, RealType, ShortDescr, Date, _Fname, _FilePos},
WantedType,
- Width, DateWidth) ->
+ Width, DateWidth, Modifier) ->
if
WantedType == all ->
print_short_descr(Fd, No, RealType, ShortDescr, Date, Width,
- DateWidth);
+ DateWidth, Modifier);
WantedType == RealType ->
print_short_descr(Fd, No, RealType, ShortDescr, Date, Width,
- DateWidth);
+ DateWidth, Modifier);
true -> ok
end.
-print_short_descr(Fd, No, Type, ShortDescr, Date, Width, DateWidth) ->
- Format = lists:concat(["~4w~20w ~", Width, "s~", DateWidth,"s~n"]),
- io:format(Fd, Format, [No,
- Type,
- io_lib:format("~s", [ShortDescr]),
- Date]).
+print_short_descr(Fd, No, Type, ShortDescr, Date, Width, DateWidth, Modifier) ->
+ Format = lists:concat(["~4w~20", Modifier, "w ~", Width, Modifier, "s~",
+ DateWidth, "s~n"]),
+ io:format(Fd, Format, [No, Type, ShortDescr, Date]).
print_report_by_num(Dir, Data, Number, Device, Abort, Log) ->
{_,Device1} = print_report(Dir, Data, Number, Device, Abort, Log),
@@ -658,7 +659,7 @@ print_report(Dir, Data, Number, Device, Abort, Log) ->
{ok, Fd} ->
read_rep(Fd, FilePosition, Device, Abort, Log);
_ ->
- io:format("rb: can't open file ~p~n", [Fname]),
+ io:format("rb: can't open file ~tp~n", [Fname]),
{proceed,Device}
end;
no_report ->
@@ -691,14 +692,14 @@ print_grep_report(Dir, Data, Number, Device, RegExp, Abort, Log) ->
{ok, Fd} when is_pid(Fd) ->
check_rep(Fd, FilePosition, Device, RegExp, Number, Abort, Log);
_ ->
- io:format("rb: can't open file ~p~n", [Fname]),
+ io:format("rb: can't open file ~tp~n", [Fname]),
{proceed,Device}
end.
check_rep(Fd, FilePosition, Device, RegExp, Number, Abort, Log) ->
case read_rep_msg(Fd, FilePosition) of
{Date, Msg} ->
- MsgStr = lists:flatten(io_lib:format("~p",[Msg])),
+ MsgStr = lists:flatten(io_lib:format("~tp",[Msg])),
case run_re(MsgStr, RegExp) of
match ->
io:format("Found match in report number ~w~n", [Number]),
@@ -722,7 +723,7 @@ run_re(Subject, Regexp) ->
run_re(Subject, Regexp, []).
run_re(Subject, Regexp, Options) ->
- case re:run(Subject, Regexp, Options) of
+ case re:run(Subject, Regexp, [unicode|Options--[unicode]]) of
nomatch ->
nomatch;
_ ->
@@ -748,7 +749,7 @@ filter_report(Dir, Data, Filters, Number, Device, Abort, Log) ->
{ok, Fd} ->
filter_rep(Filters, Fd, FilePosition, Device, Abort, Log);
_ ->
- io:format("rb: can't open file ~p~n", [Fname]),
+ io:format("rb: can't open file ~tp~n", [Fname]),
{proceed,Device}
end;
no_report ->
@@ -800,7 +801,7 @@ filter_report([{Key, RegExp, re}|T], Msg) ->
undefined ->
false;
Value ->
- Subject = lists:flatten(io_lib:format("~p",[Value])),
+ Subject = lists:flatten(io_lib:format("~tp",[Value])),
case run_re(Subject, RegExp) of
match ->
filter_report(T, Msg);
@@ -812,7 +813,7 @@ filter_report([{Key, RegExp, re, no}|T], Msg) ->
undefined ->
true;
Value ->
- Subject = lists:flatten(io_lib:format("~p",[Value])),
+ Subject = lists:flatten(io_lib:format("~tp",[Value])),
case run_re(Subject, RegExp) of
match -> false;
_ -> filter_report(T, Msg)
@@ -890,7 +891,7 @@ read_rep(Fd, FilePosition, Device, Abort, Log) ->
handle_bad_form(Date, Msg, Device, Abort, Log) ->
io:format("rb: ERROR! A report on bad form was encountered. " ++
"It can not be printed to the log.~n~n"),
- io:format("Details:~n~p ~p~n~n", [Date,Msg]),
+ io:format("Details:~n~p ~tp~n~n", [Date,Msg]),
case {Abort,Device,open_log_file(Log)} of
{true,standard_io,standard_io} ->
io:format("rb: Logging aborted.~n"),
@@ -899,7 +900,7 @@ handle_bad_form(Date, Msg, Device, Abort, Log) ->
io:format("rb: Logging resumed...~n~n"),
{proceed,Device};
{_,_,standard_io} ->
- io:format("rb: Can not reopen ~p. Logging aborted.~n", [Log]),
+ io:format("rb: Can not reopen ~tp. Logging aborted.~n", [Log]),
{abort,Device};
{true,_,NewDevice} ->
io:format(NewDevice,
diff --git a/lib/sasl/src/rb_format_supp.erl b/lib/sasl/src/rb_format_supp.erl
index 0004d85af4..1eda43dae4 100644
--- a/lib/sasl/src/rb_format_supp.erl
+++ b/lib/sasl/src/rb_format_supp.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.
@@ -94,13 +94,15 @@ print(Date, Report, Device) ->
[{header, Header}]),
io:format(Device, Format, Args);
{Type, _GL, TypeReport} ->
- io:format(Device, "~nInfo type <~w> ~s~n",
+ Modifier = misc_supp:modifier(Device),
+ io:format(Device, "~nInfo type <~"++Modifier++"w> ~s~n",
[Type, Date]),
- io:format(Device, "~p", [TypeReport]);
+ io:format(Device, "~"++Modifier++"p", [TypeReport]);
_ ->
+ Modifier = misc_supp:modifier(Device),
io:format("~nPrinting info of unknown type... ~s~n",
[Date]),
- io:format(Device, "~p", [Report])
+ io:format(Device, "~"++Modifier++"p", [Report])
% end
end.
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl
index 669ffd95c0..1f3c6877d5 100644
--- a/lib/sasl/src/release_handler.erl
+++ b/lib/sasl/src/release_handler.erl
@@ -640,8 +640,8 @@ handle_call({install_release, Vsn, ErrorAction, Opts}, From, S) ->
{noreply, NS};
{'EXIT', Reason} ->
io:format("release_handler:"
- "install_release(Vsn=~p Opts=~p) failed, "
- "Reason=~p~n", [Vsn, Opts, Reason]),
+ "install_release(Vsn=~tp Opts=~tp) failed, "
+ "Reason=~tp~n", [Vsn, Opts, Reason]),
gen_server:reply(From, {error, Reason}),
case ErrorAction of
restart ->
@@ -1124,7 +1124,7 @@ new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,RelDir,Masters) ->
{ok,[FC]} ->
FC;
{error,Error1} ->
- io:format("Warning: ~w can not read ~p: ~p~n",
+ io:format("Warning: ~w can not read ~tp: ~tp~n",
[?MODULE,FromFile,Error1]),
[]
end,
@@ -1134,7 +1134,7 @@ new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,RelDir,Masters) ->
{ok,[ToConfig]} ->
[lists:keyfind(App,1,ToConfig) || App <- [kernel,stdlib,sasl]];
{error,Error2} ->
- io:format("Warning: ~w can not read ~p: ~p~n",
+ io:format("Warning: ~w can not read ~tp: ~tp~n",
[?MODULE,ToFile,Error2]),
[false,false,false]
end,
@@ -1807,7 +1807,7 @@ check_opt_file(FileName, Type, Masters) ->
ok ->
true;
_Error ->
- io:format("Warning: ~p missing (optional)~n", [FileName]),
+ io:format("Warning: ~tp missing (optional)~n", [FileName]),
false
end.
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 654a0acff2..b1523dcbb7 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -2234,9 +2234,9 @@ check_apps(_) ->
format_error(badly_formatted_release) ->
io_lib:format("Syntax error in the release file~n",[]);
format_error({illegal_name, Name}) ->
- io_lib:format("Illegal name (~p) in the release file~n",[Name]);
+ io_lib:format("Illegal name (~tp) in the release file~n",[Name]);
format_error({illegal_form, Form}) ->
- io_lib:format("Illegal tag in the release file: ~p~n",[Form]);
+ io_lib:format("Illegal tag in the release file: ~tp~n",[Form]);
format_error({missing_parameter,Par}) ->
io_lib:format("Missing parameter (~p) in the release file~n",[Par]);
format_error({illegal_applications,Names}) ->
@@ -2251,7 +2251,7 @@ format_error({mandatory_app,Name,Type}) ->
format_error({duplicate_register,Dups}) ->
io_lib:format("Duplicated register names: ~n~ts",
[map(fun({{Reg,App1,_,_},{Reg,App2,_,_}}) ->
- io_lib:format("\t~w registered in ~w and ~w~n",
+ io_lib:format("\t~tw registered in ~w and ~w~n",
[Reg,App1,App2])
end, Dups)]);
format_error({undefined_applications,Apps}) ->
@@ -2275,20 +2275,20 @@ format_error({modules,ModErrs}) ->
format_error({circular_dependencies,Apps}) ->
io_lib:format("Circular dependencies among applications: ~p~n",[Apps]);
format_error({not_found,File}) ->
- io_lib:format("File not found: ~p~n",[File]);
+ io_lib:format("File not found: ~tp~n",[File]);
format_error({parse,File,{Line,Mod,What}}) ->
Str = Mod:format_error(What),
io_lib:format("~ts:~w: ~ts\n",[File, Line, Str]);
format_error({read,File}) ->
- io_lib:format("Cannot read ~p~n",[File]);
+ io_lib:format("Cannot read ~tp~n",[File]);
format_error({open,File,Error}) ->
- io_lib:format("Cannot open ~p - ~ts~n",
+ io_lib:format("Cannot open ~tp - ~ts~n",
[File,file:format_error(Error)]);
format_error({close,File,Error}) ->
- io_lib:format("Cannot close ~p - ~ts~n",
+ io_lib:format("Cannot close ~tp - ~ts~n",
[File,file:format_error(Error)]);
format_error({delete,File,Error}) ->
- io_lib:format("Cannot delete ~p - ~ts~n",
+ io_lib:format("Cannot delete ~tp - ~ts~n",
[File,file:format_error(Error)]);
format_error({tar_error,What}) ->
form_tar_err(What);
@@ -2297,7 +2297,7 @@ format_error({warnings_treated_as_errors,Warnings}) ->
[map(fun(W) -> form_warn("",W) end, Warnings)]);
format_error(ListOfErrors) when is_list(ListOfErrors) ->
format_errors(ListOfErrors);
-format_error(E) -> io_lib:format("~p~n",[E]).
+format_error(E) -> io_lib:format("~tp~n",[E]).
format_errors(ListOfErrors) ->
map(fun({error,E}) -> form_err(E);
@@ -2313,19 +2313,19 @@ form_err({module_not_found,App,Mod}) ->
form_err({error_add_appl, {Name, {tar_error, What}}}) ->
io_lib:format("~p: ~ts~n",[Name,form_tar_err(What)]);
form_err(E) ->
- io_lib:format("~p~n",[E]).
+ io_lib:format("~tp~n",[E]).
form_reading({not_found,File}) ->
- io_lib:format("File not found: ~p~n",[File]);
+ io_lib:format("File not found: ~tp~n",[File]);
form_reading({application_vsn, {Name,Vsn}}) ->
- io_lib:format("Application ~ts with version ~p not found~n",[Name, Vsn]);
+ io_lib:format("Application ~ts with version ~tp not found~n",[Name, Vsn]);
form_reading({parse,File,{Line,Mod,What}}) ->
Str = Mod:format_error(What),
io_lib:format("~ts:~w: ~ts\n",[File, Line, Str]);
form_reading({read,File}) ->
- io_lib:format("Cannot read ~p~n",[File]);
+ io_lib:format("Cannot read ~tp~n",[File]);
form_reading({{bad_param, P},_}) ->
- io_lib:format("Bad parameter in .app file: ~p~n",[P]);
+ io_lib:format("Bad parameter in .app file: ~tp~n",[P]);
form_reading({{missing_param,P},_}) ->
io_lib:format("Missing parameter in .app file: ~p~n",[P]);
form_reading({badly_formatted_application,_}) ->
@@ -2334,12 +2334,12 @@ form_reading({override_include,Apps}) ->
io_lib:format("Tried to include not (in .app file) specified applications: ~p~n",
[Apps]);
form_reading({no_valid_version, {{_, SVsn}, {_, File, FVsn}}}) ->
- io_lib:format("No valid version (~p) of .app file found. Found file ~p with version ~p~n",
+ io_lib:format("No valid version (~tp) of .app file found. Found file ~tp with version ~tp~n",
[SVsn, File, FVsn]);
form_reading({parse_error, {File, Line, Error}}) ->
- io_lib:format("Parse error in file: ~p. Line: ~w Error: ~p; ~n", [File, Line, Error]);
+ io_lib:format("Parse error in file: ~tp. Line: ~w Error: ~tp; ~n", [File, Line, Error]);
form_reading(W) ->
- io_lib:format("~p~n",[W]).
+ io_lib:format("~tp~n",[W]).
form_tar_err({open, File, Error}) ->
io_lib:format("Cannot open tar file ~ts - ~ts~n",
@@ -2357,14 +2357,14 @@ form_warn(Prefix, {source_not_found,{Mod,App,_}}) ->
io_lib:format("~ts~w: Source code not found: ~w.erl~n",
[Prefix,App,Mod]);
form_warn(Prefix, {{parse_error, File},{_,_,App,_,_}}) ->
- io_lib:format("~ts~w: Parse error: ~p~n",
+ io_lib:format("~ts~w: Parse error: ~tp~n",
[Prefix,App,File]);
form_warn(Prefix, {obj_out_of_date,{Mod,App,_}}) ->
io_lib:format("~ts~w: Object code (~w) out of date~n",
[Prefix,App,Mod]);
form_warn(Prefix, {exref_undef, Undef}) ->
F = fun({M,F,A}) ->
- io_lib:format("~tsUndefined function ~w:~w/~w~n",
+ io_lib:format("~tsUndefined function ~w:~tw/~w~n",
[Prefix,M,F,A])
end,
map(F, Undef);
@@ -2373,4 +2373,4 @@ form_warn(Prefix, missing_sasl) ->
"Can not upgrade with this release~n",
[Prefix]);
form_warn(Prefix, What) ->
- io_lib:format("~ts~p~n", [Prefix,What]).
+ io_lib:format("~ts~tp~n", [Prefix,What]).
diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl
index 7722cef57b..c570ed00ab 100644
--- a/lib/sasl/src/systools_rc.erl
+++ b/lib/sasl/src/systools_rc.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.
@@ -942,41 +942,41 @@ format_error(too_many_point_of_no_return) ->
io_lib:format("Too many point_of_no_return~n", []);
format_error({bad_instruction, X}) ->
- io_lib:format("Bad instruction: ~p~n", [X]);
+ io_lib:format("Bad instruction: ~tp~n", [X]);
format_error({bad_module, X}) ->
- io_lib:format("Bad module: ~p(should be atom())~n", [X]);
+ io_lib:format("Bad module: ~tp(should be atom())~n", [X]);
format_error({bad_code_change, X}) ->
- io_lib:format("Bad code_change: ~p(should be {Mod, Extra})~n", [X]);
+ io_lib:format("Bad code_change: ~tp(should be {Mod, Extra})~n", [X]);
format_error({bad_change, X}) ->
- io_lib:format("Bad change spec: ~p(should be soft | {advanced, E})~n", [X]);
+ io_lib:format("Bad change spec: ~tp(should be soft | {advanced, E})~n", [X]);
format_error({bad_mod_type, X}) ->
- io_lib:format("Bad module type: ~p(should be static | dynamic)~n", [X]);
+ io_lib:format("Bad module type: ~tp(should be static | dynamic)~n", [X]);
format_error({bad_purge_method, X}) ->
- io_lib:format("Bad purge method: ~p(should be soft_purge | brutal_purge)~n",
+ io_lib:format("Bad purge method: ~tp(should be soft_purge | brutal_purge)~n",
[X]);
format_error({bad_list, X}) ->
- io_lib:format("Bad list: ~p~n", [X]);
+ io_lib:format("Bad list: ~tp~n", [X]);
format_error({bad_args_list, X}) ->
- io_lib:format("Bad argument list: ~p~n", [X]);
+ io_lib:format("Bad argument list: ~tp~n", [X]);
format_error({bad_node, X}) ->
- io_lib:format("Bad node: ~p(should be atom())~n", [X]);
+ io_lib:format("Bad node: ~tp(should be atom())~n", [X]);
format_error({bad_application, X}) ->
- io_lib:format("Bad application: ~p(should be atom())~n", [X]);
+ io_lib:format("Bad application: ~tp(should be atom())~n", [X]);
format_error({bad_func, X}) ->
- io_lib:format("Bad function: ~p(should be atom())~n", [X]);
+ io_lib:format("Bad function: ~tp(should be atom())~n", [X]);
format_error({bad_lib, X}) ->
- io_lib:format("Bad library: ~p(should be atom())~n", [X]);
+ io_lib:format("Bad library: ~tp(should be atom())~n", [X]);
format_error({bad_lib_vsn, X}) ->
- io_lib:format("Bad library version: ~p(should be string())~n", [X]);
+ io_lib:format("Bad library version: ~tp(should be string())~n", [X]);
format_error({bad_timeout, X}) ->
- io_lib:format("Bad timeout: ~p(should be infinity | int() > 0)~n", [X]);
+ io_lib:format("Bad timeout: ~tp(should be infinity | int() > 0)~n", [X]);
format_error({undef_module, Mod}) ->
io_lib:format("Undefined module: ~p~n", [Mod]);
format_error({muldef_module, Mod}) ->
io_lib:format("Multiply defined module: ~p~n", [Mod]);
format_error(E) ->
- io_lib:format("~p~n",[E]).
+ io_lib:format("~tp~n",[E]).
%%-----------------------------------------------------------------
diff --git a/lib/sasl/src/systools_relup.erl b/lib/sasl/src/systools_relup.erl
index 6bc26c8cc9..706ae7d631 100644
--- a/lib/sasl/src/systools_relup.erl
+++ b/lib/sasl/src/systools_relup.erl
@@ -589,7 +589,7 @@ print_error({error, Mod, Error}) ->
S = apply(Mod, format_error, [Error]),
io:format(S, []);
print_error(Other) ->
- io:format("Error: ~p~n", [Other]).
+ io:format("Error: ~tp~n", [Other]).
format_error({file_problem, {File, What}}) ->
io_lib:format("Could not ~w file ~ts~n", [get_reason(What), File]);
@@ -606,7 +606,7 @@ format_error({warnings_treated_as_errors, Warnings}) ->
io_lib:format("Warnings being treated as errors:~n~ts",
[[format_warning("",W) || W <- Warnings]]);
format_error(Error) ->
- io_lib:format("~p~n", [Error]).
+ io_lib:format("~tp~n", [Error]).
print_warnings(Ws) when is_list(Ws) ->
@@ -621,12 +621,12 @@ format_warning(W) ->
format_warning("*WARNING* ", W).
format_warning(Prefix, {erts_vsn_changed, {Rel1, Rel2}}) ->
- io_lib:format("~tsThe ERTS version changed between ~p and ~p~n",
+ io_lib:format("~tsThe ERTS version changed between ~tp and ~tp~n",
[Prefix, Rel1, Rel2]);
format_warning(Prefix, pre_R15_emulator_upgrade) ->
io_lib:format("~tsUpgrade from an OTP version earlier than R15. New code should be compiled with the old emulator.~n",[Prefix]);
format_warning(Prefix, What) ->
- io_lib:format("~ts~p~n",[Prefix, What]).
+ io_lib:format("~ts~tp~n",[Prefix, What]).
get_reason({error, {open, _, _}}) -> open;
diff --git a/lib/sasl/test/sasl_report_SUITE.erl b/lib/sasl/test/sasl_report_SUITE.erl
index eb0a877c45..53fb614921 100644
--- a/lib/sasl/test/sasl_report_SUITE.erl
+++ b/lib/sasl/test/sasl_report_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2015-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/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
index 6aa662a743..2608ca5e00 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 3.0.3
+SASL_VSN = 3.0.4
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index f1919a6bb1..b902e421ca 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1996</year><year>2016</year>
+ <year>1996</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -34,7 +34,24 @@
</header>
- <section><title>SNMP 5.2.5</title>
+ <section><title>SNMP 5.2.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Internal code change: Calls to <c>catch</c> followed
+ by a call to <c>erlang:get_stacktrace/0</c> has been
+ rewritten to use <c>try</c> instead of <c>catch</c> to
+ make the code future-proof.</p>
+ <p>
+ Own Id: OTP-14400</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SNMP 5.2.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/snmp/src/agent/snmp_generic.erl b/lib/snmp/src/agent/snmp_generic.erl
index 4738525341..e67a1b3c80 100644
--- a/lib/snmp/src/agent/snmp_generic.erl
+++ b/lib/snmp/src/agent/snmp_generic.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.
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index 054e998af4..4bfeb0f8d1 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index 30b8ee1124..d41b1999cc 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 5.2.5
+SNMP_VSN = 5.2.6
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index bddae00dd2..f93753f1d2 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,176 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.5</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The internal handling of SSH options is re-written.</p>
+ <p>
+ Previously there were no checks if a client option was
+ given to a daemon or vice versa. This is corrected now.
+ If your code has e.g. a client-only option in a call to
+ start a daemon, the call will fail.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-12872</p>
+ </item>
+ <item>
+ <p>
+ Modernization of key exchange algorithms. See
+ draft-ietf-curdle-ssh-kex-sha2 for a discussion.</p>
+ <p>
+ Removed an outdated weak algorithm and added stronger
+ replacements to keep interoperability with other modern
+ ssh clients and servers. The default ordering of the
+ algorithms is also adjusted.</p>
+ <p>
+ Retired: The nowadays unsecure key-exchange
+ <c>diffie-hellman-group1-sha1</c> is not enabled by
+ default, but can be enabled with the option
+ <c>preferred-algorithms</c>.</p>
+ <p>
+ Added: The new stronger key-exchange
+ <c>diffie-hellman-group16-sha512</c>,
+ <c>diffie-hellman-group18-sha512</c> and
+ <c>diffie-hellman-group14-sha256</c> are added and
+ enabled by default.</p>
+ <p>
+ The questionable [RFC 6194] sha1-based algorithms
+ <c>diffie-hellman-group-exchange-sha1</c> and
+ <c>diffie-hellman-group14-sha1</c> are however still kept
+ enabled by default for compatibility with ancient clients
+ and servers that lack modern key-exchange alternatives.
+ When the draft-ietf-curdle-ssh-kex-sha2 becomes an rfc,
+ those sha1-based algorithms and
+ <c>diffie-hellman-group1-sha1</c> will be deprecated by
+ IETF. They might then be removed from the default list in
+ Erlang/OTP.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14110</p>
+ </item>
+ <item>
+ <p>
+ Modernized internal representation of sftp by use of
+ maps.</p>
+ <p>
+ Own Id: OTP-14117</p>
+ </item>
+ <item>
+ <p>
+ The Extension Negotiation Mechanism and the extension
+ <c>server-sig-algs</c> in
+ draft-ietf-curdle-ssh-ext-info-05 are implemented.</p>
+ <p>
+ The related draft-ietf-curdle-rsa-sha2-05 is implemented
+ and introduces the signature algorithms
+ <c>rsa-sha2-256</c> and <c>rsa-sha2-512</c>.</p>
+ <p>
+ Own Id: OTP-14193</p>
+ </item>
+ <item>
+ <p>
+ The 'timeout' and 'connect_timeout' handling in
+ ssh_sftp:start_channel documentation is clarified.</p>
+ <p>
+ Own Id: OTP-14216</p>
+ </item>
+ <item>
+ <p>
+ The functions <c>ssh:connect</c>, <c>ssh:shell</c> and
+ <c>ssh:start_channel</c> now accept an IP-tuple as Host
+ destination argument.</p>
+ <p>
+ Own Id: OTP-14243</p>
+ </item>
+ <item>
+ <p>
+ The function <c>ssh:daemon_info/1</c> now returns Host
+ and Profile as well as the Port info in the property
+ list.</p>
+ <p>
+ Own Id: OTP-14259</p>
+ </item>
+ <item>
+ <p>
+ Removed the option <c>public_key_alg</c> which was
+ deprecated in 18.2. Use <c>pref_public_key_algs</c>
+ instead.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14263</p>
+ </item>
+ <item>
+ <p>
+ The SSH application is refactored regarding daemon
+ starting. The resolution of contradicting <c>Host</c>
+ argument and <c>ip</c> option were not described. There
+ were also strange corner cases when the <c>'any'</c>
+ value was used in <c>Host</c> argument or <c>ip</c>
+ option. This is (hopefully) resolved now, but it may
+ cause incompatibilities for code using both <c>Host</c>
+ and the <c>ip</c> option. The value 'loopback' has been
+ added for a correct way of naming those addresses.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14264</p>
+ </item>
+ <item>
+ <p>
+ The supervisor code is refactored. The naming of
+ listening IP-Port-Profile triples are slightly changed to
+ improve consistency in strange corner cases as resolved
+ by OTP-14264</p>
+ <p>
+ Own Id: OTP-14267 Aux Id: OTP-14266 </p>
+ </item>
+ <item>
+ <p>
+ The <c>idle_time</c> option can now be used in daemons.</p>
+ <p>
+ Own Id: OTP-14312</p>
+ </item>
+ <item>
+ <p>
+ Added test cases for IETF-CURDLE Extension Negotiation
+ (ext-info)</p>
+ <p>
+ Own Id: OTP-14361</p>
+ </item>
+ <item>
+ <p>
+ Testcases for IETF-CURDLE extension
+ <c>server-sig-algs</c> including <c>rsa-sha2-*</c></p>
+ <p>
+ Own Id: OTP-14362 Aux Id: OTP-14361 </p>
+ </item>
+ <item>
+ <p>
+ The option <c>auth_methods</c> can now also be used in
+ clients to select which authentication options that are
+ used and in which order.</p>
+ <p>
+ Own Id: OTP-14399</p>
+ </item>
+ <item>
+ <p>
+ Checks that a ECDSA public key (<c>ecdsa-sha2-nistp*</c>)
+ stored in a file has the correct size.</p>
+ <p>
+ Own Id: OTP-14410</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index 2822bf808f..ed7fbf9cf3 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2005</year><year>2016</year>
+ <year>2005</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/ssh/test/ssh_property_test_SUITE.erl b/lib/ssh/test/ssh_property_test_SUITE.erl
index 5ea60d8a8f..3318b86d39 100644
--- a/lib/ssh/test/ssh_property_test_SUITE.erl
+++ b/lib/ssh/test/ssh_property_test_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 48332d2e5a..7208baca6e 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.4.2
+SSH_VSN = 4.5
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 29ec3f9d57..5a39cac9bc 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -28,6 +28,122 @@
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 8.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ ECDH-ECDSA key exchange supported, was accidently
+ dismissed in earlier versions.</p>
+ <p>
+ Own Id: OTP-14421</p>
+ </item>
+ <item>
+ <p>
+ Correct close semantics for active once connections. This
+ was a timing dependent bug the resulted in the close
+ message not always reaching the ssl user process.</p>
+ <p>
+ Own Id: OTP-14443</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ TLS-1.2 clients will now always send hello messages on
+ its own format, as opposed to earlier versions that will
+ send the hello on the lowest supported version, this is a
+ change supported by the latest RFC.</p>
+ <p>
+ This will make interoperability with some newer servers
+ smoother. Potentially, but unlikely, this could cause a
+ problem with older servers if they do not adhere to the
+ RFC and ignore unknown extensions.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13820</p>
+ </item>
+ <item>
+ <p>
+ Allow Erlang/OTP to use OpenSSL in FIPS-140 mode, in
+ order to satisfy specific security requirements (mostly
+ by different parts of the US federal government). </p>
+ <p>
+ See the new crypto users guide "FIPS mode" chapter about
+ building and using the FIPS support which is disabled by
+ default.</p>
+ <p>
+ (Thanks to dszoboszlay and legoscia)</p>
+ <p>
+ Own Id: OTP-13921 Aux Id: PR-1180 </p>
+ </item>
+ <item>
+ <p>
+ Implemented DTLS cookie generation, required by spec,
+ instead of using a hardcoded value.</p>
+ <p>
+ Own Id: OTP-14076</p>
+ </item>
+ <item>
+ <p>
+ Implement sliding window replay protection of DTLS
+ records.</p>
+ <p>
+ Own Id: OTP-14077</p>
+ </item>
+ <item>
+ <p>
+ TLS client processes will by default call
+ public_key:pkix_verify_hostname/2 to verify the hostname
+ of the connection with the server certificates specified
+ hostname during certificate path validation. The user may
+ explicitly disables it. Also if the hostname can not be
+ derived from the first argument to connect or is not
+ supplied by the server name indication option, the check
+ will not be performed.</p>
+ <p>
+ Own Id: OTP-14197</p>
+ </item>
+ <item>
+ <p>
+ Extend connection_information/[1,2] . The values
+ session_id, master_secret, client_random and
+ server_random can no be accessed by
+ connection_information/2. Note only session_id will be
+ added to connection_information/1. The rational is that
+ values concerning the connection security should have to
+ be explicitly requested.</p>
+ <p>
+ Own Id: OTP-14291</p>
+ </item>
+ <item>
+ <p>
+ Chacha cipher suites are currently not tested enough to
+ be most preferred ones</p>
+ <p>
+ Own Id: OTP-14382</p>
+ </item>
+ <item>
+ <p>
+ Basic support for DTLS that been tested together with
+ OpenSSL.</p>
+ <p>
+ Test by providing the option {protocol, dtls} to the ssl
+ API functions connect and listen.</p>
+ <p>
+ Own Id: OTP-14388</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 8.1.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/src/dtls_handshake.hrl b/lib/ssl/src/dtls_handshake.hrl
index 24678cba0e..50e92027d2 100644
--- a/lib/ssl/src/dtls_handshake.hrl
+++ b/lib/ssl/src/dtls_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-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/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 74505169a0..ecbacc1590 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 407152aa75..8a4d827456 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -360,6 +360,8 @@ init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
TestCase == psk_with_hint_cipher_suites;
TestCase == ciphers_rsa_signed_certs;
TestCase == ciphers_rsa_signed_certs_openssl_names;
+ TestCase == ciphers_ecdh_rsa_signed_certs_openssl_names;
+ TestCase == ciphers_ecdh_rsa_signed_certs;
TestCase == ciphers_dsa_signed_certs;
TestCase == ciphers_dsa_signed_certs_openssl_names;
TestCase == anonymous_cipher_suites;
@@ -386,22 +388,27 @@ init_per_testcase(reuse_session, Config) ->
init_per_testcase(rizzo, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, 40}),
+ ct:timetrap({seconds, 60}),
+ Config;
+
+init_per_testcase(no_rizzo_rc4, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 60}),
Config;
init_per_testcase(rizzo_one_n_minus_one, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
- ct:timetrap({seconds, 40}),
+ ct:timetrap({seconds, 60}),
rizzo_add_mitigation_option(one_n_minus_one, Config);
init_per_testcase(rizzo_zero_n, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
- ct:timetrap({seconds, 40}),
+ ct:timetrap({seconds, 60}),
rizzo_add_mitigation_option(zero_n, Config);
init_per_testcase(rizzo_disabled, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
- ct:timetrap({seconds, 40}),
+ ct:timetrap({seconds, 60}),
rizzo_add_mitigation_option(disabled, Config);
init_per_testcase(prf, Config) ->
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index e293d183f7..668c76e38d 100644
--- a/lib/ssl/test/ssl_crl_SUITE.erl
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -155,9 +155,15 @@ init_per_testcase(Case, Config0) ->
DataDir = proplists:get_value(data_dir, Config),
CertDir = filename:join(proplists:get_value(priv_dir, Config0), idp_crl),
{CertOpts, Config} = init_certs(CertDir, idp_crl, Config),
- {ok, _} = make_certs:all(DataDir, CertDir, CertOpts),
- ct:timetrap({seconds, 6}),
- [{cert_dir, CertDir} | Config];
+ case make_certs:all(DataDir, CertDir, CertOpts) of
+ {ok, _} ->
+ ct:timetrap({seconds, 6}),
+ [{cert_dir, CertDir} | Config];
+ _ ->
+ end_per_testcase(Case, Config0),
+ ssl_test_lib:clean_start(),
+ {skip, "Unable to create IDP crls"}
+ end;
false ->
end_per_testcase(Case, Config0),
ssl_test_lib:clean_start(),
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 77c21d9b57..3b9073ac0b 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1294,6 +1294,8 @@ check_sane_openssl_version(Version) ->
case supports_ssl_tls_version(Version) of
true ->
case {Version, os:cmd("openssl version")} of
+ {'sslv3', "OpenSSL 1.0.2" ++ _} ->
+ false;
{_, "OpenSSL 1.0.2" ++ _} ->
true;
{_, "OpenSSL 1.0.1" ++ _} ->
@@ -1419,6 +1421,9 @@ supports_ssl_tls_version(sslv2 = Version) ->
case os:cmd("openssl version") of
"OpenSSL 1" ++ _ ->
false;
+ %% Appears to be broken
+ "OpenSSL 0.9.8.o" ++ _ ->
+ false;
_ ->
VersionFlag = version_flag(Version),
Exe = "openssl",
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 5093ef3728..04c86ccbb6 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -338,7 +338,7 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) ->
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
V2Compat = proplists:get_value(v2_hello_compatible, Config),
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
ct:pal("v2_hello_compatible: ~p", [V2Compat]),
@@ -351,7 +351,8 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
Exe = "openssl",
- Args = ["s_client", "-connect", "localhost:" ++ integer_to_list(Port) | workaround_openssl_s_clinent()],
+ Args = ["s_client", "-connect", hostname_format(Hostname) ++
+ ":" ++ integer_to_list(Port) | workaround_openssl_s_clinent()],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, Data),
@@ -410,7 +411,7 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
@@ -422,7 +423,7 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
- Args = ["s_client", "-connect", "localhost: " ++ integer_to_list(Port),
+ Args = ["s_client", "-connect", hostname_format(Hostname) ++":" ++ integer_to_list(Port),
ssl_test_lib:version_flag(Version)],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
@@ -486,7 +487,7 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_dsa_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config),
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
CaCertFile = proplists:get_value(cacertfile, ClientOpts),
@@ -500,7 +501,7 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
- Args = ["s_client", "-connect", "localhost: " ++ integer_to_list(Port),
+ Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
ssl_test_lib:version_flag(Version),
"-cert", CertFile,
"-CAfile", CaCertFile,
@@ -525,7 +526,7 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
@@ -538,7 +539,8 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
- Args = ["s_client", "-connect", "localhost:" ++ integer_to_list(Port),
+ Args = ["s_client", "-connect", hostname_format(Hostname)
+ ++ ":" ++ integer_to_list(Port),
ssl_test_lib:version_flag(Version),
"-reconnect"],
@@ -655,7 +657,7 @@ erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
@@ -669,7 +671,7 @@ erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
- Args = ["s_client","-connect", "localhost: " ++ integer_to_list(Port),
+ Args = ["s_client","-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
ssl_test_lib:version_flag(Version),
"-msg"],
@@ -779,7 +781,7 @@ erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
@@ -799,7 +801,7 @@ erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
Exe = "openssl",
Args = ["s_client", "-cert", CertFile,
"-CAfile", CaCertFile,
- "-key", KeyFile,"-connect", "localhost:" ++ integer_to_list(Port),
+ "-key", KeyFile,"-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
ssl_test_lib:version_flag(Version)],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
@@ -982,7 +984,7 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
@@ -990,7 +992,7 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
Exe = "openssl",
- Args = ["s_client", "-connect", "localhost:" ++ integer_to_list(Port),
+ Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
"-ssl2", "-msg"],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
@@ -1010,7 +1012,7 @@ ssl2_erlang_server_openssl_client_comp(Config) when is_list(Config) ->
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
@@ -1020,7 +1022,7 @@ ssl2_erlang_server_openssl_client_comp(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
Exe = "openssl",
- Args = ["s_client", "-connect", "localhost:" ++ integer_to_list(Port),
+ Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
"-ssl2", "-msg"],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
@@ -1661,7 +1663,7 @@ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callbac
ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config),
ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>]}, ServerOpts0],
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -1672,7 +1674,8 @@ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callbac
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
- Args = ["s_client", "-nextprotoneg", "http/1.0,spdy/2", "-msg", "-connect", "localhost:"
+ Args = ["s_client", "-nextprotoneg", "http/1.0,spdy/2", "-msg", "-connect",
+ hostname_format(Hostname) ++ ":"
++ integer_to_list(Port), ssl_test_lib:version_flag(Version)],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
@@ -1690,7 +1693,7 @@ start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenS
ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config),
ServerOpts = ErlangServerOpts ++ ServerOpts0,
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -1701,8 +1704,9 @@ start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenS
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
- Args = ["s_client"] ++ OpenSSLClientOpts ++ ["-msg", "-connect", "localhost:" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version)],
+ Args = ["s_client"] ++ OpenSSLClientOpts ++ ["-msg", "-connect",
+ hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version)],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
@@ -1854,3 +1858,11 @@ consume_port_exit(OpenSSLPort) ->
{'EXIT', OpenSSLPort, _} ->
ok
end.
+
+hostname_format(Hostname) ->
+ case lists:member($., Hostname) of
+ true ->
+ Hostname;
+ false ->
+ "localhost"
+ end.
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index 60dbae70c2..7efafedc82 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.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>
@@ -187,7 +187,7 @@
<desc>
<p>Calls <c><anno>Fun</anno>(<anno>Elem</anno>)</c> on successive
elements <c>Elem</c> of <c><anno>List1</anno></c>.
- <c><anno>Fun</anno>/2</c> must return either a Boolean or a tuple
+ <c><anno>Fun</anno>/1</c> must return either a Boolean or a tuple
<c>{true, <anno>Value</anno>}</c>. The function returns the list of
elements for which <c><anno>Fun</anno></c> returns a new value, where
a value of <c>true</c> is synonymous with
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index a8a252cb35..bdd5b39cd3 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,6 +31,476 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> A bug in <c>proc_lib:format()</c> introduced in
+ Erlang/OTP 20.0 is corrected. </p>
+ <p>
+ Own Id: OTP-14482 Aux Id: PR-1488 </p>
+ </item>
+ <item>
+ <p>
+ Fix string:len/1 to be compatible with previous versions.</p>
+ <p>
+ Own Id: OTP-14487 Aux Id: ERIERL-40 </p>
+ </item>
+ <item>
+ <p>
+ In OTP-20.0, the behavior of c, make, and ct_make was
+ changed so that in some cases the beam files by default
+ would be written to the directory where the source files
+ were found. This is now changed back to the old behavior
+ so beam files are by default written to current
+ directory.</p>
+ <p>
+ Own Id: OTP-14489 Aux Id: ERL-438 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>For many releases, it has been legal to override a BIF
+ with a local function having the same name. However,
+ calling a local function with the same name as guard BIF
+ as filter in a list comprehension was not allowed.</p>
+ <p>
+ Own Id: OTP-13690</p>
+ </item>
+ <item>
+ <p> A new (default) pseudo-random number generator
+ algorithm Xoroshiro116+ has been implemented in the
+ <c>rand</c> module. </p><p> The old algorithm
+ implementations had a number of flaws so they are all
+ deprecated, but corrected versions of two of them have
+ been added. See the documentation. </p>
+ <p>
+ Own Id: OTP-14295 Aux Id: PR-1372 </p>
+ </item>
+ <item>
+ <p> The Erlang shell, <c>qlc:string_to_handle()</c>, and
+ the Debugger (the Evaluator area and Edit variable window
+ of the Bindings area) can parse pids, ports, references,
+ and external funs, as long as they can be created in the
+ running system. </p>
+ <p>
+ Own Id: OTP-14296</p>
+ </item>
+ <item>
+ <p>Internal code change: Calls to <c>catch</c> followed
+ by a call to <c>erlang:get_stacktrace/0</c> has been
+ rewritten to use <c>try</c> instead of <c>catch</c> to
+ make the code future-proof.</p>
+ <p>
+ Own Id: OTP-14400</p>
+ </item>
+ <item>
+ <p> The <c>ms_transform</c> module, used by
+ <c>ets:fun2ms/1</c> and <c>dbg:fun2ms/1</c>, evaluates
+ constant arithmetic expressions. This is necessary since
+ the Erlang compiler, which normally evaluates constant
+ expressions, does not recognize the format generated by
+ <c>ms_transform</c>. </p>
+ <p>
+ Own Id: OTP-14454 Aux Id: ERIERL-29 </p>
+ </item>
+ <item>
+ <p> The state machine engine <c>gen_statem</c> can now
+ handle generic time-outs (multiple named) as well as
+ absolute time-out time. See the documentation. </p><p>
+ The <c>gen_statem</c> callback <c>Module:init/1</c> has
+ become mandatory to harmonize with other <c>gen_*</c>
+ modules. This may be an incompatibility for
+ <c>gen_statem</c> callback modules that use
+ <c>gen_statem:enter_loop/4-6</c>. </p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14531</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved unicode support for strings. Added normalization
+ functions in the <c>unicode</c> module. Extended the
+ <c>string</c> module API with new functions with improved
+ unicode handling and that works on grapheme clusters. The
+ new functions operates on the <c>unicode:chardata()</c>
+ type, thus they also accept <c>UTF-8 binaries</c> as
+ input. </p>
+ <p>
+ The old string API have been marked as obsolete. The
+ return values have been changed for some error cases.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10289 Aux Id: OTP-10309 </p>
+ </item>
+ <item>
+ <p>There are two new guard BIFs '<c>floor/1</c>' and
+ '<c>ceil/1</c>'. They both return integers. In the
+ '<c>math</c>' module, there are two new BIFs with the
+ same names that return floating point values.</p>
+ <p>
+ Own Id: OTP-13692</p>
+ </item>
+ <item>
+ <p>
+ Making code_change, terminate and handle_info callbacks
+ optional in the OTP behaviours.</p>
+ <p>
+ Own Id: OTP-13801</p>
+ </item>
+ <item>
+ <p> The support for Dets files created with Erlang/OTP R7
+ and earlier is removed. </p>
+ <p>
+ Own Id: OTP-13830</p>
+ </item>
+ <item>
+ <p>Replaced usage of deprecated symbolic <seealso
+ marker="erts:erlang#type-time_unit"><c>time
+ unit</c></seealso> representations.</p>
+ <p>
+ Own Id: OTP-13831 Aux Id: OTP-13735 </p>
+ </item>
+ <item>
+ <p>The function <c>fmod/2</c> has been added to the
+ <c>math</c> module.</p>
+ <p>
+ Own Id: OTP-14000</p>
+ </item>
+ <item>
+ <p>The EXIT signals received from processes using
+ <c>proc_lib</c> now looks like EXIT signals from
+ processes that were spawned using <c>spawn_link</c>. In
+ particular, that means that the stack trace is now
+ included in the EXIT signal so that it can see where the
+ process crashed.</p>
+ <p>
+ Own Id: OTP-14001</p>
+ </item>
+ <item>
+ <p><c>sets:add_element/2</c> is faster when adding an
+ element that is already present, and
+ <c>sets:del_element/2</c> is faster when the element to
+ be deleted is not present. This optimization can make
+ certain operations, such as sets:union/2 with many
+ overlapping elements, up to two orders of magnitude
+ faster.</p>
+ <p>
+ Own Id: OTP-14035</p>
+ </item>
+ <item>
+ <p>
+ Add information in doc about supervisor shutdown reason
+ when maximum restart frequency is reached.</p>
+ <p>
+ Own Id: OTP-14037 Aux Id: PR-1233 </p>
+ </item>
+ <item>
+ <p>
+ Added <c>rand:jump/[0|1]</c> functions.</p>
+ <p>
+ Own Id: OTP-14038 Aux Id: PR-1235 </p>
+ </item>
+ <item>
+ <p>Functions for detecting changed code has been added.
+ <c>code:modified_modules/0</c> returns all currently
+ loaded modules that have changed on disk.
+ <c>code:module_status/1</c> returns the status for a
+ module. In the shell and in <c>c</c> module, <c>mm/0</c>
+ is short for <c>code:modified_modules/0</c>, and
+ <c>lm/0</c> reloads all currently loaded modules that
+ have changed on disk.</p>
+ <p>
+ Own Id: OTP-14059</p>
+ </item>
+ <item>
+ <p>Each assert macro in <c>assert.hrl</c> now has a
+ corresponding version with an extra argument, for adding
+ comments to assertions. These can for example be printed
+ as part of error reports, to clarify the meaning of the
+ check that failed.</p>
+ <p>
+ Own Id: OTP-14066</p>
+ </item>
+ <item>
+ <p><c>error_logger_tty_h</c> and
+ <c>error_logger_file_h</c> now inserts the node
+ information for nonlocal messages before the message
+ itself instead of after, both for readability and so as
+ not to change the line termination property at the end of
+ the message.</p>
+ <p>
+ Own Id: OTP-14068</p>
+ </item>
+ <item>
+ <p>The Erlang code linter checks for badly formed type
+ constraints. </p>
+ <p>
+ Own Id: OTP-14070 Aux Id: PR-1214 </p>
+ </item>
+ <item>
+ <p>By default, there will now be a warning when
+ <c>export_all</c> is used. The warning can be disabled
+ using <c>nowarn_export_all</c>.</p>
+ <p>
+ Own Id: OTP-14071</p>
+ </item>
+ <item>
+ <p>When a <c>gen_server</c> process crashes, the
+ stacktrace for the client will be printed to facilitate
+ debugging.</p>
+ <p>
+ Own Id: OTP-14089</p>
+ </item>
+ <item>
+ <p>Optimized ETS operations by changing table identifier
+ type from integer to reference. The reference enables a
+ more direct mapping to the table with less potential lock
+ contention and makes especially creation and deletion of
+ tables scale much better.</p> <p>The change of the opaque
+ type for the ETS table identifiers may cause failure in
+ code that make faulty assumptions about this opaque
+ type.</p> <note> <p> The number of tables stored at one
+ Erlang node <em>used</em> to be limited. This is no
+ longer the case (except by memory usage). The previous
+ default limit was about 1400 tables and could be
+ increased by setting the environment variable
+ <c>ERL_MAX_ETS_TABLES</c> before starting the Erlang
+ runtime system. This hard limit has been removed, but it
+ is currently useful to set the <c>ERL_MAX_ETS_TABLES</c>
+ anyway. It should be set to an approximate of the maximum
+ amount of tables used. This since an internal table for
+ named tables is sized using this value. If large amounts
+ of named tables are used and <c>ERL_MAX_ETS_TABLES</c>
+ hasn't been increased, the performance of named table
+ lookup will degrade. </p> </note>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14094</p>
+ </item>
+ <item>
+ <p><c>take/2</c> has been added to <c>dict</c>,
+ <c>orddict</c>, and <c>gb_trees</c>. <c>take_any/2</c>
+ has been added to <c>gb_trees</c>.</p>
+ <p>
+ Own Id: OTP-14102</p>
+ </item>
+ <item>
+ <p>
+ Extend gen_event API to handle options as well.</p>
+ <p>
+ Own Id: OTP-14123</p>
+ </item>
+ <item>
+ <p>
+ Advice on how to tune the supervisor restart frequency
+ (intensity and period) is added to System Documentation -
+ Design Principles - Supervisor Behaviour.</p>
+ <p>
+ Own Id: OTP-14168 Aux Id: PR-1289 </p>
+ </item>
+ <item>
+ <p>
+ gen_fsm is deprecated and is replaced by gen_statem,
+ however for backwards compatibility reasons gen_fsm may
+ continue to exist as an undocumented feature for quite
+ some time.</p>
+ <p>
+ Own Id: OTP-14183</p>
+ </item>
+ <item>
+ <p>The shell functions <c>c/1</c> and <c>c/2</c> have
+ been extended so that if the argument is a module name
+ instead of a file name, it automatically locates the
+ .beam file and the corresponding source file, and then
+ recompiles the module using the same compiler options
+ (plus any options passed to c/2). If compilation fails,
+ the old beam file is preserved. Also adds <c>c(Mod, Opts,
+ Filter)</c>, where the Filter argument allows you to
+ remove old compiler options before the new options are
+ added.</p> <p>New utility functions <c>file_find/2/3</c>
+ and <c>find_source/1/2/3</c> have been added to
+ <c>filelib</c>.</p>
+ <p>
+ Own Id: OTP-14190</p>
+ </item>
+ <item>
+ <p><c>erl_tar</c> in previous versions of OTP only
+ supports the USTAR format. That limited path names to at
+ most 255 bytes, and did not support Unicode characters in
+ names in a portable way.</p>
+ <p><c>erl_tar</c> now has support for reading tar
+ archives in the formats currently in common use, such as
+ v7, STAR, USTAR, PAX, and GNU tar's extensions to the
+ STAR/USTAR format. When writing tar archives,
+ <c>erl_tar</c> can now write them in the <c>PAX</c>
+ format if necessary (for example, to support very long
+ filenames or filenames with Unicode characters). If
+ possible, <c>erl_tar</c> will still write tar archives in
+ the USTAR for maximum portability.</p>
+ <p>
+ Own Id: OTP-14226</p>
+ </item>
+ <item>
+ <p><c>base64:mime_decode/1</c> has been optimized so that
+ it is now almost as fast as<c>base64:decode/1</c>; it
+ used be noticeably slower.</p>
+ <p>
+ Own Id: OTP-14245</p>
+ </item>
+ <item>
+ <p><c>erl_tar</c> will now strip any leading '<c>/</c>'
+ from pathnames when extracting files from a tar archive
+ and write a message to the error logger. There is also
+ new check for directory traversal attacks; if a relative
+ path points above the current working directory the
+ extraction will be aborted.</p>
+ <p>
+ Own Id: OTP-14278</p>
+ </item>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ <item>
+ <p>
+ The Crypto application now supports generation of
+ cryptographically strong random numbers (floats &lt; 1.0
+ and integer arbitrary ranges) as a plugin to the 'rand'
+ module.</p>
+ <p>
+ Own Id: OTP-14317 Aux Id: PR-1372 </p>
+ </item>
+ <item>
+ <p>
+ Add new function <c>ets:select_replace/2</c> which
+ performs atomic "compare-and-swap" operations for ETS
+ objects using match specifications.</p>
+ <p>
+ Own Id: OTP-14319 Aux Id: PR-1076 </p>
+ </item>
+ <item>
+ <p> The Erlang code linter checks for bad <c>dialyzer</c>
+ attributes. It also checks for bad type variables in type
+ declarations. </p>
+ <p>
+ Own Id: OTP-14323</p>
+ </item>
+ <item>
+ <p> Two new functions has been implemented in the
+ <c>rand</c> module; <c>normal/2</c> and
+ <c>normal_s/3</c>, that both produce normal distribution
+ (pseudo) random numbers with mean value and variance
+ according to arguments. </p>
+ <p>
+ Own Id: OTP-14328 Aux Id: PR-1382 </p>
+ </item>
+ <item>
+ <p>
+ Upgraded the OTP internal PCRE library from version 8.33
+ to version 8.40. This library is used for implementation
+ of the <seealso marker="stdlib:re"><c>re</c></seealso>
+ regular expressions module.</p>
+ <p>
+ Besides various bug fixes, the new version allows for
+ better stack protection. In order to utilize this
+ feature, the stack size of normal scheduler threads is
+ now by default set to 128 kilo words on all platforms.
+ The stack size of normal scheduler threads can be set
+ upon system start by passing the <seealso
+ marker="erts:erl#sched_thread_stack_size"><c>+sss</c></seealso>
+ command line argument to the <seealso
+ marker="erts:erl"><c>erl</c></seealso> command.</p>
+ <p>
+ See <url
+ href="http://pcre.org/original/changelog.txt"><c>http://pcre.org/original/changelog.txt</c></url>
+ for information about changes made to PCRE between the
+ versions 8.33 and 8.40.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14331 Aux Id: ERL-208 </p>
+ </item>
+ <item>
+ <p>
+ Added function <c>re:version/0</c> which returns
+ information about the OTP internal PCRE version used for
+ implementation of the <c>re</c> module.</p>
+ <p>
+ Own Id: OTP-14347 Aux Id: PR-1412 </p>
+ </item>
+ <item>
+ <p>The format of debug information that is stored in BEAM
+ files (when <c>debug_info</c> is used) has been changed.
+ The purpose of the change is to better support other
+ BEAM-based languages such as Elixir or LFE.</p>
+ <p>All tools included in OTP (dialyzer, debugger, cover,
+ and so on) will handle both the new format and the
+ previous format. Tools that retrieve the debug
+ information using <c>beam_lib:chunk(Beam,
+ [abstract_code])</c> will continue to work with both the
+ new and old format. Tools that call
+ <c>beam_lib:chunk(Beam, ["Abst"])</c> will not work with
+ the new format.</p>
+ <p>For more information, see the description of
+ <c>debug_info</c> in the documentation for
+ <c>beam_lib</c> and the description of the
+ <c>{debug_info,{Backend,Data}}</c> option in the
+ documentation for <c>compile</c>.</p>
+ <p>
+ Own Id: OTP-14369 Aux Id: PR-1367 </p>
+ </item>
+ <item>
+ <p>
+ Add option hibernate_after to gen_server, gen_statem and
+ gen_event. Also added to the deprecated gen_fsm
+ behaviour.</p>
+ <p>
+ Own Id: OTP-14405</p>
+ </item>
+ <item>
+ <p> The size of crash reports created by
+ <c>gen_server</c>, <c>gen_statem</c> and <c>proc_lib</c>
+ is limited with aid of the Kernel application variable
+ <c>error_logger_format_depth</c>. The purpose is to limit
+ the size of the messages sent to the <c>error_logger</c>
+ process when processes with huge message queues or states
+ crash. </p> <p>The crash report generated by
+ <c>proc_lib</c> includes the new tag
+ <c>message_queue_len</c>. The neighbour report also
+ includes the new tag <c>current_stacktrace</c>. Finally,
+ the neighbour report no longer includes the tags
+ <c>messages</c> and <c>dictionary</c>. </p> <p> The new
+ function <c>error_logger:get_format_depth/0</c> can be
+ used to retrieve the value of the Kernel application
+ variable <c>error_logger_format_depth</c>. </p>
+ <p>
+ Own Id: OTP-14417</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index f24e42bcee..64d8789016 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -308,7 +308,7 @@
format to the file. The events are formatted with a function that is
defined by the process that generated the event (with a call to
<seealso marker="#handle_debug/4"><c>handle_debug/4</c></seealso>).
- </p>
+ The file is opened with encoding UTF-8.</p>
</desc>
</func>
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index 4ab9234b81..c04a201ce1 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -255,7 +255,7 @@ safe_recompile(File, Options, BeamFile) ->
compile_and_load(File, Opts0) when is_list(Opts0) ->
Opts = [report_errors, report_warnings
| ensure_from(filename:extension(File),
- ensure_outdir(filename:dirname(File), Opts0))],
+ ensure_outdir(".", Opts0))],
case compile:file(File, Opts) of
{ok,Mod} -> %Listing file.
purge_and_load(Mod, File, Opts);
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index fcfd0d8493..65ba343368 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -194,8 +194,6 @@ format_error({bad_nowarn_bif_clash,{F,A}}) ->
format_error(disallowed_nowarn_bif_clash) ->
io_lib:format("compile directive nowarn_bif_clash is no longer allowed,~n"
" - use explicit module names or -compile({no_auto_import, [F/A]})", []);
-format_error({bad_nowarn_deprecated_function,{M,F,A}}) ->
- io_lib:format("~tw:~tw/~w is not a deprecated function", [M,F,A]);
format_error({bad_on_load,Term}) ->
io_lib:format("badly formed on_load attribute: ~tw", [Term]);
format_error(multiple_on_loads) ->
@@ -856,14 +854,11 @@ not_deprecated(Forms, St0) ->
{nowarn_deprecated_function, MFAs0} <- lists:flatten([Args]),
MFA <- lists:flatten([MFAs0])],
Nowarn = [MFA || {MFA,_L} <- MFAsL],
- Bad = [MFAL || {{M,F,A},_L}=MFAL <- MFAsL,
- otp_internal:obsolete(M, F, A) =:= no],
- St1 = func_line_warning(bad_nowarn_deprecated_function, Bad, St0),
ML = [{M,L} || {{M,_F,_A},L} <- MFAsL, is_atom(M)],
- St3 = foldl(fun ({M,L}, St2) ->
+ St1 = foldl(fun ({M,L}, St2) ->
check_module_name(M, L, St2)
- end, St1, ML),
- St3#lint{not_deprecated = ordsets:from_list(Nowarn)}.
+ end, St0, ML),
+ St1#lint{not_deprecated = ordsets:from_list(Nowarn)}.
%% The nowarn_bif_clash directive is not only deprecated, it's actually an error from R14A
disallowed_compile_flags(Forms, St0) ->
diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl
index 76f89841b9..b7c193f965 100644
--- a/lib/stdlib/src/error_logger_file_h.erl
+++ b/lib/stdlib/src/error_logger_file_h.erl
@@ -55,7 +55,7 @@ init(File) ->
init(File, PrevHandler) ->
process_flag(trap_exit, true),
- case file:open(File, [write]) of
+ case file:open(File, [write,{encoding,utf8}]) of
{ok,Fd} ->
Depth = error_logger:get_format_depth(),
State = #st{fd=Fd,filename=File,prev_handler=PrevHandler,
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 257c829801..32f43fc706 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.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.
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 9ce8e7d60e..d4d1bdccec 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -779,11 +779,13 @@ format_link_report([Link|Reps], Indent, Extra) ->
LinkIndent = [" ",Indent],
[Indent,"neighbour:\n",format_report(Rep, LinkIndent, Extra)|
format_link_report(Reps, Indent, Extra)];
-format_link_report([], _, _) ->
- [].
+format_link_report(Rep, Indent, Extra) ->
+ format_report(Rep, Indent, Extra).
format_report(Rep, Indent, Extra) when is_list(Rep) ->
format_rep(Rep, Indent, Extra);
+format_report(Rep, Indent, {Enc,unlimited}) ->
+ io_lib:format("~s~"++modifier(Enc)++"p~n", [Indent, Rep]);
format_report(Rep, Indent, {Enc,Depth}) ->
io_lib:format("~s~"++modifier(Enc)++"P~n", [Indent, Rep, Depth]).
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 6eafc7b209..26b3960f4f 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -727,7 +727,7 @@ result_will_be_saved() ->
used_record_defs(E, RT) ->
%% Be careful to return a list where used records come before
%% records that use them. The linter wants them ordered that way.
- UR = case used_records(E, [], RT) of
+ UR = case used_records(E, [], RT, []) of
[] ->
[];
L0 ->
@@ -737,13 +737,19 @@ used_record_defs(E, RT) ->
end,
record_defs(RT, UR).
-used_records(E, U0, RT) ->
+used_records(E, U0, RT, Skip) ->
case used_records(E) of
{name,Name,E1} ->
- U = used_records(ets:lookup(RT, Name), [Name | U0], RT),
- used_records(E1, U, RT);
+ U = case lists:member(Name, Skip) of
+ true ->
+ U0;
+ false ->
+ R = ets:lookup(RT, Name),
+ used_records(R, [Name | U0], RT, [Name | Skip])
+ end,
+ used_records(E1, U, RT, Skip);
{expr,[E1 | Es]} ->
- used_records(Es, used_records(E1, U0, RT), RT);
+ used_records(Es, used_records(E1, U0, RT, Skip), RT, Skip);
_ ->
U0
end.
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index 6f7009b5d9..4972da297d 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -384,7 +384,7 @@ to_float(String) ->
end.
to_number(String, Number, Rest, List, _Tail) when is_binary(String) ->
- BSz = length(List)-length(Rest),
+ BSz = erlang:length(List)-erlang:length(Rest),
<<_:BSz/binary, Cont/binary>> = String,
{Number, Cont};
to_number(_, Number, Rest, _, Tail) ->
@@ -1344,7 +1344,7 @@ bin_search_str(Bin0, Start, Cont, [CP|_]=SearchCPs) ->
String :: string(),
Length :: non_neg_integer().
-len(S) -> length(S).
+len(S) -> erlang:length(S).
%% equal(String1, String2)
%% Test if 2 strings are equal.
@@ -1689,7 +1689,7 @@ left(String, Len) when is_integer(Len) -> left(String, Len, $\s).
Character :: char().
left(String, Len, Char) when is_integer(Char) ->
- Slen = length(String),
+ Slen = erlang:length(String),
if
Slen > Len -> substr(String, 1, Len);
Slen < Len -> l_pad(String, Len-Slen, Char);
@@ -1714,7 +1714,7 @@ right(String, Len) when is_integer(Len) -> right(String, Len, $\s).
Character :: char().
right(String, Len, Char) when is_integer(Char) ->
- Slen = length(String),
+ Slen = erlang:length(String),
if
Slen > Len -> substr(String, Slen-Len+1);
Slen < Len -> r_pad(String, Len-Slen, Char);
@@ -1741,7 +1741,7 @@ centre(String, Len) when is_integer(Len) -> centre(String, Len, $\s).
centre(String, 0, Char) when is_list(String), is_integer(Char) ->
[]; % Strange cases to centre string
centre(String, Len, Char) when is_integer(Char) ->
- Slen = length(String),
+ Slen = erlang:length(String),
if
Slen > Len -> substr(String, (Slen-Len) div 2 + 1, Len);
Slen < Len ->
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index a6ecf03716..1f966411c5 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.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.
@@ -525,7 +525,7 @@ debug_cmd({log_to_file, false}, Debug) ->
{ok, NDebug};
debug_cmd({log_to_file, FileName}, Debug) ->
NDebug = close_log_file(Debug),
- case file:open(FileName, [write]) of
+ case file:open(FileName, [write,{encoding,utf8}]) of
{ok, Fd} ->
{ok, install_debug(log_to_file, Fd, NDebug)};
_Error ->
@@ -648,7 +648,7 @@ debug_options([{log, N} | T], Debug) when is_integer(N), N > 0 ->
debug_options([statistics | T], Debug) ->
debug_options(T, install_debug(statistics, init_stat(), Debug));
debug_options([{log_to_file, FileName} | T], Debug) ->
- case file:open(FileName, [write]) of
+ case file:open(FileName, [write,{encoding,utf8}]) of
{ok, Fd} ->
debug_options(T, install_debug(log_to_file, Fd, Debug));
_Error ->
diff --git a/lib/stdlib/test/c_SUITE.erl b/lib/stdlib/test/c_SUITE.erl
index 4bd32a30f8..f01988478c 100644
--- a/lib/stdlib/test/c_SUITE.erl
+++ b/lib/stdlib/test/c_SUITE.erl
@@ -21,7 +21,9 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
-export([c_1/1, c_2/1, c_3/1, c_4/1, nc_1/1, nc_2/1, nc_3/1, nc_4/1,
- ls/1, memory/1]).
+ c_default_outdir_1/1, c_default_outdir_2/1,
+ nc_default_outdir_1/1, nc_default_outdir_2/1,
+ ls/1, memory/1]).
-include_lib("common_test/include/ct.hrl").
@@ -30,7 +32,10 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [c_1, c_2, c_3, c_4, nc_1, nc_2, nc_3, nc_4, ls, memory].
+ [c_1, c_2, c_3, c_4, nc_1, nc_2, nc_3, nc_4,
+ c_default_outdir_1, c_default_outdir_2,
+ nc_default_outdir_1, nc_default_outdir_2,
+ ls, memory].
groups() ->
[].
@@ -124,6 +129,50 @@ nc_4(Config) when is_list(Config) ->
Result = nc(R,[{outdir,W}]),
{ok, m} = Result.
+c_default_outdir_1(Config) ->
+ R = filename:join(proplists:get_value(data_dir, Config), "m.erl"),
+ W = proplists:get_value(priv_dir, Config),
+ file:set_cwd(W),
+ Obj = "m" ++ code:objfile_extension(),
+ _ = file:delete(Obj),
+ false = filelib:is_file(Obj),
+ Result = c:c(R),
+ {ok, m} = Result,
+ true = filelib:is_file(Obj).
+
+c_default_outdir_2(Config) ->
+ R = filename:join(proplists:get_value(data_dir, Config), "m"),
+ W = proplists:get_value(priv_dir, Config),
+ file:set_cwd(W),
+ Obj = "m" ++ code:objfile_extension(),
+ _ = file:delete(Obj),
+ false = filelib:is_file(Obj),
+ Result = c:c(R),
+ {ok, m} = Result,
+ true = filelib:is_file(Obj).
+
+nc_default_outdir_1(Config) ->
+ R = filename:join(proplists:get_value(data_dir, Config), "m.erl"),
+ W = proplists:get_value(priv_dir, Config),
+ file:set_cwd(W),
+ Obj = "m" ++ code:objfile_extension(),
+ _ = file:delete(Obj),
+ false = filelib:is_file(Obj),
+ Result = c:nc(R),
+ {ok, m} = Result,
+ true = filelib:is_file(Obj).
+
+nc_default_outdir_2(Config) ->
+ R = filename:join(proplists:get_value(data_dir, Config), "m"),
+ W = proplists:get_value(priv_dir, Config),
+ file:set_cwd(W),
+ Obj = "m" ++ code:objfile_extension(),
+ _ = file:delete(Obj),
+ false = filelib:is_file(Obj),
+ Result = c:nc(R),
+ {ok, m} = Result,
+ true = filelib:is_file(Obj).
+
ls(Config) when is_list(Config) ->
Directory = proplists:get_value(data_dir, Config),
ok = c:ls(Directory),
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 6a75eaa737..cb1cceb8db 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -66,7 +66,7 @@
otp_11851/1,otp_11879/1,otp_13230/1,
record_errors/1, otp_11879_cont/1,
non_latin1_module/1, otp_14323/1,
- get_stacktrace/1, otp_14285/1]).
+ get_stacktrace/1, otp_14285/1, otp_14378/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -87,7 +87,7 @@ all() ->
maps, maps_type, maps_parallel_match,
otp_11851, otp_11879, otp_13230,
record_errors, otp_11879_cont, non_latin1_module, otp_14323,
- get_stacktrace, otp_14285].
+ get_stacktrace, otp_14285, otp_14378].
groups() ->
[{unused_vars_warn, [],
@@ -2054,12 +2054,10 @@ otp_5362(Config) when is_list(Config) ->
spawn(A).
">>,
{[nowarn_unused_function]},
- {error,[{3,erl_lint,disallowed_nowarn_bif_clash},
- {4,erl_lint,disallowed_nowarn_bif_clash},
- {4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}],
- [{5,erl_lint,{bad_nowarn_deprecated_function,{3,now,-1}}},
- {5,erl_lint,{bad_nowarn_deprecated_function,{erlang,now,-1}}},
- {5,erl_lint,{bad_nowarn_deprecated_function,{{a,b,c},now,-1}}}]}
+ {errors,[{3,erl_lint,disallowed_nowarn_bif_clash},
+ {4,erl_lint,disallowed_nowarn_bif_clash},
+ {4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}],
+ []}
},
{otp_5362_8,
@@ -3937,10 +3935,6 @@ non_latin1_module(Config) ->
UndefBehav = {undefined_behaviour,'кирилли́ческий атом'},
"behaviour 'кирилли́ческий атом' undefined" =
format_error(UndefBehav),
- BadDepr = {bad_nowarn_deprecated_function,
- {'кирилли́ческий атом','кирилли́ческий атом',18}},
- "'кирилли́ческий атом':'кирилли́ческий атом'/18 is not a deprecated "
- "function" = format_error(BadDepr),
Ts = [{non_latin1_module,
<<"
%% Report uses of module names with non-Latin-1 characters.
@@ -3951,9 +3945,6 @@ non_latin1_module(Config) ->
-callback 'кирилли́ческий атом':'кирилли́ческий атом'() -> a.
- -compile([{nowarn_deprecated_function,
- [{'кирилли́ческий атом','кирилли́ческий атом',18}]}]).
-
%% erl_lint:gexpr/3 is not extended to check module name here:
t1() when 'кирилли́ческий атом':'кирилли́ческий атом'(1) ->
b.
@@ -3977,16 +3968,14 @@ non_latin1_module(Config) ->
{6,erl_lint,non_latin1_module_unsupported},
{8,erl_lint,non_latin1_module_unsupported},
{8,erl_lint,BadCallback},
- {10,erl_lint,non_latin1_module_unsupported},
- {14,erl_lint,illegal_guard_expr},
- {18,erl_lint,non_latin1_module_unsupported},
+ {11,erl_lint,illegal_guard_expr},
+ {15,erl_lint,non_latin1_module_unsupported},
+ {17,erl_lint,non_latin1_module_unsupported},
{20,erl_lint,non_latin1_module_unsupported},
{23,erl_lint,non_latin1_module_unsupported},
- {26,erl_lint,non_latin1_module_unsupported},
- {28,erl_lint,non_latin1_module_unsupported}],
+ {25,erl_lint,non_latin1_module_unsupported}],
[{5,erl_lint,UndefBehav},
- {6,erl_lint,UndefBehav},
- {10,erl_lint,BadDepr}]}}],
+ {6,erl_lint,UndefBehav}]}}],
run(Config, Ts),
ok.
@@ -4000,6 +3989,22 @@ do_non_latin1_module(Mod) ->
ok.
+otp_14378(Config) ->
+ Ts = [
+ {otp_14378_1,
+ <<"-export([t/0]).
+ -compile({nowarn_deprecated_function,{erlang,now,1}}).
+ t() ->
+ erlang:now().">>,
+ [],
+ {warnings,[{4,erl_lint,
+ {deprecated,{erlang,now,0},
+ "Deprecated BIF. See the \"Time and Time Correction"
+ " in Erlang\" chapter of the ERTS User's Guide"
+ " for more information."}}]}}],
+ [] = run(Config, Ts),
+ ok.
+
%% OTP-14323: Check the dialyzer attribute.
otp_14323(Config) ->
Ts = [
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
index 029e6286e4..c4fafe82a4 100644
--- a/lib/stdlib/test/proc_lib_SUITE.erl
+++ b/lib/stdlib/test/proc_lib_SUITE.erl
@@ -28,7 +28,7 @@
init_per_group/2,end_per_group/2,
crash/1, stacktrace/1, sync_start_nolink/1, sync_start_link/1,
spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1, '\x{447}'/0,
- hibernate/1, stop/1, t_format/1]).
+ hibernate/1, stop/1, t_format/1, t_format_arbitrary/1]).
-export([ otp_6345/1, init_dont_hang/1]).
-export([hib_loop/1, awaken/1]).
@@ -51,7 +51,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[crash, stacktrace, {group, sync_start}, spawn_opt, hibernate,
- {group, tickets}, stop, t_format].
+ {group, tickets}, stop, t_format, t_format_arbitrary].
groups() ->
[{tickets, [], [otp_6345, init_dont_hang]},
@@ -78,6 +78,14 @@ end_per_group(_GroupName, Config) ->
%% synchronous, and we want to test that the crash report is ok.
%%-----------------------------------------------------------------
crash(Config) when is_list(Config) ->
+ ok = application:unset_env(kernel, error_logger_format_depth),
+ crash_1(Config),
+ ok = application:set_env(kernel, error_logger_format_depth, 30),
+ crash_1(Config),
+ ok = application:unset_env(kernel, error_logger_format_depth),
+ ok.
+
+crash_1(_Config) ->
error_logger:add_report_handler(?MODULE, self()),
%% Make sure that we don't get a crash report if a process
@@ -562,9 +570,32 @@ t_format() ->
ok.
+t_format_arbitrary(_Config) ->
+ error_logger:tty(false),
+ try
+ t_format_arbitrary()
+ after
+ error_logger:tty(true)
+ end,
+ ok.
+
+t_format_arbitrary() ->
+ A = list_to_atom([1024]),
+ do_test_format([fake_report, A], unlimited),
+ do_test_format([fake_report, A], 20),
+
+ do_test_format([fake_report, foo], unlimited),
+ do_test_format([fake_report, foo], 20),
+ do_test_format([fake_report, []], unlimited),
+ do_test_format([fake_report, []], 20).
+
do_test_format(Report, Depth) ->
- io:format("*** Depth = ~p", [Depth]),
- S0 = proc_lib:format(Report, latin1, Depth),
+ do_test_format(Report, latin1, Depth),
+ do_test_format(Report, unicode, Depth).
+
+do_test_format(Report, Encoding, Depth) ->
+ io:format("*** Depth = ~p, Encoding = ~p", [Depth, Encoding]),
+ S0 = proc_lib:format(Report, Encoding, Depth),
S = lists:flatten(S0),
io:put_chars(S),
length(S).
@@ -584,7 +615,7 @@ init(Tester) ->
{ok, Tester}.
handle_event({error_report, _GL, {Pid, crash_report, Report}}, Tester) ->
- io:format("~s\n", [proc_lib:format(Report)]),
+ io:format("~ts\n", [proc_lib:format(Report)]),
Tester ! {crash_report, Pid, Report},
{ok, Tester};
handle_event(_Event, State) ->
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 4f0fdc4c6a..217e8cc252 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -31,7 +31,7 @@
progex_lc/1, progex_funs/1,
otp_5990/1, otp_6166/1, otp_6554/1,
otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1, otp_13719/1,
- otp_14285/1, otp_14296/1]).
+ otp_14285/1, otp_14296/1, typed_records/1]).
-export([ start_restricted_from_shell/1,
start_restricted_on_command_line/1,restricted_local/1]).
@@ -74,10 +74,10 @@ suite() ->
{timetrap,{minutes,10}}].
all() ->
- [forget, records, known_bugs, otp_5226, otp_5327,
+ [forget, known_bugs, otp_5226, otp_5327,
otp_5435, otp_5195, otp_5915, otp_5916, {group, bits},
{group, refman}, {group, progex}, {group, tickets},
- {group, restricted}].
+ {group, restricted}, {group, records}].
groups() ->
[{restricted, [],
@@ -86,6 +86,8 @@ groups() ->
{bits, [],
[bs_match_misc_SUITE, bs_match_tail_SUITE,
bs_match_bin_SUITE, bs_construct_SUITE]},
+ {records, [],
+ [records, typed_records]},
{refman, [], [refman_bit_syntax]},
{progex, [],
[progex_bit_syntax, progex_records, progex_lc,
@@ -486,6 +488,48 @@ records(Config) when is_list(Config) ->
ok.
+%% Test of typed record support.
+typed_records(Config) when is_list(Config) ->
+ Test = filename:join(proplists:get_value(priv_dir, Config), "test.hrl"),
+ Contents = <<"-module(test).
+ -record(r0,{f :: any()}).
+ -record(r1,{f1 :: #r1{} | undefined, f2 :: #r0{} | atom()}).
+ -record(r2,{f :: #r2{} | undefined}).
+ ">>,
+ ok = file:write_file(Test, Contents),
+
+ RR1 = "rr(\"" ++ Test ++ "\"),
+ #r1{} = (#r1{f1=#r1{f1=undefined, f2=x}, f2 = #r0{}})#r1.f1,
+ ok.",
+ RR2 = "rr(\"" ++ Test ++ "\"),
+ #r0{} = (#r1{f1=#r1{f1=undefined, f2=x}, f2 = #r0{}})#r1.f2,
+ ok. ",
+ RR3 = "rr(\"" ++ Test ++ "\"),
+ #r1{f2=#r0{}} = (#r1{f1=#r1{f1=undefined, f2=#r0{}}, f2 = x})#r1.f1,
+ ok.",
+ RR4 = "rr(\"" ++ Test ++ "\"),
+ (#r1{f2 = #r0{}})#r1{f2 = x},
+ ok. ",
+ RR5 = "rr(\"" ++ Test ++ "\"),
+ (#r1{f2 = #r0{}})#r1{f1 = #r1{}},
+ ok. ",
+ RR6 = "rr(\"" ++ Test ++ "\"),
+ (#r2{f=#r2{f=undefined}})#r2.f,
+ ok.",
+ RR7 = "rr(\"" ++ Test ++ "\"),
+ #r2{} = (#r2{f=#r2{f=undefined}})#r2.f,
+ ok.",
+ [ok] = scan(RR1),
+ [ok] = scan(RR2),
+ [ok] = scan(RR3),
+ [ok] = scan(RR4),
+ [ok] = scan(RR5),
+ [ok] = scan(RR6),
+ [ok] = scan(RR7),
+
+ file:delete(Test),
+ ok.
+
%% Known bugs.
known_bugs(Config) when is_list(Config) ->
%% erl_eval:merge_bindings/2 cannot handle _removal_ of bindings.
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index f7bd21472c..8a83cdec1e 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.3
+STDLIB_VSN = 3.4.1
diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index e8de0ffce2..f85d963919 100644
--- a/lib/syntax_tools/doc/src/notes.xml
+++ b/lib/syntax_tools/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2007</year><year>2016</year>
+ <year>2007</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,21 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 2.1.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 2.1.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index 1ca60ea73b..888cb71f51 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -301,6 +301,8 @@ file(Name, Opts) ->
{Child, ok} ->
ok;
{Child, {error, Reason}} ->
+ exit(Reason);
+ {'EXIT', Child, Reason} ->
exit(Reason)
end.
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index 868f43b8ee..ae2c67c03e 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -239,6 +239,12 @@ t_erl_tidy(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
File = filename:join(DataDir,"erl_tidy_tilde.erl"),
ok = erl_tidy:file(File, [{stdout, true}]),
+
+ %% OTP-14471.
+ Old = process_flag(trap_exit, true),
+ NonExisting = filename:join(DataDir,"non_existing_file.erl"),
+ {'EXIT',{error,{0,file,enoent}}} = (catch erl_tidy:file(NonExisting)),
+ true = process_flag(trap_exit, Old),
ok.
test_comment_scan([],_) -> ok;
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index c5e363112b..9b33f1e1f4 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 2.1.1
+SYNTAX_TOOLS_VSN = 2.1.2
diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml
index 6e66a957ab..922c2ac0f4 100644
--- a/lib/tools/doc/src/lcnt.xml
+++ b/lib/tools/doc/src/lcnt.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2016</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -109,14 +109,6 @@
statistics. If the server held any lock statistics data before the collect then
that data is lost.
</p>
- <note>
- <p>
- When collection occurs the runtime system transitions to a single thread,
- blocking all other threads. No other tasks will be scheduled during this
- operation. Depending on the size of the data this might take a long time
- (several seconds) and cause timeouts in the system.
- </p>
- </note>
</desc>
</func>
@@ -322,24 +314,22 @@
<func>
<name>apply(Fun) -> term()</name>
<fsummary>Same as <c>apply(Fun, [])</c>.</fsummary>
+ <type>
+ <v>Fun = fun()</v>
+ </type>
<desc>
<p>Same as <c>apply(Fun, [])</c>.</p>
</desc>
</func>
<func>
<name>apply(Fun, Args) -> term()</name>
- <fsummary>Clears counters, applies function and collects the profiling results.</fsummary>
+ <fsummary>Same as <c>apply(Module, Function, Args)</c>.</fsummary>
<type>
<v>Fun = fun()</v>
<v>Args = [term()]</v>
</type>
<desc>
- <p> Clears the lock counters and then setups the instrumentation to save all destroyed locks.
- After setup the fun is called, passing the elements in <c>Args</c> as arguments.
- When the fun returns the statistics are immediately collected to the server. After the
- collection the instrumentation is returned to its previous behavior.
- The result of the applied fun is returned.
- </p>
+ <p>Same as <c>apply(Module, Function, Args)</c>.</p>
</desc>
</func>
<func>
@@ -357,6 +347,13 @@
collection the instrumentation is returned to its previous behavior.
The result of the applied function is returned.
</p>
+ <warning>
+ <p>
+ This function should only be used for micro-benchmarks; it sets <c>copy_save</c>
+ to <c>true</c> for the duration of the call, which can quickly lead to running
+ out of memory.
+ </p>
+ </warning>
</desc>
</func>
@@ -429,6 +426,68 @@
<desc> <p>Clear the internal counters. Same as <c>lcnt:clear(Node)</c>.</p></desc>
</func>
+ <func>
+ <name>rt_mask() -> [category_atom()]</name>
+ <fsummary>Same as <c>rt_mask(node())</c>.</fsummary>
+ <desc><p>Same as <c>rt_mask(node())</c>.</p></desc>
+ </func>
+
+ <func>
+ <name>rt_mask(Node) -> [category_atom()]</name>
+ <fsummary>Returns the current lock category mask.</fsummary>
+ <type>
+ <v>Node = node()</v>
+ </type>
+ <desc>
+ <p>
+ Refer to <c>rt_mask/2</c> for a list of valid categories. All
+ categories are enabled by default.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>rt_mask(Categories) -> ok | {error, copy_save_enabled}</name>
+ <fsummary>Same as <c>rt_mask(node(), Categories)</c>.</fsummary>
+ <type>
+ <v>Categories = [atom()]</v>
+ </type>
+ <desc><p>Same as <c>rt_mask(node(), Categories)</c>.</p></desc>
+ </func>
+
+ <func>
+ <name>rt_mask(Node, Categories) -> ok | {error, copy_save_enabled}</name>
+ <fsummary>Changes the lock category mask.</fsummary>
+ <type>
+ <v>Node = node()</v>
+ <v>Categories = [atom()]</v>
+ </type>
+ <desc>
+ <p>
+ Sets the lock category mask to the given categories.
+ </p>
+ <p>
+ This will fail if the <c>copy_save</c> option is enabled; see
+ <c>lcnt:rt_opt/2</c>.
+ </p>
+ <p>Valid categories are:</p>
+ <taglist>
+ <item><c>allocator</c></item>
+ <item><c>db</c> (ETS tables)</item>
+ <item><c>debug</c></item>
+ <item><c>distribution</c></item>
+ <item><c>generic</c></item>
+ <item><c>io</c></item>
+ <item><c>process</c></item>
+ <item><c>scheduler</c></item>
+ </taglist>
+ <p>
+ This list is subject to change at any time, as is the category any given lock
+ may belong to.
+ </p>
+ </desc>
+ </func>
+
<func>
<name>rt_opt({Type, bool()}) -> bool()</name>
<fsummary>Same as <c>rt_opt(node(), {Type, Opt})</c>.</fsummary>
@@ -442,16 +501,25 @@
<v>Type = copy_save | process_locks</v>
</type>
<desc>
- <p>Changes the lock counter behavior and returns the previous behaviour.</p>
<p>Option description:</p>
<taglist>
<tag><c>{copy_save, bool()}</c></tag>
- <item>Enable statistics saving from destroyed locks by copying. This might consume a lot of memory.
+ <item>Retains the statistics of destroyed locks.
<br/>Default: <c>false</c>
+ <warning>
+ <p>
+ This option will use a lot of memory when enabled, which must be
+ reclaimed with <c>lcnt:rt_clear</c>. Note that it makes no distinction
+ between locks that were destroyed and locks for which counting was
+ disabled, so enabling this option will disable changes to the lock
+ category mask.
+ </p>
+ </warning>
</item>
<tag><c>{process_locks, bool()}</c></tag>
- <item>Profile process locks.
+ <item>Profile process locks, equal to adding <c>process</c> to the lock category mask;
+ see <c>lcnt:rt_mask/2</c>
<br/>Default: <c>true</c>
</item>
</taglist>
diff --git a/lib/tools/doc/src/lcnt_chapter.xml b/lib/tools/doc/src/lcnt_chapter.xml
index 1981d66117..24b58136aa 100644
--- a/lib/tools/doc/src/lcnt_chapter.xml
+++ b/lib/tools/doc/src/lcnt_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2009</year><year>2016</year>
+ <year>2009</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,7 +29,7 @@
<approved>nobody</approved>
<checked>no</checked>
<date>2009-11-26</date>
- <rev>PA1</rev>
+ <rev>PA2</rev>
<file>lcnt_chapter.xml</file>
</header>
<p>
@@ -97,8 +97,11 @@ ok
ok
</pre>
<p>
- Another way to to profile a specific function is to use <c>lcnt:apply/3</c> or <c>lcnt:apply/1</c> which does <c>lcnt:clear/0</c> before the function and <c>lcnt:collect/0</c> after its invocation.
- It also sets <c>copy_save</c> to <c>true</c> for the duration of the function call
+ Another way to to profile a specific function is to use <c>lcnt:apply/3</c> or <c>lcnt:apply/1</c>
+ which does <c>lcnt:clear/0</c> before the function and <c>lcnt:collect/0</c> after its invocation.
+ This method should only be used in micro-benchmarks since it sets <c>copy_save</c> to <c>true</c>
+ for the duration of the function call, which may cause the emulator to run out of memory if
+ attempted under load.
</p>
<pre>
Erlang R13B03 (erts-5.7.4) [source] [smp:8:8] [rq:8] [async-threads:0] [hipe]
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index bc17fd5307..f0df43bf2b 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -31,6 +31,61 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ In OTP-20.0, the behavior of c, make, and ct_make was
+ changed so that in some cases the beam files by default
+ would be written to the directory where the source files
+ were found. This is now changed back to the old behavior
+ so beam files are by default written to current
+ directory.</p>
+ <p>
+ Own Id: OTP-14489 Aux Id: ERL-438 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Tools 2.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ In some situations, <c>make:all()</c> and friends did not
+ detect changes in include files located in the current
+ directory. This is now corrected.</p>
+ <p>
+ Own Id: OTP-14339 Aux Id: ERL-395 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>The <c>make</c> module now accepts the
+ <c>{emake,Emake}</c> option.</p>
+ <p>
+ Own Id: OTP-14253</p>
+ </item>
+ <item>
+ <p> Miscellaneous updates due to atoms containing
+ arbitrary Unicode characters. </p>
+ <p>
+ Own Id: OTP-14285</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.9.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index e2db4f0148..5517882ffa 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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.
@@ -2414,8 +2414,8 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
{ok, InFd} ->
case file:open(OutFile, [write,raw,delayed_write]) of
{ok, OutFd} ->
+ Enc = encoding(ErlFile),
if HTML ->
- Encoding = encoding(ErlFile),
Header =
["<!DOCTYPE HTML PUBLIC "
"\"-//W3C//DTD HTML 3.2 Final//EN\">\n"
@@ -2423,13 +2423,14 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
"<head>\n"
"<meta http-equiv=\"Content-Type\""
" content=\"text/html; charset=",
- Encoding,"\"/>\n"
+ html_encoding(Enc),"\"/>\n"
"<title>",OutFile,"</title>\n"
"</head>"
"<body style='background-color: white;"
" color: black'>\n"
"<pre>\n"],
- ok = file:write(OutFd,Header);
+ H1Bin = unicode:characters_to_binary(Header,Enc,Enc),
+ ok = file:write(OutFd,H1Bin);
true -> ok
end,
@@ -2443,12 +2444,15 @@ do_analyse_to_file1(Module, OutFile, ErlFile, HTML) ->
string:right(integer_to_list(H), 2, $0),
string:right(integer_to_list(Mi), 2, $0),
string:right(integer_to_list(S), 2, $0)]),
- ok = file:write(OutFd,
- ["File generated from ",ErlFile," by COVER ",
+
+ H2Bin = unicode:characters_to_binary(
+ ["File generated from ",ErlFile," by COVER ",
Timestamp,"\n\n"
"**************************************"
"**************************************"
- "\n\n"]),
+ "\n\n"],
+ Enc, Enc),
+ ok = file:write(OutFd, H2Bin),
Pattern = {#bump{module=Module,line='$1',_='_'},'$2'},
MS = [{Pattern,[{is_integer,'$1'},{'>','$1',0}],[{{'$1','$2'}}]}],
@@ -2752,16 +2756,22 @@ pmap_collect(Mons,Acc) ->
end.
%%%-----------------------------------------------------------------
-%%% Read encoding from source file
+%%% Decide which encoding to use when analyzing to file.
+%%% The target file contains the file path, so if either the file name
+%%% encoding or the encoding of the source file is utf8, then we need
+%%% to use utf8.
encoding(File) ->
- Encoding =
- case epp:read_encoding(File) of
- none ->
- epp:default_encoding();
- E ->
- E
- end,
- html_encoding(Encoding).
+ case file:native_name_encoding() of
+ latin1 ->
+ case epp:read_encoding(File) of
+ none ->
+ epp:default_encoding();
+ E ->
+ E
+ end;
+ utf8 ->
+ utf8
+ end.
html_encoding(latin1) ->
"iso-8859-1";
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index d1a4624419..436f68d12b 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -2636,22 +2636,32 @@ funcstat_pd(Pid, Func1, Func0, Clocks) ->
#funcstat{callers_sum = CallersSum,
callers = Callers} = FuncstatCallers ->
FuncstatCallers#funcstat{
- callers_sum = clocks_sum(CallersSum, Clocks, Func0),
- callers = [Clocks#clocks{id = Func1} | Callers]}
- end),
+ callers_sum = clocks_sum(CallersSum, Clocks, Func0),
+ callers = insert_call(Clocks, Func1, Callers)}
+ end),
put({Pid, Func1},
case get({Pid, Func1}) of
undefined ->
- #funcstat{callers_sum = #clocks{id = Func1},
+ #funcstat{callers_sum = #clocks{id = Func1},
called_sum = Clocks#clocks{id = Func1},
called = [Clocks#clocks{id = Func0}]};
#funcstat{called_sum = CalledSum,
called = Called} = FuncstatCalled ->
FuncstatCalled#funcstat{
called_sum = clocks_sum(CalledSum, Clocks, Func1),
- called = [Clocks#clocks{id = Func0} | Called]}
+ called = insert_call(Clocks, Func0, Called)}
end).
+insert_call(Clocks, Func, ClocksList) ->
+ insert_call(Clocks, Func, ClocksList, []).
+
+insert_call(Clocks, Func, [#clocks{id = Func} = C | T], Acc) ->
+ [clocks_sum(C, Clocks, Func) | T ++ Acc];
+insert_call(Clocks, Func, [H | T], Acc) ->
+ insert_call(Clocks, Func, T, [H | Acc]);
+insert_call(Clocks, Func, [], Acc) ->
+ [Clocks#clocks{id = Func} | Acc].
+
%% Sort a list of funcstat records,
diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl
index d881fedbd5..139b3d8a4a 100644
--- a/lib/tools/src/lcnt.erl
+++ b/lib/tools/src/lcnt.erl
@@ -34,8 +34,11 @@
-export([start/0,
stop/0]).
-%% erts_debug:lock_counters api
--export([rt_collect/0,
+%% erts_debug:lcnt_xxx api
+-export([rt_mask/0,
+ rt_mask/1,
+ rt_mask/2,
+ rt_collect/0,
rt_collect/1,
rt_clear/0,
rt_clear/1,
@@ -134,27 +137,61 @@ start_internal() ->
%% -------------------------------------------------------------------- %%
%%
-%% API erts_debug:lock_counters
+%% API erts_debug:lcnt_xxx
%%
%% -------------------------------------------------------------------- %%
-rt_collect() ->
- erts_debug:lock_counters(info).
+rt_mask(Node, Categories) when is_atom(Node), is_list(Categories) ->
+ rpc:call(Node, lcnt, rt_mask, [Categories]).
+
+rt_mask(Node) when is_atom(Node) ->
+ rpc:call(Node, lcnt, rt_mask, []);
+
+rt_mask(Categories) when is_list(Categories) ->
+ case erts_debug:lcnt_control(copy_save) of
+ false ->
+ erts_debug:lcnt_control(mask, Categories);
+ true ->
+ {error, copy_save_enabled}
+ end.
+
+rt_mask() ->
+ erts_debug:lcnt_control(mask).
rt_collect(Node) ->
- rpc:call(Node, erts_debug, lock_counters, [info]).
+ rpc:call(Node, lcnt, rt_collect, []).
+rt_collect() ->
+ erts_debug:lcnt_collect().
+rt_clear(Node) ->
+ rpc:call(Node, lcnt, rt_clear, []).
rt_clear() ->
- erts_debug:lock_counters(clear).
+ erts_debug:lcnt_clear().
-rt_clear(Node) ->
- rpc:call(Node, erts_debug, lock_counters, [clear]).
+rt_opt(Node, Arg) ->
+ rpc:call(Node, lcnt, rt_opt, [Arg]).
-rt_opt({Type, Opt}) ->
- erts_debug:lock_counters({Type, Opt}).
+%% Compatibility shims for the "process/port_locks" options mentioned in the
+%% manual.
+rt_opt({process_locks, Enable}) ->
+ toggle_category(process, Enable);
+rt_opt({port_locks, Enable}) ->
+ toggle_category(io, Enable);
-rt_opt(Node, {Type, Opt}) ->
- rpc:call(Node, erts_debug, lock_counters, [{Type, Opt}]).
+rt_opt({Type, NewVal}) ->
+ PreviousVal = erts_debug:lcnt_control(Type),
+ erts_debug:lcnt_control(Type, NewVal),
+ PreviousVal.
+
+toggle_category(Category, true) ->
+ PreviousMask = erts_debug:lcnt_control(mask),
+ erts_debug:lcnt_control(mask, [Category | PreviousMask]),
+ lists:member(Category, PreviousMask);
+
+toggle_category(Category, false) ->
+ PreviousMask = erts_debug:lcnt_control(mask),
+ erts_debug:lcnt_control(mask, lists:delete(Category, PreviousMask)),
+ lists:member(Category, PreviousMask).
%% -------------------------------------------------------------------- %%
%%
@@ -192,13 +229,9 @@ call(Msg) -> gen_server:call(?MODULE, Msg, infinity).
%% -------------------------------------------------------------------- %%
apply(M,F,As) when is_atom(M), is_atom(F), is_list(As) ->
- ok = start_internal(),
- Opt = lcnt:rt_opt({copy_save, true}),
- lcnt:clear(),
- Res = erlang:apply(M,F,As),
- lcnt:collect(),
- lcnt:rt_opt({copy_save, Opt}),
- Res.
+ apply(fun() ->
+ erlang:apply(M,F,As)
+ end).
apply(Fun) when is_function(Fun) ->
lcnt:apply(Fun, []).
@@ -209,7 +242,9 @@ apply(Fun, As) when is_function(Fun) ->
lcnt:clear(),
Res = erlang:apply(Fun, As),
lcnt:collect(),
- lcnt:rt_opt({copy_save, Opt}),
+ %% _ is bound to silence a dialyzer warning; it used to fail silently and
+ %% we don't want to change the error semantics.
+ _ = lcnt:rt_opt({copy_save, Opt}),
Res.
all_conflicts() -> all_conflicts(time).
diff --git a/lib/tools/src/make.erl b/lib/tools/src/make.erl
index ce30156db6..6554d338af 100644
--- a/lib/tools/src/make.erl
+++ b/lib/tools/src/make.erl
@@ -267,15 +267,47 @@ include_opt([]) ->
recompile(File, true, _Load, _Opts) ->
io:format("Out of date: ~ts\n",[File]);
-recompile(File, false, noload, Opts) ->
+recompile(File, false, Load, Opts) ->
io:format("Recompile: ~ts\n",[File]),
- compile:file(File, [report_errors, report_warnings, error_summary |Opts]);
-recompile(File, false, load, Opts) ->
- io:format("Recompile: ~ts\n",[File]),
- c:c(File, Opts);
-recompile(File, false, netload, Opts) ->
- io:format("Recompile: ~ts\n",[File]),
- c:nc(File, Opts).
+ case compile:file(File, [report_errors, report_warnings |Opts]) of
+ Ok when is_tuple(Ok), element(1,Ok)==ok ->
+ maybe_load(element(2,Ok), Load, Opts);
+ _Error ->
+ error
+ end.
+
+maybe_load(_Mod, noload, _Opts) ->
+ ok;
+maybe_load(Mod, Load, Opts) ->
+ %% We have compiled File with options Opts. Find out where the
+ %% output file went to, and load it.
+ case compile:output_generated(Opts) of
+ true ->
+ Dir = proplists:get_value(outdir,Opts,"."),
+ do_load(Dir, Mod, Load);
+ false ->
+ io:format("** Warning: No object file created - nothing loaded **~n"),
+ ok
+ end.
+
+do_load(Dir, Mod, load) ->
+ code:purge(Mod),
+ case code:load_abs(filename:join(Dir, Mod),Mod) of
+ {module,Mod} ->
+ {ok,Mod};
+ Other ->
+ Other
+ end;
+do_load(Dir, Mod, netload) ->
+ Obj = atom_to_list(Mod) ++ code:objfile_extension(),
+ Fname = filename:join(Dir, Obj),
+ case file:read_file(Fname) of
+ {ok,Bin} ->
+ rpc:eval_everywhere(code,load_binary,[Mod,Fname,Bin]),
+ {ok,Mod};
+ Other ->
+ Other
+ end.
exists(File) ->
case file:read_file_info(File) of
diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src
index 12f0cfd2df..8beef49bf9 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -41,6 +41,6 @@
]
},
{runtime_dependencies, ["stdlib-3.1","runtime_tools-1.8.14",
- "kernel-3.0","erts-7.0","compiler-5.0"]}
+ "kernel-5.4","erts-9.1","compiler-5.0"]}
]
}.
diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index 2e48b11740..146c915087 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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.
@@ -151,10 +151,9 @@ t_swap_keys_file([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 ->
+ case catch erlang:system_info(lock_counting) of
+ true ->
do_smoke_lcnt(Config);
_ ->
{skip,"Lock counting is not enabled"}
diff --git a/lib/tools/test/make_SUITE.erl b/lib/tools/test/make_SUITE.erl
index 2f6fe1c732..02da4f4ace 100644
--- a/lib/tools/test/make_SUITE.erl
+++ b/lib/tools/test/make_SUITE.erl
@@ -36,7 +36,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [make_all, make_files, recompile_on_changed_include,
+ [make_all, make_files, load, netload, recompile_on_changed_include,
emake_opts, {group, otp_6057}].
groups() ->
@@ -55,6 +55,21 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
otp_6057_end(Config).
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(netload,_Config) ->
+ %% Stop slave - in case of failure
+ Nodes = nodes(),
+ case [N || N <- Nodes,
+ "make_SUITE_netload" == hd(string:lexemes(atom_to_list(N),"@"))] of
+ [Node] ->
+ ct_slave:stop(Node);
+ _ ->
+ ok
+ end;
+end_per_testcase(_,_Config) ->
+ ok.
test_files() -> ["test1", "test2", "test3", "test4"].
@@ -83,6 +98,32 @@ make_files(Config) when is_list(Config) ->
ensure_no_messages(),
ok.
+load(Config) ->
+ Current = prepare_data_dir(Config),
+ code:purge(test1),
+ code:delete(test1),
+ false = code:is_loaded(test1),
+ up_to_date = make:files([test1], [load]),
+ {file,_} = code:is_loaded(test1),
+ file:set_cwd(Current),
+ ensure_no_messages(),
+ ok.
+
+netload(Config) ->
+ Current = prepare_data_dir(Config),
+ code:purge(test1),
+ code:delete(test1),
+ false = code:is_loaded(test1),
+ {ok,Node} = ct_slave:start(make_SUITE_netload),
+ up_to_date = make:files([test1], [netload]),
+ timer:sleep(1000), % async, so give some time
+ {file,F} = code:is_loaded(test1),
+ {file,F} = rpc:call(Node,code,is_loaded,[test1]),
+ ct_slave:stop(Node),
+ file:set_cwd(Current),
+ ensure_no_messages(),
+ ok.
+
recompile_on_changed_include(Config) ->
Current = prepare_data_dir(Config),
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index f60da27c44..831d850217 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.9.1
+TOOLS_VSN = 2.10.1
diff --git a/lib/wx/c_src/egl_impl.h b/lib/wx/c_src/egl_impl.h
index 7ecd484de5..f52f68c84a 100644
--- a/lib/wx/c_src/egl_impl.h
+++ b/lib/wx/c_src/egl_impl.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2010-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/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 9086117c81..d300ab5128 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2009</year><year>2016</year>
+ <year>2009</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,21 @@
<p>This document describes the changes made to the wxErlang
application.</p>
+<section><title>Wx 1.8.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a livelock that could be caused by <c>wx:batch/1</c>.</p>
+ <p>
+ Own Id: OTP-14289</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Wx 1.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index cfa256fb12..b9100e7c87 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 1.8
+WX_VSN = 1.8.1
diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml
index 56856d026e..1162561225 100644
--- a/lib/xmerl/doc/src/notes.xml
+++ b/lib/xmerl/doc/src/notes.xml
@@ -32,6 +32,25 @@
<p>This document describes the changes made to the Xmerl application.</p>
+<section><title>Xmerl 1.3.15</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Improves accumulator fun in xmerl_scan so that only one
+ #xmlText record is returned for strings which have
+ character references.</p>
+ <p>
+ (Thanks to Jimmy Zöger)</p>
+ <p>
+ Own Id: OTP-14377 Aux Id: PR-1369 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Xmerl 1.3.14</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/xmerl/src/xmerl_sax_old_dom.erl b/lib/xmerl/src/xmerl_sax_old_dom.erl
index 411121370f..6d0d836487 100644
--- a/lib/xmerl/src/xmerl_sax_old_dom.erl
+++ b/lib/xmerl/src/xmerl_sax_old_dom.erl
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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/lib/xmerl/src/xmerl_sax_simple_dom.erl b/lib/xmerl/src/xmerl_sax_simple_dom.erl
index d842bd982b..7b15cd92dc 100644
--- a/lib/xmerl/src/xmerl_sax_simple_dom.erl
+++ b/lib/xmerl/src/xmerl_sax_simple_dom.erl
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index 4e741d59a8..2e9c9061d9 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1 +1 @@
-XMERL_VSN = 1.3.14
+XMERL_VSN = 1.3.15
diff --git a/otp_versions.table b/otp_versions.table
index cb52cbd51e..3ac2d75a23 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,6 @@
+OTP-20.0.1 : common_test-1.15.1 erts-9.0.1 runtime_tools-1.12.1 stdlib-3.4.1 tools-2.10.1 # asn1-5.0 compiler-7.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 eldap-1.2.2 erl_docgen-0.7 erl_interface-3.10 et-1.6 eunit-2.3.3 hipe-3.16 ic-4.4.2 inets-6.4 jinterface-1.8 kernel-5.3 megaco-3.18.2 mnesia-4.15 observer-2.4 odbc-2.12 orber-3.8.3 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 syntax_tools-2.1.2 wx-1.8.1 xmerl-1.3.15 :
+OTP-20.0 : asn1-5.0 common_test-1.15 compiler-7.1 cosProperty-1.2.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 erl_docgen-0.7 erl_interface-3.10 erts-9.0 eunit-2.3.3 hipe-3.16 inets-6.4 jinterface-1.8 kernel-5.3 megaco-3.18.2 mnesia-4.15 observer-2.4 orber-3.8.3 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 runtime_tools-1.12 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 stdlib-3.4 syntax_tools-2.1.2 tools-2.10 wx-1.8.1 xmerl-1.3.15 # cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 eldap-1.2.2 et-1.6 ic-4.4.2 odbc-2.12 os_mon-2.4.2 otp_mibs-1.1.1 :
+OTP-19.3.6.1 : erts-8.3.5.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.6 : erts-8.3.5 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.5 : erts-8.3.4 xmerl-1.3.14 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 :
OTP-19.3.4 : inets-6.3.9 ssl-8.1.3 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.13 :
diff --git a/system/doc/getting_started/conc_prog.xml b/system/doc/getting_started/conc_prog.xml
index 4b19095d95..7936e0d484 100644
--- a/system/doc/getting_started/conc_prog.xml
+++ b/system/doc/getting_started/conc_prog.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2016</year>
+ <year>2003</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/system/doc/programming_examples/bit_syntax.xml b/system/doc/programming_examples/bit_syntax.xml
index 98ad2808cf..d1dd52c5ab 100644
--- a/system/doc/programming_examples/bit_syntax.xml
+++ b/system/doc/programming_examples/bit_syntax.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2015</year>
+ <year>2003</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/system/doc/reference_manual/distributed.xml b/system/doc/reference_manual/distributed.xml
index 01d78436c5..b519609717 100644
--- a/system/doc/reference_manual/distributed.xml
+++ b/system/doc/reference_manual/distributed.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2015</year>
+ <year>2003</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/system/doc/top/templates/index.html.src b/system/doc/top/templates/index.html.src
index b987fb4722..747d19cf7e 100644
--- a/system/doc/top/templates/index.html.src
+++ b/system/doc/top/templates/index.html.src
@@ -2,7 +2,7 @@
<!--
%CopyrightBegin%
-Copyright Ericsson AB 2009-2016. All Rights Reserved.
+Copyright Ericsson AB 2009-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.